Your question is partly based on bad naming convention. A "thread of control" in kernel-speak is a process in user-speak. So when you read that vfork "the calling thread is suspended" think "process" (or "heavyweight thread" if you like) not "thread" as in "multi-threaded process".
- So yes, the parent process is suspended.
vfork
semantics were defined for the very common case where a process (the shell most often) would fork, mess with some file descriptors, and then exec
another process in place. The kernel folks realized they could save a huge amount of page copying overhead if they skipped the copy since the exec
was just going to throw those copied pages away. A vforked child does have its own file descriptor table in the kernel, so manipulating that doesn't affect the parent process, keeping the semantics of fork
unchanged.
- Why? Because fork/exec was common, expensive, and wasteful
Given the more accurate definition of "kernel thread of control", the answer to can they run in parallel is clearly
- No, the parent will be blocked by the kernel until the child exits or execs
How does the parent know the child has exited?
- It doesn't, the kernel knows and keeps the parent from getting any CPU at all until the child has gone away.
As for the last question, I would suspect that the kernel would detect the child stack operations involved in a return and signal the child with an uncatchable signal or just kill it, but I don't know the details.
How CTRL+C works
The first thing is to understand how CTRL+C works.
When you press CTRL+C, your terminal emulator sends an ETX character (end-of-text / 0x03).
The TTY is configured such that when it receives this character, it sends a SIGINT to the foreground process group of the terminal. This configuration can be viewed by doing stty -a
and looking at intr = ^C;
.
The POSIX specification says that when INTR is received, it should send a SIGINT to the foreground process group of that terminal.
What is the foreground process group?
So, now the question is, how do you determine what the foreground process group is?
The foreground process group is simply the group of processes which will receive any signals generated by the keyboard (SIGTSTP, SIGINT, etc).
Simplest way to determine the process group ID is to use ps
:
ps ax -O tpgid
The second column will be the process group ID.
How do I send a signal to the process group?
Now that we know what the process group ID is, we need to simulate the POSIX behavior of sending a signal to the entire group.
This can be done with kill
by putting a -
in front of the group ID.
For example, if your process group ID is 1234, you would use:
kill -INT -1234
Simulate CTRL+C using the terminal number.
So the above covers how to simulate CTRL+C as a manual process. But what if you know the TTY number, and you want to simulate CTRL+C for that terminal?
This becomes very easy.
Lets assume $tty
is the terminal you want to target (you can get this by running tty | sed 's#^/dev/##'
in the terminal).
kill -INT -$(ps h -t $tty -o tpgid | uniq)
This will send a SIGINT to whatever the foreground process group of $tty
is.
Best Answer
To answer that question, you have to understand how signals are sent to a process and how a process exists in the kernel.
Each process is represented as a
task_struct
inside the kernel (the definition is in thesched.h
header file and begins here). That struct holds information about the process; for instance the pid. The important information is in line 1566 where the associated signal is stored. This is set only if a signal is sent to the process.A dead process or a zombie process still has a
task_struct
. The struct remains, until the parent process (natural or by adoption) has calledwait()
after receivingSIGCHLD
to reap its child process. When a signal is sent, thesignal_struct
is set. It doesn't matter if the signal is a catchable one or not, in this case.Signals are evaluated every time when the process runs. Or to be exact, before the process would run. The process is then in the
TASK_RUNNING
state. The kernel runs theschedule()
routine which determines the next running process according to its scheduling algorithm. Assuming this process is the next running process, the value of thesignal_struct
is evaluated, whether there is a waiting signal to be handled or not. If a signal handler is manually defined (viasignal()
orsigaction()
), the registered function is executed, if not the signal's default action is executed. The default action depends on the signal being sent.For instance, the
SIGSTOP
signal's default handler will change the current process's state toTASK_STOPPED
and then runschedule()
to select a new process to run. Notice,SIGSTOP
is not catchable (likeSIGKILL
), therefore there is no possibility to register a manual signal handler. In case of an uncatchable signal, the default action will always be executed.To your question:
A defunct or dead process will never be determined by the scheduler to be in the
TASK_RUNNING
state again. Thus the kernel will never run the signal handler (default or defined) for the corresponding signal, whichever signal is was. Therefore theexit_signal
will never be set again. The signal is "delivered" to the process by setting thesignal_struct
intask_struct
of the process, but nothing else will happen, because the process will never run again. There is no code to run, all that remains of the process is that process struct.However, if the parent process reaps its children by
wait()
, the exit code it receives is the one when the process "initially" died. It doesn't matter if there is a signal waiting to be handled.