简体   繁体   中英

signal handling when process is waiting for another process to terminate

I am just trying to understand the concept of signal handling with respective from kernel and user mode for the running process.

           PROCESS-1       --------------------> PROCESS-3
        (parent process)  <-------------------
             ^               process-3 sending signals(SIGPIPE-for communication) or
             ||              SIGSTOP or SIGKILL to process-1
             ||
             ||
             ||process-1 waiting for child process-2
             || using waitpid command.
             ||
              v
           PROCESS-2(waiting for resource, page fault happened, etc)
        (child process)

I want to know how kernel sends the signal from process-3 to process-1 knowing that process-1 is waiting for process-2 to finish. Would like to know more about the user and kernel communication during the signal handling scenario(PCB,resources,open file descriptors etc.). Please explain related to this context..

Any help given is thankful..!!!

The kernel doesn't really care that process-1 is "waiting for process-2 to finish" (in particular it's not interested in "why" it's in the state it is, merely that it is in some state: in this case, idling in the kernel waiting for some event). For typical 1 caught signals, the signal-sender essentially just sets some bit(s) in the signal-receiver's process/thread state, and then if appropriate, schedules that process/thread to run so that it can see those bits. If the receiver is idling in the kernel waiting for some event, that's one of the "schedule to run" cases. (Other common situations include: the receiver is in STOP state, where it stays stopped except for SIGCONT signals; or, the receiver is running in user mode, where it is set up to transition to kernel mode so as to notice the pending signals.)

Both SIGKILL and SIGSTOP cannot be caught or ignored, so, no, you cannot provide a handler for these. (Normally processes are put into stop state via SIGTSTP , SIGTTIN , or SIGTTOU , all of which can be caught or ignored.)

If system calls are set to restart after a user signal handler returns (via the SA_RESTART flag of sigaction() ), this is achieved by setting up the "return address" for the sigreturn() operation to, in fact, make the system call over again. That is, if process-1 is in waitpid() , the sequence of operations (from process-1's point of view) from the point of the initial waitpid() , through receiving a caught signal s , and back to more waiting, is:

  1. system call: waitpid()
  2. put self to sleep waiting for an event
  3. awakened: check for awakening event
  4. event is signal and signal is caught, so:
  5. set new signal mask per sigaction() settings (see sigaction() )
  6. push signal frame on a stack (see SA_ONSTACK and sigaltstack() )
  7. set up for user code (program counter) to enter at "signal trampoline"
  8. return to user code (into trampoline)

(At this point process-1 is back in user mode. The remaining steps are not numbered because I can't make SO start at 9. :-) )

  • call user handler routine (still on stack chosen above)
  • when user routine returns, execute sigreturn() system call, using the frame stored at setup time, possibly modified by user routine

(At this point the process enters kernel mode, to execute sigreturn() system call)

  • system call: sigreturn() : set signal mask specified by sigreturn() argument
  • set other registers, including stack pointer(s) and program counter, as specified by sigreturn() arguments
  • return to user code

(the program is now back in user mode, with registers set up to enter waitpid )

  1. system call: waitpid()

At this point the process returns to the same state it had before it received the caught signal: waitpid puts it to sleep waiting for an event (step 2). Once awakened (step 3), either the event it was waiting for has occurred (eg, the process being waitpid() -ed is done) and it can return normally, or another caught signal has occurred and it should repeat this sequence, or it is being killed and it should clean up, or whatever.

This sequence is why some system calls (such as some read() -like system calls) will "return early" if interrupted by a signal: they've done something irreversible between the "first" entry into the kernel and the time the signal handler is to be run. In this case, the signal frame pushed at step 6 must not have a program-counter value that causes the entire system call to restart. If it did, the irreversible work done before the process went to sleep would be lost. So, it is instead set up to return to the instruction that detects a successful system call, with the register values set up to return the short read() count, or whatever.

When system calls are set up not to restart ( SA_RESTART is not set), the signal frame pushed in step 6 is also different. Instead of returning to the instruction that executes the system call, it returns to the instruction that detects a failed system call, with the register values set up to indicate an EINTR error.

(Often, but not always, these are the same instruction, eg, a conditional branch to test for success/fail. In my original SPARC port, I made them different instructions in most cases. Since leaf routines return to %o6+8 with no register or stack manipulation, I just set a bit indicating that a successful return should return to the leaf routine's return address. So most system calls were just "put syscall number and ret-on-success flag into %g1 , then trap to kernel, then jump-to-error-handling because the system call must have failed if we got here.")


1 Versus queued signals .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM