Live data from Hacker News

A fork() in the road

microsoft.com

31–40 of 184 posts

Re: A fork() in the road

#31

Fork has really weird semantics, and a lot of fun gotchas around managing resources. Good riddance?

Not even just the semantics, the performance is awful. Even when the fork is virtual (as any modern fork is) and there's no memory copying because it's COW, all the kernel page tables still need to be copied and for a multi-GB process that's nontrivial. That's why any sane large service that needs to fork anything will early on start up a slave subprocess whose only job is to fork quickly when the master process needs it.

Re: A fork() in the road

#32
fork() is also used to daemonize and for privilege separation, two tasks where posix_spawn() cannot be used. I suppose daemonization can be seen as something of the past, but privilege separation is not. On Linux, privileges are attached to a thread, so it should be possible to spawn a new thread instead of a new process. However, a privileged thread sharing the same address space as an unprivileged one doesn't seem a good idea.

The paper also mention the use case of multiprocess servers which relies heavily on fork() but dismiss it as it could be implemented with threads. A crash in a worker would lead to the crash of the whole application. While a worker could just be restarted.

A proper use case of removing fork() from an actual program would help. For example, how nginx on Windows is implemented?

Re: A fork() in the road

#34

Fork has really weird semantics, and a lot of fun gotchas around managing resources. Good riddance?

Not even just the semantics, the performance is awful. Even when the fork is virtual (as any modern fork is) and there's no memory copying because it's COW, all the kernel page tables still need to be copied and for a multi-GB process that's nontrivial. That's why any sane large service that needs to fork anything will early on start up a slave subprocess whose only job is to fork quickly when the master process need…

> That's why any sane large service that needs to fork anything will early on start up a slave subprocess whose only job is to fork quickly when the master process needs it.

I don't think that's (entirely) true. This is more because a large service with some potent master process will have said process Do Stuff(tm) that will involve opening files, threads, signal handling, or whatever things that need to be taken care of one way or the other when forking to a worker (or whatever other child) process. It's therefore much simpler to fork a master subprocess into a child spawner earlier on, when it has yet to do anything. You significantly reduce your chances of screwing up if you have nothing to clean up for.

Re: A fork() in the road

#35
post #17

I read the paper, and they make a lot of good points about fork's warts. But I really wanted some explanation of why Windows process startup seems to be so heavyweight. Why does anything that spawns lots of little independent processes take so bloody long on Windows? I'm not saying "lots of processes on Windows is slow, lots of processes on Linux is fast, Windows uses CreateProcess, Linux uses fork, CreateProcess is…

I used to work on a cross-platform project, and spent several weeks trying to figure out why our application ran significantly faster on linux than windows. One major culprit was process creation (another was file creation). I never really uncovered the true reason, but I suspect it had to do with the large number of DLLs that Windows would automatically link if you weren't very careful. Linux, of course, can also load shared code objects, but in my experience, they are smaller and lighter weight.

Re: A fork() in the road

#36
post #34

Earlier quoted context omitted.

Not even just the semantics, the performance is awful. Even when the fork is virtual (as any modern fork is) and there's no memory copying because it's COW, all the kernel page tables still need to be copied and for a multi-GB process that's nontrivial. That's why any sane large service that needs to fork anything will early on start up a slave subprocess whose only job is to fork quickly when the master process need…

> That's why any sane large service that needs to fork anything will early on start up a slave subprocess whose only job is to fork quickly when the master process needs it. I don't think that's (entirely) true. This is more because a large service with some potent master process will have said process Do Stuff(tm) that will involve opening files, threads, signal handling, or whatever things that need to be taken car…

That's true, it's not the only reason. Dealing with threads and buffers and pthread_atfork and the associated heartbreak is a biggie also. But the performance is nothing to laugh at.

I just did a quick test, a 100mb process generally takes >2ms to fork, while a 1mb or less process takes 70us. It seems like its pretty much linear with process size.

Re: A fork() in the road

#37

Fork has really weird semantics, and a lot of fun gotchas around managing resources. Good riddance?

Between the `fork()` and an `exec()`, I can:

    * redirect stdin, stdout, and stderr
    * open files that might be needed and close files that aren't
    * change process limits
    * drop privileges 
    * change the root directory
    * change namespaces
And there are a few other things I am probably forgetting.

Re: A fork() in the road

#38

Earlier quoted context omitted.

Can't you use posix_spawn() with WSL and your favorite POSIX-compatible libc implementation?

Well that's a complicated question to answer. You can use the posix_spawn function in glibc, which uses a vfork or clone syscall just like on Linux.

Also relevant, regarding the Linux native performance:

https://mobile.twitter.com/RichFelker/status/602313979894038...

"Rich Felker, May 24, 2015: Some interesting preliminary timing of @musllibc 's posix_spawn vs fork+exec shows it ~25x faster for large parent processes. (~360us vs 9ms). #glibc has a vfork-based posix_spawn but it's only usable for trivial cases; others use fork. @musllibc posix_spawn always uses CLONE_VM. This also means @musllibc posix_spawn will fill the fork gap on NOMMU systems cleanly/safely (unlike vfork) once we get NOMMU working."

Also evilotto's post here:

https://news.ycombinator.com/item?id=19622477

"a 100mb process generally takes >2ms to fork, while a 1mb or less process takes 70us"

Re: A fork() in the road

#39
post #37

Fork has really weird semantics, and a lot of fun gotchas around managing resources. Good riddance?

Between the `fork()` and an `exec()`, I can: * redirect stdin, stdout, and stderr * open files that might be needed and close files that aren't * change process limits * drop privileges * change the root directory * change namespaces And there are a few other things I am probably forgetting.

And ideally all these things become properties to a configuration object which is then used to spawn a process.

Re: A fork() in the road

#40
I readily admit that I am unfamiliar with POSIX_spawn() and its benefits over fork().

However, may I point out that Microsoft SQL Server benchmarks have been posted that show Linux TCP-H outperforming Windows?

https://www.dbbest.com/blog/running-sql-server-on-linux/

While I am sure that this is wise criticism, it might also be concluded that Windows itself contains no small amount of architectural decisions that limit performance.

Post reply on HN