The Clone Trick of Programs
The clone trick in the title refers to the supernatural ability that the monkey king possesses in the story of the Journey to the West, one of the most popular Chinese traditional novels. When the monkey king needs a helper, he clones himself and goes on to other places while the cloned replica stays on current task. Wouldn’t it be nice if we could have such an ability? We could have our replica continue working while we enjoy life and free time. In the digital world, running programs often perform exactly the same trick as the monkey king.
A program is normally an executable machine code file; when it is loaded by the Operating System (OS), it is read into the memory and executed by the processor. A running program is called a process, an entity that consumes computer resources, such as memory, processor time, and I/O devices. A computer can have many running processes at the same time. The OS manages the processes and schedules them to run based on their priorities.?
A program is written to perform a function for users or the OS. Some functions are unique and singular such as a calculator or video player; some functions are of server type and require calling other programs to fulfill users’ requests. For example, a shell program displays a command prompt and waits for users’ inputs. Upon receiving users’ commands, the shell program executes the corresponding commands, displays outputs, then returns to command prompt and waits for user inputs again.?
Another example is a TCP server program. It listens to the incoming data on an Internet socket; when a connection request is received, the program accepts the connection and responds to the request.? There could be multiple requests at the same time; the server program should be designed to handle each request while continuing to listen on the socket for new requests.?
In above shell and server examples, the programs have two distinct functions: 1) Wait for user command input/request; 2) Respond to the command input/request. Programs can use the clone trick to create a new process to fulfill these two functions.
In Linux, a program can use fork() system call to create a new process. When fork() is called, the OS creates a new child process, by duplicating the memory segments of the original process, which becomes the parent process. Now both the parent and child processes share and execute the same code.?
At first glance, it seems strange that the program needs to create a new process to execute a new program. Why can’t the same program execute the new program? The reason is the new program to be executed is in a separate executable file. To execute a separate program, a new process must be created; fork() can be considered a quick and easy way to create a new process by duplicating memory segments of the existing process, basically a copy-paste in process creation. If we were to avoid creating a new child process, we will need to include the code for all kinds of possible user commands in the shell program, which increases memory usage and reduces modularity.?
As the intent of a fork is to have two processes carry out different tasks, the program needs to be able to distinguish between the parent and the child process. This is done by the different return values to the parent and child process: the child process ID to the parent process and 0 to the child process. The program can check the return value and execute different codes. The child process can run the exec() system call to load a new executable file, which transforms itself into a completely new process with memory segments from the new program. Because in practice, the fork() is almost always followed by an exec(), it would be a waste of time to replicate the memory segments and then replace them with the new program. So the OS uses a copy-on-write technique, which replicates the pointers to the memory segments and only creates new memory segments when there are writes to the memory.
领英推荐
The separation between the fork() and exec() allows the child process to set up the runtime environment before executing the new program. For example, if the user command needs to redirect output to a file instead of the standard output device, the child process can close the file descriptor of the standard output and then open a new file descriptor for the file. The command program is executed as usual, but the output will go to the file as the new file descriptor replaces the standard output device.
Once the child process is created, the parent process can choose to continue running in parallel or perform the wait() system call, which suspends the parent process and waits for the child process to finish. In the case of the shell program, the shell stops taking user input when a user command is being executed. So the child process finishes execution first, then the parent process continues execution.
For the server program, concurrent execution is required. The child process handles the requests while the parent process listens to the new requests at the same time. This would require both processes to have their own sockets for receiving and sending data. When the server process listens to a socket, it runs the accept() system call, which returns a new file descriptor that corresponds to a new socket when a connection request is received. Then, the server program runs fork() to create a child process with the replicated listening socket and new connection socket. The child process would handle the request via the new socket while the parent process continues accepting connection requests on the original socket.
The clone ability provided by the system calls from the OS seems to give running programs a sense of life. They can now create child processes to help with tasks, a supernatural ability in the digital world.
Reference
[1] The Linux Programming Interface. Michael Kerrisk, no starch press, 2010.