Live data from Hacker News

Fork() without exec() is dangerous in large programs

evanjones.ca

71–80 of 106 posts

Re: Fork() without exec() is dangerous in large programs

#71
post #51

Earlier quoted context omitted.

compilers and runtimes can spawn threads implicitly when required (think of openmp, cilk+, or even autopar). Are you saying that libraries shouldn't use these facilities?

If you're spawning threads implicitly then you're no longer a library, you're a framework. Sometimes a framework is a good fit, but frameworks don't compose (if you try to use two frameworks in the same application you're gonna have a bad time) so there is a cost. But in any case, better a framework that's explicitly a framework than a "library" which can't actually be used freely in general-purpose code.

> If you're spawning threads implicitly then you're no longer a library, you're a framework.

That't not what makes a framework. It's all about inversion of control, you call a library and a framework calls you.

http://stackoverflow.com/questions/3057526/framework-vs-tool...

Re: Fork() without exec() is dangerous in large programs

#72
post #50

> How to use fork safely: 1. Only use fork to immediately call exec. 2. Fork a worker at the beginning of your program, before there can be other threads. 3. Only use fork in toy programs. 4. Stop writing broken multithreaded code altogether or if you must at least run an event loop per core/thread and use a wrapper for fork() to put the system into a fork()able state before forking. It's nice and reliable. Multithre…

Fork/Exec is only good if you are going to run a different program then you are currently executing. If you want a series of worker threads that are effectively the same as the master/initial thread. The proper way to do this clone(2) not fork, or fork/exec. When used properly clone allows your group of threads to share a single PID, and TGID (Thread Group IDentifier). This cuts down on kernel resources your process(…

...And why would you want that? The advantage for fork(2) over clone is that you CAN'T share those datastructures, making it harder to write code resulting a deadlock: you have to share explicitely.

As for the kernel resources for the process, you'd be surprised how large RAM has gotten these days...

Re: Fork() without exec() is dangerous in large programs

#73
post #26
post #11

Earlier quoted context omitted.

This is not a bad strategy, especially because between the moment you fork() and the child calls exec(), you have a lot of memory regions shared in copy-on-write (because it would be Too costly to actually duplicate the universelle). Which means that busy threads writing in memory in the parent area will trigger minor page faults, possibly impacting performances...

yeah, that's why vfork has been introduced back in the days. See http://man7.org/linux/man-pages/man2/vfork.2.html

The "historic description" in the man page you cite disagrees: vfork(2) was added before BSD had copy-on-write, so the memory was actually copied and vfork(2) is essentially a crude hack to avoid that.

Unfortunately once copy-on-write was implemented, the crude hack was retained for backwards compatibility....

Re: Fork() without exec() is dangerous in large programs

#74
post #60

It sounds like if you use threads and fork it might be a disaster. Its true, but the author blames only fork and skip the second part of the problem, threads. This is not fair, because fork is much older than POSIX threads. In fact, POSIX threads were poorly designed to use with fork.

Fair point. My opinion is that in today's age of multi-core CPUs, shared memory concurrency is extremely useful for making efficient use of computing resources. As a result, I find threads to be unavoidable in most large systems I've dealt with recently.

Agree, that multi-threading probably the best choice for CPU-intensive applications. But for databases or http-servers multi-process + coroutines/fibers can be a better solution. For example, Redis is few-thread application. It creates additional threads only for disk I/O, because unfortunately file descriptors in Linux/UNIX do not work in async mode.

Btw, Redis reminds me, one really awesome usage of fork. To make a snapshot of itself Redis forks the main process. After that the child simply goes through tuples and write them to a files with out any worries that somebody will modify a records. Copy-on-write mechanism simply prevents it.

Redis save snapshot code: https://github.com/antirez/redis/blob/unstable/src/rdb.c#L99...

Re: Fork() without exec() is dangerous in large programs

#75
post #52

Earlier quoted context omitted.

> It is true that fork(2) is simple and elegant design What exactly is simple and elegant about it? Have you looked into how much tooling is necessary everywhere in unix to make it work? It's insane. It has a huge footprint and it does not provide standardized APIs to make it work for non covered cases (the best we have is pthread_atfork which is not portable).

> What exactly is simple and elegant about it? Spawn-style APIs have to take a billion parameters that are mostly set to defaults, for things like working directory, environment variables, user ID, controlling terminal. Whereas fork+exec means you can express these things in a more compositional, buildery style: fork, change the two things you actually need to change, then exec.

I don't think this makes the case for elegance at all.

Global vars set wherever in a program are preferable to explicit params when creating a new process? And there's no way to have default params or anything?

Re: Fork() without exec() is dangerous in large programs

#76
post #51

Earlier quoted context omitted.

If you're spawning threads implicitly then you're no longer a library, you're a framework. Sometimes a framework is a good fit, but frameworks don't compose (if you try to use two frameworks in the same application you're gonna have a bad time) so there is a cost. But in any case, better a framework that's explicitly a framework than a "library" which can't actually be used freely in general-purpose code.

> If you're spawning threads implicitly then you're no longer a library, you're a framework. That't not what makes a framework. It's all about inversion of control, you call a library and a framework calls you. http://stackoverflow.com/questions/3057526/framework-vs-tool...

If it's spawning threads, it's controlling the control flow. Either it's calling you, or the control flow is out of control.

Re: Fork() without exec() is dangerous in large programs

#77
post #51

Earlier quoted context omitted.

If you're spawning threads implicitly then you're no longer a library, you're a framework. Sometimes a framework is a good fit, but frameworks don't compose (if you try to use two frameworks in the same application you're gonna have a bad time) so there is a cost. But in any case, better a framework that's explicitly a framework than a "library" which can't actually be used freely in general-purpose code.

would you consider a parallelized BLAS a framework?

Yes. Isn't that what I just said?

Re: Fork() without exec() is dangerous in large programs

#78
post #61

Earlier quoted context omitted.

Thanks for pointing this out! Interesting that the code states that part of the concern is increasing memory usage. I don't quite understand why it would "duplicate the memory usage" since fork uses copy-on-write? Anyway, I didn't explain it well, but this was what I was trying to get at with "fork a worker at the beginning of your program, before there can be other threads."

Copy-on-write doesn't work the same on every OS... Linux uses over-commit so it's not a big deal, but Solaris (and NT) don't over-commit so you actually need enough VM at the time when you call fork or fork will fail, so you may need to provide a lot of swap space on those systems to successfully call fork in large processes.

Ah thanks, that makes sense. Fun trade-offs: Either run out of memory when you call fork, or fight with the out-of-memory killer at a later time :)

Re: Fork() without exec() is dangerous in large programs

#80
post #64
post #48

Earlier quoted context omitted.

Then you should read what POSIX says about pthreads, regarding mandatory and optional API support, specially the implementation semantics and UB. For example, what happens to the interactions of signal handlers semantics and thread scheduling. Or what happens to the threads when the process does a fork().

> Or what happens to the threads when the process does a fork(). You mean this? http://pubs.opengroup.org/onlinepubs/009695399/functions/for... 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. Granted, it's SUSv3.

I would advise people that lack experience writing portable UNIX code to read "Advanced Programming in the UNIX Environment" by W. Richard Stevens and Stephen A. Rago.

Just to get a glimpse of what POSIX says and what each UNIX actually does.

EDIT: Not talking about you, rather in general.

Post reply on HN