Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spawn process from multithreaded application

I have a situation where I need to spawn a helper process from within a very large, multithreaded application, which I do not have complete control over.

Right now I'm using fork()/exec(). This works a lot of the time, but in some circumstances the child crashes weirdly before the exec() happens. I'm suspecting this is because fork()ing multithreaded applications is generally considered to be a Really Bad Idea.

I would really, really like a way to start a process atomically, without fork()ing the parent: with all file descriptors closed, environment set up the way I want, CWD set, etc. This should avoid all the horror of fork()ing my multithreaded parent app, and dealing with file descriptor inheritance, etc. posix_spawn() should be ideal. Unfortunately, on Linux, posix_spawn() is implemented using fork() and exec()...

vfork() is defined to suspend the parent process until the child calls exec(). This would appear to be more like what I want, but my understanding was that vfork() is generally considered a historical relic these days and is equivalent to fork() --- is this still the case?

What's the least bad way of dealing with this?

Note that:

  • I cannot spawn my process before any threads start (because I can't run code at that point)
  • I cannot redesign my application not to need the helper process, due to external requirements
  • I cannot suspend all my threads before spawning the helper process, because they don't belong to me

This is on Linux. Java is involved, but all my code is in C.

like image 829
David Given Avatar asked Nov 16 '11 12:11

David Given


2 Answers

fork'ing multithreaded application is considered to be safe if you use only async-signal-safe operations. POSIX says:

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. Fork handlers may be established by means of the pthread_atfork() function in order to maintain application invariants across fork() calls.

posix_spawn() is not the best idea:

It is also complicated to modify the environment of a multi-threaded process temporarily, since all threads must agree when it is safe for the environment to be changed. However, this cost is only borne by those invocations of posix_spawn() and posix_spawnp() that use the additional functionality. Since extensive modifications are not the usual case, and are particularly unlikely in time-critical code, keeping much of the environment control out of posix_spawn() and posix_spawnp() is appropriate design.

(see man posix_spawn)

I guess you have problems with replicated from parent resources. You may clean them up using pthread_atfork() handler (you use pthread, right?). The other way is to use low level function for process creation called clone(). It gives you almost full control on what exactly child process should inherit from its parent.

[UPDATE]

Probably the simplest way to get rid of the problem is to change your fork'ing scheme. For example you can create a new process (fork) even before your program initializes all resources. I.e. call fork() in main() before you create all your threads. In child process setup a signal handler (for example for SIGUSR2 signal) and sleep. When parent needs to exec some new process, it sends the SIGUSR2 signal to your child process. When child catches it, it calls fork/exec.

like image 200
Dan Kruchinin Avatar answered Oct 24 '22 04:10

Dan Kruchinin


Calling fork should be safe if you limit yourself to "raw" system calls (syscall(SYS_fork), syscalll(SYS_execve, ...), etc.). Call into any glibc routine, and you'll be in a lot of trouble.

Calling vfork is not at all what you want: only the thread that called vfork is suspended, and other threads will continue to run (and in the same address space as the vforked child). This is very likely to complicate your life.

Calling clone directly is possible, but exceedingly tricky. We have an implementation that allows for safe forking of child processes from multithreaded apps (unfortunately not open source). That code is very tricky, and surprisingly long.

like image 39
Employed Russian Avatar answered Oct 24 '22 05:10

Employed Russian