Assuming check_if_pid_exists(pid)
returns true when a process with such a pid exists (but possibly hasn’t been running yet) or false when there is no process with such pid, is there any chance in parent code for a race condition when the fork()
returned the child pid, however the kernel hasn’t had a chance to initialize the data structures so that check_if_pid_exists(child)
returns false? Or perhaps after returning from fork()
we have a guarantee that check_if_pid_exists(pid)
returns true?
pid_t child = fork(); if (child == 0) { /* here the child just busy waits */ for (;;) ; } if (child > 0) { /* here the parent checks whether child PID already exists */ check_if_pid_exists(child); }
Advertisement
Answer
No.
The fork()
returns after the new task is created and visible as expected by the parent. Most everything wouldn’t work otherwise.
Whether the process has had a chance to run at all or has started quite a bit, is not known at that point. Whether the child has completed is known as you receive the SIGCHLD
signal once that happens.
Where you can have a race is with the SIGCHLD
if not handled properly. That is, you are expected to ignore the SIGCHLD
signal, call fork()
, save the results appropriately so you have the PID of the child (i.e. allocate a struct to save the child pid_t
value), then use one of the wait()
functions to know whether the child died.
Assuming your fork()
ed process is expected to run for a while, then the
check_if_pid_exists(child) == true
will likely be true 99.9999% of the time (actually, assuming the parent is in control of when the child is expected to exit, make it 100% of the time).
As mentioned by others, many things can prevent the new process from running:
- Not enough memory
- You already started too many children
- The child tries to do something and encounters a fatal error and exits
- Some third party thing prevents the
fork()
- …
Also, fork()
may return -1 in case it fails to create the child.
However, if the question was about: how do I track the lifetime of a child? Then the correct answer is for the parent to check whether it died. You should not rely on a function such as check_if_pid_exists()
searching for the process under /proc/...
or similar implementation (see waitid
), because such a function may determine that the process is still running, then the process dies, and yet the function still returns true…
// ignore SIGCHLD struct sigaction sa, chld; sa.sa_handler = SIG_IGN; sa.sa_flags = 0; __sigemptyset (&sa.sa_mask); sigaction(SIGCHLD, &sa, NULL); [...] int child_is_running = 0; [...] pid_t child = fork(); if(child == 0) ...do stuff in the child... if(child < 0) ...handle error... int child_is_running = 1; [...] wait(...); if(...child exited...) child_is_running = 0;
Now you can write a safe check_if_pid_exists()
:
int check_if_pid_exists() { return child_is_running; }
In my example here, I only allow one child. If you need multiple, that’s where you need a better scheme (probably a struct to save the child info and a table of some sort or linked list of all your children).