What happens when a thread forks?

21,063

Solution 1

The new process will be the child of the main thread that created the thread. I think.

fork creates a new process. The parent of a process is another process, not a thread. So the parent of the new process is the old process.

Note that the child process will only have one thread because fork only duplicates the (stack for the) thread that calls fork. (This is not entirely true: the entire memory is duplicated, but the child process will only have one active thread.)

If its parent finishes first, the new process will be attached to init process.

If the parent finishes first a SIGHUP signal is sent to the child. If the child does not exit as a result of the SIGHUP it will get init as its new parent. See also the man pages for nohup and signal(7) for a bit more information on SIGHUP.

And its parent is main thread, not the thread that created it.

The parent of a process is a process, not a specific thread, so it is not meaningful to say that the main or child thread is the parent. The entire process is the parent.

One final note: Mixing threads and fork must be done with care. Some of the pitfalls are discussed here.

Solution 2

Correct me if I am wrong.

Will do :)

As fork() is a POSIX system call, its behavior is well defined:

A process shall be created with a single thread. If a multi-threaded process calls fork(), the new process shall contain a replica of the calling thread and its entire address space, possibly including the states of mutexes and other resources. Consequently, to avoid errors, the child process may only execute async-signal-safe operations until such time as one of the exec functions is called.

https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html

A forked child is an exact duplicate of its parent, yet only the thread that called fork() in the parent, still exists in the child and is the new main thread of that child until you call exec().

The POSIX description "shall be created with a single thread" is misleading as in fact most implementation will really create an exact duplicate of the parent process, so all other threads and their memory are duplicated as well, which means the threads are in fact there, they just cannot run anymore as the system never assigns any CPU time to them; they are in fact missing in the kernel thread scheduler table.

An easier mental image is the following:

When the parent calls fork, the entire process is frozen for a moment, atomically duplicated, and then the parent is unfrozen as a whole, yet in the child only the one thread that called fork is unfrozen, everything else stays frozen.

That's why it isn't safe to perform certain system calls in between fork() and exec() as also pointed out by the POSIX standard. Ideally you shouldn't do much more than maybe closing or duplicating file descriptors, setting or restoring signal handlers and then calling exec().

Solution 3

However, what will happen if a thread creates a new process using fork()?

A new process will be created by copying the calling thread's address space (not the entire address space of the process). It's generally considered a bad idea because it's very hard to get it right. POSIX says the child process (created in a multi-threaded program) can only call async-signal-safe functions until it calls one of the exec* functions.

If its parent finishes first, the new process will be attached to init process.

The child process is typically inherited by the init process. If the parent process is a controlling process (e.g. shell), then POSIX requires:

If the process is a controlling process, the SIGHUP signal shall be sent to each process in the foreground process group of the controlling terminal belonging to the calling process.

However, this is not true for most processes as most processes aren't controlling processes.

And its parent is main thread, not the thread that created it.

The parent of forked child will always be the process that called fork(). So, PPID is the child process will be the PID of your program.

Solution 4

problem stems from the behaviour of fork(2) itself. Whenever a new child process is created with fork(2) the new process gets a new memory address space but everything in memory is copied from the old process (with copy-on-write that’s not 100% true, but the semantics are the same).

If we call fork(2) in a multi-threaded environment the thread doing the call is now the main-thread in the new process and all the other threads, which ran in the parent process, are dead. And everything they did was left exactly as it was just before the call to fork(2).

Now imagine that these other threads were happily doing their work before the call to fork(2) and a couple of milliseconds later they are dead. What if something these now-dead threads did was not meant to be left exactly as it was?

Let me give you an example. Let’s say our main thread (the one which is going to call fork(2)) was sleeping while we had lots of other threads happily doing some work. Allocating memory, writing to it, copying from it, writing to files, writing to a database and so on. They were probably allocating memory with something like malloc(3). Well, it turns out that malloc(3) uses a mutex internally to guarantee thread-safety. And exactly this is the problem.

What if one of these threads was using malloc(3) and has acquired the lock of the mutex in the exact same moment that the main-thread called fork(2)? In the new child process the lock is still held - by a now-dead thread, who will never return it.

The new child process will have no idea if it’s safe to use malloc(3) or not. In the worst case it will call malloc(3) and block until it acquires the lock, which will never happen, since the thread who’s supposed to return it is dead. And this is just malloc(3). Think about all the other possible mutexes and locks in database drivers, file handling libraries, networking libraries and so on.

for full explanation you can go through this link.

Solution 5

The Linux kernel itself doesn't have the distinction between threads and processes. When a process forks it specifies which things are shared (memory, open filehandles etc) with the parent process. But it's just a set of flags. The concept of threads and processes is applied on top of the kernel implementation.

Of course most people call into the kernel via libc which picks the flags according to the common conception of threads/processes.

At the operating system level forking a thread is the same as forking a process. This is one of the (tricky) differences between UNIX implementations. For example some UNIXs do have the concept of threads - and then they end up with the question of: if I fork a process do I duplicate all its threads in the new process? But for linux a thread and a process are essentially the same.

Share:
21,063
Tony Tannous
Author by

Tony Tannous

Sunspots have faded and now I'm doing time.

Updated on February 27, 2022

Comments

  • Tony Tannous
    Tony Tannous about 2 years

    I know calling fork() sys_call from a thread is a bad idea. However, what will happen if a thread creates a new process using fork()?

    The new process will be the child of the main thread that created the thread. I think.

    If its parent finishes first, the new process will be attached to init process. And its parent is main thread, not the thread that created it.

    Correct me if I am wrong.

    #include <stdio.h>
    #include <pthread.h>
    
    int main () 
    {
         thread_t pid;
         pthread_create(&(pid), NULL, &(f),NULL);
         pthread_join(tid, NULL);
         return 0;
    }
    
    void* f()
    {
         int i;
         i = fork();
    
         if (i < 0) {
             // handle error
         } else if (i == 0) // son process
         {
              // Do something;
         } else {
              // Do something;
         }
     }
    
  • P.P
    P.P over 7 years
    SIGHUB is sent only if the parent is a controlling process per POSIX. It's rare that child processes receive SIGHUB.
  • Arnout
    Arnout over 7 years
    "copying the calling thread's address space": threads within a process share the same address space, so in fact fork() does copy the entire address space of the process. The part about async-signal-safe function is also incorrect: that's only for vfork(), not for full fork().
  • P.P
    P.P over 7 years
    @Arnout POSIX rationale disagrees with your claim. See pubs.opengroup.org/onlinepubs/009695399/functions/… which says "There are at least two serious problems with the semantics of fork() in a multi-threaded program. [...] It is suggested that programs that use fork() call an exec function very soon afterwards in the child process, thus resetting all states. In the meantime, only a short list of async-signal-safe library routines are promised to be available". In fact, the entire rationale is relevant here.
  • y_159
    y_159 over 3 years
    @Klas Lindback how can one notify parent/thread if the child process created by it finishes its job first. and also is it correct that after forking if we call exec to run some command and after finishing the command child process will be destroyed? In this case how the child process can send notification to the thread/parent process who created it?
  • Tony Tannous
    Tony Tannous over 3 years
    Cheers, I forgot this question even exist.
  • Ichthyo
    Ichthyo almost 3 years
    @y_159 Parent process won't be notified. Rather the parent process needs to watch the child process, either by reading from a pipe or by waiting for the exit code.
  • Ichthyo
    Ichthyo almost 3 years
    @y_159 And moreover exec replaces the current process. If this current process was created by fork, the parent kind of "inherits" the launched other executable as child. I.e. if the child process opens pipes with its parent and connects them to STDIN and STOUT, when you then exec the parent just is connected through those pipes to the new executable's STDIN and STDOUT See the very good example this SO answer: stackoverflow.com/a/479103
  • Алексей Неудачин
    Алексей Неудачин over 2 years
    in early versions linux does threading via hardware So it's either there or not there .