When you make a new tab Chrome (usually) starts a new process for that
tab. How is this done? It would seem natural to just
fork can't be used safely in the presence of threads.
forks the current thread but other threads may be holding locks
(including e.g. inside glibc or in the allocator) which would never
be released after the fork.
If you are careful to not touch anything after a fork, it can be safe
exec. This matches the process launching model on
Windows (no fork, only fork+exec), with the negative that it forces
the overhead of startup again on each new process. (Code reference:
LaunchProcess(), which also knows to e.g. use
Instead, at startup, before we spawn any threads, we fork off a helper process. This process opens every file we might use and then waits for commands from the main process. When it's time to make a new subprocess we ask the helper, which forks itself again as the new child. By virtue of always forking from the same initial process, we guarantee that we are always running the same code; even if the files we opened are replaced by a system update our handle on them is the handle for the previous file. (That works as long as nobody overwrites the contents of the file we have open; thankfully, package updates write a new file and rename it over the old name, leaving our open copy the only remaining reference to the old file.)
ifdef soup used when launching a subprocess. Truly some
This solution is both clever and an ugly hack. Any time someone adds code to Chrome that interacts with a file on disk they either need to be aware that they need to preemptively open it or they will produce mysterious failures across updates (in practice, usually the latter; e.g. bug 35793: Devtools stop working when chrome gets updated). An interesting question to ask is: why is this not a problem on Windows and Mac?
On Windows, files are locked if any process is using them, which forces a design where updates install into a separate directory. But -- annoyingness of locking aside -- in fact I think that design is preferable. To start with, a given version of Chrome will know its files will remain unmolested by updates. Furthermore, when an update happens, the updater can write out a separate "update succeeded" sentinel after writing all the files out, making impossible for an aborted update to leave both the previous and next version in a half-working state. (On Mac, we take a similar approach; I don't know enough about Macs to know whether the versioned directories within bundles make this magically work.)
With all this in mind you might reasonably ask why Linux needs to be
special: why we waste memory on this zygote process launcher and have
extra buggy codepaths just to support an inferior update model. (Note
that by using
.deb files we also lose our tiny incremental
And to that I can only answer the thinking we had at the time: one, we wanted to be good citizens on Linux; one distinction between "lame port of a Windows app" and "real Linux software" is exactly whether you distribute as a tarball or as a package. Secondly, and more importantly, we knew that regardless of what we did for Google Chrome the Linux distros would attempt to stuff Chromium into their package manager even when they know it breaks the app, much like they've done to Firefox. Now that I've summarized it in these terms it sounds a little depressing, but there it is; with ChromeOS where we control the stack we have more intelligent updates.