1

Does the process begin when fork() is declared? Is anything being killed here?

pid_t child;
child = fork();
kill (child, SIGKILL);

Or do you need to declare actions for the fork process in order for it to actually "begin"?

pid_t child;
child = fork();

if (child == 0) {
  // do something 
}
kill (child, SIGKILL);

I ask because what I am trying to do is create two children, wait for the first to complete, and then kill the second before exiting:

pid_t child1;
pid_t child2;
child1 = fork();
child2 = fork();
int status;

if (child1 == 0) { //is this line necessary?
}

waitpid(child1, &status, 0);

kill(child2, SIGKILL);
user3063864
  • 661
  • 2
  • 7
  • 18

4 Answers4

2

The C function fork is defined in the standard C library (glibc on linux). When you call it, it performs an equivalent system call (on linux its name is clone) by the means of a special CPU instruction (on x86 sysenter). This causes the CPU to switch to a privileged mode and start executing instructions of the kernel. The kernel then creates a new process (a record in a list and accompanying structures), which inherits a copy of memory mappings of the original process (text, heap, stack, and others), file descriptors and more.

The memory areas are marked as non-writable, so that when the new or the original process tries to overwrite them, the kernel gets to handle a CPU exception and perform a copy-on-write (therefore delaying the need to copy a memory page until absolutely necessary). That's because the mappings initially point to the same pages (pieces of physical memory) in both processes.

The kernel then gives execution to the scheduler, which decides which process to run next. It could be the original process, the child process, or any other process running in the system.

Note: The Linux kernel actually puts the child process in front of the parent process in the run queue, so it is run earlier than the parent. This is deemed to give better performance when the child calls exec right after forking.

When execution is given to the original process, the CPU is switched back to nonprivileged mode and starts executing the next instruction. In this case it continues with the fork function of the standard library, which returns the PID of the child process (as returned by the clone system call).

Similarly, the child process continues execution in the fork function, but here it returns 0 to the calling function.

After that, the program continues in both cases normally. The child process has the original process as the parent (this is noted in a structure in the kernel). When it exists, the parent process is supposed to do the cleanup (receiving the exit status of the child) by calling wait.

Note: The clone system call is rather complicated, because it unifies fork with the creation of threads, as well as linux namespaces. Other operating systems have different implementation of fork, e.g. FreeBSD has fork system call by itself.

Disclaimer: I am not a kernel developer. If you know better, please correct the answer.

See Also

Community
  • 1
  • 1
peter
  • 480
  • 3
  • 7
1

The fork process spawns a new process identical to the old one and returns in both functions.

This happens automatically so you don't have to take any actions.

But nevertheless, it is cleaner to check if the call indeed succeeded:

A value below 0 indicates failure. In this case, it is not good to call kill().

A value == 0 indicates that we are the child process. In this case, it is not very clean to call kill().

A value > 0 indicates that we are the parent process. In this case, the return value is our child. Here it is safe to call kill().

In your case, you even end up with 4 processes:

  • Your parent calls fork(), being left with 2 processes.
  • Both of them call fork() again, resulting in a new child process for each of them.

You should move the 2nd fork() process into the branch where the parent code runs.

glglgl
  • 89,107
  • 13
  • 149
  • 217
1

The child process begins some time after fork() has been called (there is some setup which happens in the context of the child).

You can be sure that the child is running when fork() returns.

So the code

pid_t child = fork();
kill (child, SIGKILL);

will kill the child. The child might execute kill(0, SIGKILL) which does nothing and returns an error.

There is no way to tell whether the child might ever live long enough to execute it's kill. Most likely, it won't since the Linux kernel will set up the process structure for the child and let the parent continue. The child will just be waiting in the ready list of the processes. The kill will then remove it again.

EDIT If fork() returns a value <= 0, then you shouldn't wait or kill.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
1

"Declare" is the wrong word to use in this context; C uses that word to talk about constructs that merely assert the existence of something, e.g.

extern int fork(void);

is a declaration of the function fork. Writing that in your code (or having it written for you as a consequence of #include <unistd.h>) does not cause fork to be called.

Now, the statement in your sample code, child = fork(); when written inside a function body, does (generate code to) make a call to the function fork. That function, assuming it is in fact the system primitive fork(2) on your operating system, and assuming it succeeds, has the special behavior of returning twice, once in the original process and once in a new process, with different return values in each so you can tell which is which.

So the answer to your question is that in both of the code fragments you showed, assuming the things I mentioned in the previous paragraph, all of the code after the child = fork(); line is at least potentially executed twice, once by the child and once by the parent. The if (child == 0) { ... } construct (again, this is not a "declaration") is the standard idiom for making parent and child do different things.

EDIT: In your third code sample, yes, the child1 == 0 block is necessary, but not to ensure that the child is created. Rather, it is there to ensure that whatever you want child1 to do is done only in child1. Moreover, as written (and, again, assuming all calls succeed) you are creating three child processes, because the second fork call will be executed by both parent and child! You probably want something like this instead:

pid_t child1, child2;
int status;
child1 = fork();
if (child1 == -1) {
  perror("fork"); 
  exit(1);
}
else if (child1 == 0) {
  execlp("program_to_run_in_child_1", (char *)0);
  /* if we get here, exec failed */
  _exit(127);
}

child2 = fork();
if (child2 == -1) {
  perror("fork");
  kill(child1, SIGTERM);
  exit(1);
}

else if (child2 == 0) {
  execlp("program_to_run_in_child_2", (char *)0);
  /* if we get here, exec failed */
  _exit(127);
}

/* control reaches this point only in the parent and only when
   both fork calls succeeded */
if (waitpid(child1, &status, 0) != child1) {
  perror("waitpid");
  kill(child1, SIGTERM);
}

/* only use SIGKILL as a last resort */
kill(child2, SIGTERM);

FYI, this is only a skeleton. If I were writing code to do this for real (which I have: see for instance https://github.com/zackw/tbbscraper/blob/master/scripts/isolate.c ) there would be a whole bunch more code just to comprehensively detect and report errors, plus the additional logic required to deal with file descriptor management in the children and a few other wrinkles.

zwol
  • 135,547
  • 38
  • 252
  • 361
  • So, because you call child2 = fork(); after child1's action block, it won't create three child processes? When does child1 terminate here? – user3063864 Jan 22 '14 at 17:40
  • Correct. child1 goes into its "action block" and never comes out, because `execlp` only returns if it fails, and `_exit` never returns, period. – zwol Jan 22 '14 at 17:46
  • So if child1's "action block" were empty, would calling child2 = fork(); after it still cause too many children? – user3063864 Jan 22 '14 at 17:47
  • Yes. It is your responsibility to ensure that the child cannot get out of its "action block" no matter what. Well, unless you *want* the parent and the child to do the same thing, but that almost never happens. – zwol Jan 22 '14 at 17:48