There are two ways to achieve concurrency, one of which is to have each "task" or "process" work in a separate inner space, each with its own working memory area. However, while processes can be executed in a separate memory space, they are not actually run "at the same time" unless they are executed on a separate processor. The processor's time slice is assigned to a process by the operating system, and the time slice is used to exit the processor and wait for another time slice to arrive. Another way is to specify multiple "threads of Execution" in the program to allow them to work in the same memory space. This is called multithreaded processing. Threads are more efficient than processes because the operating system does not have to create separate memory spaces for each thread.
The new process uses the Os.fork function. But it is only available on POSIX systems, and in the Windows version of Python, the OS module does not define the Os.fork function. Instead, Windows programmers use multithreaded programming techniques to accomplish concurrent tasks.
The process of creating a os.fork function is like this. Each time the program executes, the operating system creates a new process to run the program directives. The process can also call Os.fork, which requires the operating system to create a new process. The parent process is the process that calls the Os.fork function. The process created by the parent process is called a child process. Each process has a process ID number that is not duplicated. Or PID, which identifies the process. The child process is exactly the same as the parent process, where the child process inherits copies of multiple values, such as global variables and environment variables, from the parent process. The only difference between the two processes is the return value of the fork. The child process receives a return value of 0, and the parent process receives the PID of the child process as the return value.
Child processes and parent processes created with Os.fork are executed separately as asynchronous concurrent processes. Asynchronous means that they are fragmentation and do not synchronize with each other; concurrency means that they can be executed concurrently. So we cannot know the relative speed of the child process and the parent process.
The Os.wait function is used to wait for the child process to end (only for UNIX-compatible systems). The function returns a tuple containing two elements, including the completed child process number PID, and the exit status of the child process, with a return status of 0, indicating that the child process completed successfully. The return status is a positive integer indicating an error occurred when the child process terminated. If there are no child processes, a OSError error is raised. Os.wait requires the parent process to wait for any one of its child processes to end execution and then wake the parent process.
To instruct the parent process to wait for a specified child process to terminate, use the Os.waitpid function in the parent process (for UNIX-compatible systems only). It waits for a specified process to end, and then returns a two-element tuple that includes the PID of the child process and the exit state of the child process. The function call passes the PID as the first parameter and an option as the second option, and if the first argument is greater than 0, then Waitpid waits for the PID to end, and if the first parameter is-1, it waits for all child processes, just like os.wait.
Use the Os.system and os.exec function families to execute system commands and other programs. Os.system uses the shell to execute system commands, then returns control to the original process after the command is finished, and the Os.exec function family does not return control to the calling process after the command has been executed. It takes over the Python process and the PID does not change. These two functions support both UNIX and Windows platforms.
The Os.popen () function executes the command and obtains the stdout stream of the command. The function takes two arguments, one is the command to execute, and the other is the pattern used to invoke the function, such as "R" read-only mode. The Os.popen2 () function executes the command and obtains the stdout stream and stdin stream of the command. The function returns a tuple that contains two file objects, one object corresponding to the stdin stream, and one object corresponding to the stdout stream.
Processes use the IPC mechanism to pass information between processes, an IPC mechanism is a "conduit", a file-like object that provides a one-way communication channel. The parent process can open a pipeline and then branch a child process. The parent process uses the pipeline to write information to the child process, and the child process uses the pipeline to read the information from the parent process. Create a pipeline using the Os.pipe function in Python.
Os._exit () is similar to Sys.exit (), but it does not perform any cleanup work (such as flushing buffers). So Os._exit () is especially useful for exiting child processes. If the program uses Sys.exit (), the operating system reclaims resources that the parent process or other child processes might still need. The arguments passed to the Os._exit () function must be the exit state of the process. The exit status is 0, which indicates normal termination.
The process can also communicate using signals. The so-called "signal" is the operating system to take the asynchronous way to the program message. If CTRL + C passes an "interrupt signal", usually the signal causes the program to abort. However, the program can definitely specify a different action to respond to any one signal. In signal processing, the program receives a signal and takes an action based on that signal. Errors (such as writing to closed pipes), events (such as timers becoming 0), and user input (such as pressing CTRL + C) generate a signal.
For each signal, each Python program has a default signal handler. For example, suppose the Python interpreter receives a signal indicating that the program attempted to write to a closed pipe, or that the user typed in a keyboard interrupt, and Python throws an exception. After an exception occurs, the program can either use the default handler or use a custom handler.
The Signal.signal function registers a signal handler for the interrupt signal. The function obtains two parameters: a signal and a function corresponding to the signal handler.
In a unix/linux system, when a child process terminates, it remains in the process table, letting the parent process know whether the child process terminates normally. If you create a large number of child processes, but do not remove them from the process table after termination, the process tables accumulate more and more dead processes called "Zombies" (Zombie processes), and the action to eliminate the zombie process is called "reaping", This is achieved through the os.wait and OS.WAITPID functions.
18.2. Good programming habits
18.3. Portability Tips