Very discordant fork multi-threaded
Continue the topic of the previous few days. Do a fantastic West Tour server optimizer thing. Previous code, the work of a regular disk two steps, the dynamic Data in the VM serialization, and then the serialized data write disk. In these two steps, the serialization work is not done independently in a separate thread/process, but on the main thread. The IO section is in a separate process.
Serializing a task is a tedious process. Very time consuming (as opposed to MMORPG, an environment that requires a quick response to user requests). When the number of players at the same time on the rise, a simple optimization method is to complete the serialization of the task in step, spread across multiple heartbeats. There are some data consistency issues here, but there are different ways to solve them.
However, the serialization process can still have a significant impact on system performance after the number of people online has reached a certain size. When you do a regular disk, the player's input response speed is significantly larger. Performance is the game server periodic card. To alleviate this, I want to transform the system to separate the serialization tasks into separate processes.
The method is very simple, in the time to save the moment, call Fork, and then in the child process slowly do the serialization work. (You might consider using Nice) and then hand over the data to the IO process to write the disk. However, given our earlier design problem, in implementation, I need to return the serialized result to the parent process through shared memory, which is sent by the parent process to the IO process.
Because fork produces a snapshot of memory, there is even no data consistency problem. This should be a common pattern used by online games.
The problem is that, through historical changes, our servers are already using multi-threading, which makes the fork process less reliable and needs to be hammered out.
Multi-process multithreaded program, it sounds much more unreliable. It is the people who are so busy that they will do this design. But you can still use the almighty refuse: history is the result.
In the POSIX standard, the fork behaves like this: copying data from the entire user space (usually using the copy-on-write policy, so it can be implemented quickly) and all system objects, and then only copying the current thread to the child process. Here: All the other threads in the parent process are suddenly evaporated in the child process.
The sudden disappearance of other threads is at the root of all problems.
I have never written too many process multithreaded programs, but the company has David Xu classmate (he realizes the maintenance of FreeBSD line libraries) is this expert, today and Xu classmate discussed an afternoon, finally feel oneself to understand the tangled. Well, write something and sort out the idea.
The most serious problem that may arise is the lock problem.
Because for performance, most of the system's locks are implemented in user space. So the lock object is copied to the child process because of the fork.
For locks, from the OS, each lock has an owner, the last thread to lock it.
Assuming such an environment, before fork, a child thread locks a lock and gains ownership of the lock. After fork, all the extra threads in the sub-process have evaporated in the human world. While the lock is normal copy, in the child process, the lock has no owner, so no one can unlock it.
When the child process wants to lock the lock, there is no longer any means to untie it. The program has a deadlock.
Why, when POSIX specifies a standard, does it set such an apparently dubious rule? Allow a completely dead lock to be copied? The answer is history and performance. Because historically, it is most convenient to implement the lock in the user state (as is still the case today). You may only need an atomic operation instruction behind your back. Supported by most CPUs. Fork does not involve the object details in the copy of the user space.
As a general rule, multi-threaded threads should be used by the thread that initiates the fork to lock all child processes that may be needed, fork, and put them one by one unlock. This, of course, implies the order of the locks. If the order is different from normal, then it will deadlock.
Not only is explicit use of locks, but many CRT functions are also used indirectly. such as fprintf these file operations. Because the operation of FILE * relies on locks to achieve thread safety. The most common problem is calling fprintf to write log in a sub-thread.
In addition, be careful about some data consistency issues that do not depend on locks. For example, if a linked list is manipulated in another thread in the parent process, when the fork occurs, because the other threads suddenly disappear, the linked list may be because it only operates half of the data and is incomplete. However, this is generally not a problem, or can be attributed to the handling of locks. (Multiple threads, accessing the same piece of data.) Like a linked list. That need to be locked)
In the final citation of the discussion, David Xu's remark "POSIX has been a hot topic for discussion." And the position of both sides is clear, one side is the user, the other is the realization, the two sides accuse each other.
It suddenly occurred to me that lua/java the implementation of these VMS, can the fork be used to alleviate the stagnation caused by GC? Only need to be in the GC, fork a copy out to do the scanning. Find the garbage that is not referenced,
Turn from Cloud Blog: http://blog.codingnow.com/2011/01/fork_multi_thread.html
[Turn] a very discordant fork multi-threaded thread