Abstract: This section describes some basic operations on a process. Through this section, we will learn how to generate a sub-process, how the process changes its execution image, and synchronization of parent and child processes. We also learned some basic concepts of parallel programs and how to compile simple parallel programs.
2. General process operations
The previous section introduces some basic concepts about processes. Starting from this section, we will introduce some examples to illustrate the system calls of related processes. This section describes some basic operations on a process. Through this section, we will learn how to generate a sub-process, how the process changes its execution image, and synchronization of parent and child processes. We also learned some basic concepts of parallel programs and how to compile simple parallel programs.
2.1 fork system call
Fork is used to create a sub-process. The creation process is described in the previous section. Now, we will introduce a system call to vfork, which is generated because it realizes that the performance can be improved by not copying all pages of the parent process when creating a child process. This call assumes that exec will be called immediately after vfork is called, so that you do not need to copy all the page tables of the parent process. Because it does not copy the page table, it is faster than the fork call. In some systems, fork also uses other methods to improve performance. A typical method is to add "Copy at write time ". In this fork call, when a child process is generated, the child process does not copy all pages of the parent process, but copies all pages of the parent process at write time. The child process shares all pages of the parent process. When a parent or child process writes a page, a protective error occurs and the page is copied. This not only improves the kernel performance, but also improves memory utilization.
The declaration formats of fork and vfork are as follows:
Pid_t fork (void );
Pid_t vfork (void );
Add the following header files to programs called by the system:
# Include <unistd. h>
When the call is successful, the call returns the child process PID to the parent process and the child process returns 0. If the call fails,-1 is returned to the parent process, and no child process is created.
The following error code errno may be set when an error occurs:
Eagain: The system calls fork and cannot obtain enough memory to copy the parent process page table. Or the user is a Super User but the table is full, or the user is not a Super User but reaches the maximum number of processes that a single user can execute.
Enomem: there is not enough space for creating a new process. This error indicates that there is not enough space allocated to the necessary kernel structure.
The following is a simple example of fork call. In this example, a child process is generated. The parent process prints the PID of itself and the child process, and the child process prints its own PID and the PID of the parent process.
Note: the parent process opens a file. Both parent and child processes can operate on the file. The Parent and Child processes of the program write a row into the file.
# Include <stdio. h>
# Include <string. h>
# Include <unistd. h>
# Include <stdlib. h>
# Include <sys/types. h>
# Include <sys/STAT. h>
# Include <fcntl. h>
# Include <errno. h>
Extern int errno;
Int main ()
{
Char Buf [100];
Pid_t cld_pid;
Int FD;
Int status;
If (FD = open ("Temp", o_creat | o_trunc | o_rdwr, s_irwxu) =-1)
{
Printf ("Open error % d", errno );
Exit (1 );
}
Strcpy (BUF, "this is parent process write ");
If (cld_pid = fork () = 0)
{/* Here is the code executed by the sub-process */
Strcpy (BUF, "this is child process write ");
Printf ("this is child process ");
Printf ("My PID (child) is % d", getpid ();/* print the ID of the process */
Printf ("My parent PID is % d", getppid ();/* print the ID of the parent process */
Write (FD, Buf, strlen (BUF ));
Close (FD );
Exit (0 );
}
Else
{/* Code executed by the parent process */
Printf ("this is parent process ");
Printf ("My PID (parent) is % d", getpid ();/* print the ID of the current process */
Printf ("My child PID is % d", cld_pid);/* print the ID of the sub-process */
Write (FD, Buf, strlen (BUF ));
Close (FD );
}
Wait (& status);/* What if there is no such sentence? */
Return 0;
}
Let's take a look at the program running result. Assume that the source file is named fork. C:
[Root @ wapgw/root] # gcc-O fork. c
[Root @ wapgw/root] #./fork
This is parent process
This is child process
My PID (child) is 5258
My parent PID is 5257
My PID (parent) is 5257
My child PID is 5258
[Root @ wapgw/root] #
From the preceding running results, we can see that the process is scheduled. After the parent process prints the first line, the CPU schedules the sub-process and prints the next three lines. The sub-process ends, schedule the execution of the parent process (other processes may be scheduled). After the execution of the parent process, return the control to the shell program. The last line is the prompt output by the shell program.
See what is in the temp file.
[Root @ wapgw/root] # More temp
This is child process write
This is parent process write
[Root @ wapgw/root] #
Now we will slightly modify the program. Comment out the wait call and let's see what the results will look like. If you execute multiple times for scheduling, you will see the following results:
[Root @ wapgw/root] # vi fork. C // comment out the wait call
[Root @ wapgw/root] # gcc-O fork. c
[Root @ wapgw/root] #./fork
This is parent process
This is child process
My PID (parent) is 5282
My child PID is 5283
[Root @ wapgw/root] # My PID (child) is 5283
My parent PID is 1
[Root @ wapgw/root] #
The first line is the output of the parent process, the second line is the output of the child process, and the third and fourth lines are the output of the parent process. In this case, the parent process has no wait call, stops without waiting for the child process. In the following line, "[root @ wapgw/root] #" indicates the end of the parent process and the shell output prompt when the control is returned to the shell. Then the CPU calls the sub-process and the PID of the output sub-process is 5283. Note: the ID of the parent process output by the lower-face process is 1, because its parent process is finished, and the kernel gives it to process 1 (process init) for management, for more information about this process, see the previous section. The output result sequence is related to the process scheduling sequence. The result of your own test may be different from the sequence in the example. Please analyze it on your own. (According to the results provided by my system, the addition and deletion of wait are all the same. The sub-process is executed first, and then the parent process is executed. However, a message is sent from the parent process to the child process in the MPs queue, the sub-process can also be normally received. It seems that the kernel scheduling is intelligent now, and the specific scheduling sequence needs to be further studied)
2.2 exec system call
The system calls exec to execute an executable file to replace the execution image of the current process. It should be noted that the call did not generate a new process, but instead replaced the text of the original process on the basis of the original process. The process id remains unchanged before and after the call. But the execution program has changed (the execution command sequence has changed ). It has six calling modes, which are not exactly the same as described below. Their declaration formats are as follows:
Int execl (const char * path, const char * Arg ,...);
Int execlp (const char * file, const char * Arg ,...);
Int execle (const char * path, const char * Arg,..., char * const envp []);
Int execv (const char * path, char * const argv []);
Int execve (const char * filename, char * const argv [], char * const envp []);
Int execvp (const char * file, char * const argv []);
Add the following header files and external variables to programs that use these system calls:
# Include
Extern char ** environ;
Next we will first detail one of them, and then give the difference between them. In the execve system call, the path parameter is the file to be executed, the argv parameter is the parameter to be passed to the file, and the envp parameter is the environment variable to be passed to the file. After the file indicated by the path parameter replaces the execution image of the original process, the file path starts to be executed, and the argv and envp parameters are passed to the process. The following is a simple example.
Let's take a look at the environment variables before describing the example of a system call to execve. Linux introduces the concept of environment to allow users to use shell conveniently and flexibly. The environment is some data. You can change the data, add new data, or delete some data. These data are called environment variables. Because they define the user's work environment and can be modified at the same time. Each user can have their own different environment variables. You can use the Env command (without parameters) to browse the environment variables, the output format and variable name vary with Shell and system configuration. In the following example, all parameters and environment variables passed to the process are printed:
# Include
# Include
Extern char ** environ;
Int main (INT argc, char * argv [])
{
Int I;
Printf ("argument:/N ");
For (I = 0; I <= argc; I ++) printf ("Arg % d is: % s/n", I, argv [I]);
Printf ("environment:/N ");
For (I = 0; Environ [I]! = NULL; I ++) printf ("% S/", Environ [I]);
}
The Screen Copy during execution is as follows:
[Root @ wapgw/root] # gcc-O example. c
[Root @ wapgw/root] #./example test
Argument:
Arg0 is./Example
Arg1 is test
Environment:
Pwd =/root
Remotehost = CJM
Hostname = wapgw
Home =/root
.......................
Ssh_askpass =/usr/libexec/ssh/gnome-ssh-askpass
Path =/usr/local/sbin:/usr/local/bin:
/Sbin:/bin:/usr/sbin:/usr/bin:/usr/x11r6/bin:/root/bin
[Root @ wapgw/root] #
Environment variables and their values are used after environment. Let's take a look at a simple example of execve:
# Include
# Include
Extern char ** environ;
Int main (INT argc, char * argv [])
{
Printf ("will replace by another image ");
Execve ("example", argv, Environ);/* replace the process execution image with example in the preceding example */
Printf ("process never go to here");/* The process will never be executed here */
}
The program uses its own parameters argv and environment variables to pass to the new execution image. The Screen Copy of the execution result is as follows:
[Root @ wapgw/root] # gcc-O execve. c
[Root @ wapgw/root] #./execve these ARGs will dend to Example
Will replace by another image
Argument:
Arg0 is./execve
Arg1 is these
Arg2 is ARGs
Arg3 is Will
Arg4 is dend
Arg5 is
Arg6 is example
Environment:
Pwd =/root
Remotehost = CJM
Hostname = wapgw
Home =/root
.......................
Ssh_askpass =/usr/libexec/ssh/gnome-ssh-askpass
Path =/usr/local/sbin:/usr/local/bin:
/Sbin:/bin:/usr/sbin:/usr/bin:/usr/x11r6/bin:/root/bin
[Root @ wapgw/root] #
Note that if you use execve some ARGs> screen, you will find that after the output is redirected to a file, lost Data (that is, the first line of the output will replace ...). This is because after the output is redirected to a file, the first line of the process is output to the file, so the buffer has not actually written the file, and the second line of the process replaces the execution image of the process, it also overwrites the buffer of the file. This problem can be solved by refreshing the stdout buffer through fflush (stdout) or using setbuf (stdout, null) to set the stdout buffer to null.
If the close-on-exec flag is set for a file descriptor FD, the file descriptor is closed after exec is called. Here is a simple example:
Here is a program pp. C:
# Include
Int main ()
{
Printf ("test ");
}
It is used to replace the process execution image. Let's take a look at the following program:
# Include
# Include
# Include
Extern char ** environ;
Int main (INT argc, char * argv [])
{
Printf ("Close-on-exec is % d", fcntl (1, f_getfd ));
Fcntl (1, f_setfd, 16 );
Printf ("Close-on-exec is % d", fcntl (1, f_getfd ));
Execve ("PP", argv, Environ );
Printf ("Ah !!!!! ");
}
The execution result of this program is:
[Root @ wapgw/root] #./fcntl
Close-on-exec is 0
Close-on-exec is 0
Test
[Root @ wapgw/root] #
This is the result that the close-on-exec flag is not set. Change the fcntl statement
Fcntl (1, f_setfd, 25 );
For the last parameter, you only need to ensure that the bitwise (Binary) of this parameter is 1. The execution result is as follows:
[Root @ wapgw/root] #./fcntl
Close-on-exec is 0
Close-on-exec is 1
[Root @ wapgw/root] #
At this time, the system calls execve to replace the execution image of the original process with pp. However, since file descriptor 1 (stdout) is disabled, no output is generated after execve is called.
The system calls execve to execute binary executable files (such as a. out ). You can also run a shell program. The shell program must start with the format shown in the following figure, that is, the first action :#! Interpreter [Arg]. Interpreter can be a shell or another interpreter, for example :#! /Bin/bsh or #! /Usr/bin/Perl. Arg is the parameter passed to the interpreter.
When the system call is successful, no value is returned (because the execution image of the process has been replaced and the returned value is not received ). If there is any return value (generally-1), it indicates that an error occurs. The kernel will set the corresponding error code errno. Below are some possible error codes:
Eacces: the file or script file to which the file is directed is not a common file; the executable bit is not set for the file or script file to which the file is directed; the file system is installed as noexec; the directory in the path of the file or script file cannot be searched (no execute permission ).
E2big: the list of passed parameters is too large.
Enoexec: the specified file does have an execution limit, but it is in an unidentifiable format.
Etxtbusy: specifies that the file is opened by one or more processes in a writable manner.
EIO: an I/O error occurs when reading from the file system.
Now let's take a look at this kind of system calls. In System Call execl, execlp, and execle, the parameters are arg0, arg1, arg2 ,... . By convention, arg0 should be the name of the program to be executed. The values of environment variables in the invocation of execl and execlp are automatically transferred, that is, the envp parameter is not specified in the call as in the invocation of execve and execle. Parameters in execve, execv, and execvp are transmitted in an array. Another difference is that execlp and execvp can be called to find the execution program in the path defined by the Environment Variable path. For example, the path is defined as "/bin:/usr/sbin". If you specify the execution file name as test, the two calls will find the executable file named test in the three directories defined by path.
The system calls exec and fork in combination. The parent process fork is a sub-process, and exec is called in the sub-process to replace the execution image of the sub-process and execute some operations concurrently.
2.3 exit system call
The system calls the exit function to terminate the calling process. The statement format is as follows:
Void _ exit (INT status );
Add the following header files to programs that use this system call:
# Include
The system calls _ exit to terminate the process that initiates the call immediately. All file descriptors belonging to the process are disabled. All sub-processes of the process are received by process 1 (process init), and a sigchld (sub-process dead) signal is sent to the parent process of the process. The status parameter is returned to the parent process as the exit status value, which can be collected by calling wait. The returned status code status is valid in only one byte. If a process is a control terminal process, the sighup signal is sent to the frontend process of the control terminal. System Call _ exit never returns any value to the process that sends the call, nor refresh the I/O buffer. to automatically complete the refresh, you can use the function to call exit.
2.4 wait system call
The wait function of the system is to call a process that has sub-processes and sleep until one of them is terminated. The call declaration format is as follows:
Pid_t wait (int * Status)
Pid_t waitpid (pid_t PID, int * status, int options );
Add the following header files to programs that use these system calls:
# Include
# Include
The process that sends a wait call goes to sleep until one of its sub-processes exits or is awakened when it receives a signal that cannot be ignored. If a sub-process has exited when the call is sent (the sub-process is in the dead state), the call will return immediately. The status parameter returned by the call contains the status information when the sub-process exits.
The difference between waitpid and wait is that waitpid waits for the child process specified by the PID parameter to exit. The meanings and values of the parameter PID are as follows:
When the PID parameter is set to <-1, stop waiting when the exited sub-process meets the following conditions: the process group ID (Process Group) of the sub-process is equal to the PID of the absolute value.
When the PID is set to 0, wait for any sub-process that meets the following conditions to exit: The Process Group ID of the sub-process is equal to the ID of the Process Group that sends the call process.
When the PID parameter is greater than 0, wait for the child process whose process ID is equal to the PID parameter to exit.
When the PID =-1, wait for any sub-process to exit, which is equivalent to calling wait.
The optional values of the waitpid parameter and their meanings are as follows:
Wnohang: This option requires immediate return if no sub-process exits.
Wuntraced: For stopped but not reported sub-processes, this call also returns and reports the status from waiting. If the status is not empty, the call directs the status to this information. The following macros can be used to check the return status of sub-processes. The first three are used to determine the reason for exit, and the last three are returned for different reasons:
Wifexited (Status): If the process Exits normally through System Call _ exit or function call exit, the macro value is true.
Wifsignaled (Status): If the sub-process exits because the signal (signal) is not captured, the macro value is true.
Wifstopped (Status): If the sub-process is not terminated but can be re-executed, the macro returns true. This case only occurs when the wuntraced option is used in the waitpid call.
Wexitstatus (Status): If wifexited (Status) returns true, the macro returns the status value of the call parameter set when the sub-process calls _ exit (Status) or exit (Status.
Wtermsig (Status): If wifsignaled (Status) returns true, this macro returns the value of the signal that causes the sub-process to exit (signal.
Wstopsig (Status): If wifstopped (Status) returns true, this macro returns the signal (signal) value that causes the sub-process to stop.
This call returns the PID of the exited sub-process; or-1 if an error occurs; or 0 if no sub-process exits if the wnohang option is set; when an error occurs, the possible error code is as follows:
Echild: the specified sub-process PID does not exist or is not the sub-process that calls the process.
Einval: The options parameter is invalid.
Erestartsys: wnohang is not set and sigchld or other unshielded signals are captured.
For the example of wait calling, we introduced a simple application for Fork calling. This example is not used here.
Note: setting different processing methods for the signal of the sub-process exit (sigchld) may result in different calls. For details, refer to the Linux signal processing mechanism.
2.5 sleep function call
Function call sleep can be used to suspend a process for a specified number of seconds. The Declaration format of this function call is as follows:
Unsigned int sleep (unsigned int seconds)
Add the following header file to the program that uses this function call:
# Include
This function causes the process to suspend for a specified time until the specified time is used up or a signal is received. System activities have a certain impact on the specified time. In Linux, the sleep function is implemented using sigalrm. in Linux, the sleep function cannot be mixed with alarm () calls.
If the specified suspension time is reached, the call returns 0; if the function call is interrupted by the signal, the remaining suspension time is returned (the specified time minus the suspended time ).