11.3 Starting a new process can
start another program inside the program to create a new process. This work can be done through the library function systemYes.
#include <stdlib.h>
int system (const char *string);
the function of the system function is to run the command passed to it as a string parameter and wait for the command to complete.The execution of the command is like executing the following command in the shell:
$ sh-c String
If the shell cannot be started to complete this command, the system function returns error code 127, or 1 if it is a different error. Otherwise, system returns the exit code for the command.
Use the system function below to write a program that lets it run the PS command.
Because the system function uses a shell to start the program that you want to execute, you can put the program in the background. The practice is to modify the function call in system1.c to the following:
System ("ls-l&");
In
in the first example, the program calls the system function with the string "PS Ax" as a parameter to execute the PS command in the program. The program returns from the system call after the PS command is complete. The system function is very useful, but it also has limitations because
The program must wait for the process started by the system function to finish before it can continue, so other tasks cannot be performed immediately.
In
in a second exampleRight
The call to the system function returns immediately after the shell command ends. Because it is a request to run the program in the background, the LS program returns when the shell is started, which is the same as executing this command at the shell prompt.
$ ls-l &
Before the LS command can print out all output results, the SYSTEM2 program prints out the string done and exits. After the SYSTEM2 program exits, the LS command continues to complete its output. This kind of processing behavior often causes the user to have the very big perplexity. If you want to use a good process, You need to be able to control their behavior better. Here is a bottom-level interface exec that is used to create the process.
Generally speaking
using the system function is far from the ideal way to start other processes, because it must use a shell to start the required processBecause you need to start a shell before starting the program, and the installation of the shell and the dependency on the use is also very large, so the use of the system function is not efficient, in the next section, you will see a better method of calling the program, compared to the system call, This method should always be used first in the program.
1. Replace the process image the Exec series function consists of a set of related functions, which vary in how the process is started and how the program parameters are expressed.
The exec function can replace the current process with a new process, which is specified by the path or file parameter. You can use the EXEC function to switch the execution of a program from one program to another. For example, you can check the credentials of a user before starting another program that has a restricted usage policy. The EXEC function is more efficient than the system function because the original program is no longer running after the new program starts.
#include <unistd.h>
Char **environ;
int execl (const char *path, const char *arg0, ..., (char *) 0);
int EXECLP (const char *file, const char *arg0, ..., (char *) 0);
int execle (const char *path, const char *arg0, ..., (char *) 0, Char *const envp[]);
int execv (const char *path, char *const argv[]);
int EXECVP (const char *file, char *const argv[]);
int Execve (const char *path, char *const argv[], char *const envp[]);
These functions can be divided into two main classes, the number of arguments for EXECL,EXECLP and execle is variable, and the argument ends with a null pointer. The second parameter of EXECV and EXECVP is an array of strings. In either case, At startup, the new program passes the arguments given in the argv array to the main function.
These functions are usually implemented with EXECVE, although this is not necessary.
A function that ends with the letter P finds the path to the executable file for the new program by searching for the paths environment variable. If the executable file is not in the path defined by paths, you need to pass the file name with the absolute path, including the directory, as a parameter to the function.
The global variable environ is used to pass a value to the new program environment, in addition the function execle and Execve can pass the string array as the environment variable of the new program through the parameter envp.
If you want to start the PS program from the EXEC function case, you can select one of the 6 exec functions, as shown in the following code snippet:
#include <unistd.h>
/* Example of an argument list */
/* Note that we need a program name for ARGV[0] */
Char *const ps_argv[] = {"PS", "Ax", 0};
/* Example environment, not terribly useful */
Char *const ps_envp[] = {"Path=/bin:/usr/bin", "Term=console", 0);
/* Possible calls to EXEC functions */
Execl ("/bin/ps", "PS", "Ax", 0); /* Assumes PS is In/bin */
EXECLP ("PS", "PS", "Ax", 0); /* Assumes/bin is in PATH */
Execle ("/bin/ps", "PS", "Ax", 0, PS_ENVP); /* Passes OWN environment */
EXECV ("/bin/ps", PS_ARGV);
EXECVP ("PS", PS_ARGV);
Execve ("/bin/ps", PS_ARGV, PS_ENVP);
Write the Pexec program, you can see the normal PS output, but the string did not appear at all. It is also worth noting that the PS output does not have any information for the pexec process.
The program first prints out its first message, and then calls EXECLP, which searches for the program PS in the directory given by the PATH environment variable, and then replaces the PEXEC program with this program, just as if it were a shell command like this:
$ ps AX
At the end of the PS command, you see a new shell prompt, because it is not returned to the PEXEC program, so the second message is not printed. The Pid,ppid and nice values of the new process are exactly the same as they were originally. In fact, what happened here is actually, The running program starts executing code in the new executable file that is specified in the EXEC call.
For a process initiated by the EXEC function, the total length of its parameter table and environment added together is limited. The upper limit is given by Arg_max, which is 128K bytes on the Linux system. Other systems may have a very limited length, which can cause problems.
In general, the EXEC function is not returned, unless an error occurs and an EXEC function returns 1, and the error variable errno is set.
By
The new process initiated by exec inherits many of the attributes of the original process. In particular, the open file descriptors in the original process remain open in the new process, unless they are "closed on execution" (Close on Exec flag) is set. Any directory streams that have been opened in the original process will be closed in the new process.
2. Copy the process image
Toto allow a process to execute multiple functions at the same time, you can use a thread or create a completely detached process from the original program, which is the same as INIT, rather than replacing the currently executing thread with a new program like the exec call。
OKCreate a new process by calling Fork, which invokes the replication of the current process, creates a new table entry in the process table, and many of the properties in the new table key are the same as the current process. The new process is almost identical to the original process and executes exactly the same code, but the new process has its own data space, environment, and file descriptors. The combination of fork and EXEC functions is all you need to create a new process.。
#include <sys/types.h>
#include <unistd.h>
pid_t fork (void);
Initial process---->fork ()-----> New Process (return 0)
|
--------> The original process continues execution (returns a new PID)
InThe fork call in the parent process returns the PID of the new subprocess, and the new process continues, just as the original process does, except that the fork call in the child process returns 0. The parent-child process can determine exactly who is the parent process and who is the child process.
If the fork fails, it returns-1. The failure is usually due to the number of child processes owned by the parent process exceeding the specified limit (Child_max), at which point errno will be set to Eagain. If it is because there is not enough space in the process table to create a new form or virtual memory, the errno variable will be set to Enomen.
A typical code snippet that uses fork is as follows:
pid_t New_pid;
New_pid = fork ();
Switch (new_pid) {
Case-1:/* Error */
Break
Case 0:/* We are the child */
Break
Default:/* We are parent */
Break
}
Writing a program fork1.c
This program is running in two process ways. The child process is created and the message is output 5 times. The original process (that is, the parent process) only outputs the message 3 times. The parent process ends before the child process prints out all of its messages, so you can see a shell prompt mixed in the output.
The program is divided into two separate processes when calling fork. The program determines the parent process by the non-0 value returned by the fork call, and sets the output count of the message based on that value, one second between the output of the two messages.
11.3.1 waiting for a process
When you start a child process with fork, the child process has its own declaration cycle and runs independently. Sometimes, you want to know when a child process ends. For example, in the previous example program, the parent process ended before the child process, and the resulting output was a bit messy because the child process was still running. OKlet the parent process wait for the child process to end by calling the wait function in the parent process。
#include <sys/types.h>
#include <sys/wait.h>
pid_t Wait (int *stat_loc);
the wait system call pauses the parent process until its child processes end. This call returns the PID of the subprocess, which is usually the PID of the child process that has finished running. State information allows the parent process to understand the exit status of the child process, that is, the value returned by the main function of the child process or the exit code of the Exit function in the child process。 If Stat_loc is not a null pointer, the state information is written to the location it points to.
You can use macros defined in the Sys/wait.h file to interpret state information. As shown below:
Macro Description
Wifexited (Stat_val) If the child process ends normally, it goes to a non-0 value
Wexitstatus (stat_val) if wifexited nonzero, it returns the exit code of the child process
Wifsignaled (Stat_val) If a child process terminates because of an uncaught signal, it takes a non-0 value
Wtermsig (stat_val) if wifsignaled nonzero, it returns a signal code
wifstopped (Stat_val) If the child process terminates unexpectedly, it takes a non-0 value
Wstopsig (stat_val) if wifstopped nonzero, it returns a signal code
Writing wait.c
The WAIT.C function has the following code than the FORK1.C function:
if (pid! = 0) {
int stat_val;
pid_t Child_pid;
Child_pid = Wait (&stat_val);
printf ("Child has Finished:pid =%d\n", child_pid);
if (wifexited (Stat_val))
printf ("Child exited with code%d\n", Wexitstatus (Stat_val));
Else
printf ("Child terminated abnormally\n");
}
The parent process, which obtains a nonzero return value from the fork call, suspends its execution with the wait system call until the child process's state information appears. This will occur when the child process calls exit. Set the exit code of the child process to 37. The parent process then continues to run, judging whether the child process terminates normally by testing the return value of the wait call. If so, this extracts the exit code of the child process from the status information.
11.3.2 Zombie Process
creating a process with fork is really useful, but it must be clear how the child process is running. When a child process terminates, the association between it and the parent process remains until the parent process terminates normally or the parent process calls wait. So, Table entries in the process table that represent child processes are not released immediately. Although the child process is no longer running, it still exists in the system because its exit code needs to be saved for future wait calls from the parent process. This is the process that it will become a dead (defunct) process or a zombie (zombie).
If you modify the number of message outputs in the Fork sample program, you can see the zombie process. If the child process outputs a message less than the parent process, it will be the first to end and become a zombie process until the parent process ends.
If you use the./FORK2 & command to run the above program and then call the PS program before the end of the child process, you will find FORK2 <defunct>
If the parent process terminates abnormally at this point, the child process automatically takes the PID 1 process (that is, Init) as its parent process. The child process is now a zombie process that is no longer running, but because its parent process terminates abnormally, So it is taken over by the Init process. The zombie process remains in the process table until it is discovered and freed by the INIT process. The larger the process table, the slower the process. should be
Avoid zombie processes as much as possible, because they will consume system resources until Init cleans them up.
And also
Another system call can be used to wait for the end of a child process, which is the Waitpid function, which may be used to wait for the end of a particular process.
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid (pid_t pid, int *stat_loc, int options);
The PID parameter specifies the PID of the child process that needs to wait, and if its value is -1,waitpid, the information for any child process is returned. As with wait, if Stat_loc is not a null pointer, Waitpid will write the status information to the location it points to. The option parameter can be used to change the behavior of Waitpid, one of the most useful options is Wnohang, Its role is to prevent waitpid calls from suspending the execution of the caller. You can use this option to find out if a child process has ended, and if not, the program will continue to execute. The other options are the same as the wait drop option.
Therefore, if you want the parent process to periodically check whether a particular child process has been terminated, you can use the following method of invocation:
Waitpid (child_pid, (int *) 0, Wnohang);
If the child process does not end or terminates unexpectedly, it returns 0, otherwise it returns CHILD_PID. If Waitpid fails, it returns-1 and sets the errno. Failure scenarios include: No Child process (errno is set to Echild), call is interrupted by a signal (EINTR) or option parameter is invalid (EINVAL)
11.3.3 input and output redirects have been
Open file descriptors are retained after the fork and exec calls, and can be used to alter the behavior of the program by understanding the knowledge of the process.The next example involves a filter: it reads the data from the standard input, then writes the data like a standard input, and makes some useful conversions between the input and the output.
Write the filter upper.c, which reads the input and converts the input characters to uppercase
You can also use the shell redirection to convert the contents of a file to uppercase, first touch file.txt, and then enter the following:
This is the file, File.txt, was is all lower case.
After saving, use the command./upper < file.txt
This is the file, file. TXT, IT's all LOWER case.
What happens if you want to use this filter program in another program? The following program USEUPPER.C accepts a file name as a command-line argument, and if the call to it is incorrect, it responds with an error message.
Writing a program useupper.c
When you run this program, you can provide it with a file that converts the contents of the file to uppercase. This work is done by the program upper, but it does not handle file name parameters. You can run any executable program in the following way:
$./useupper file.txt
U
The seupper program uses the Freopen function to turn off standard input, then associates the file stream stdin with the file name given by the program, and then calls Execl to replace the calling running process code with a upper program. Because the file descriptor that has already been opened is preserved after the EXECL call, the upper program runs exactly as it did under the prompt.
The processes in the 11.3.4 thread Linux system can assist each other, send messages, interrupt each other, and even share memory segments. But essentially, they are separate entities within the operating system, and it's not easy to share variables between them.
In many UNIX and Linux systems, there is a class of processes called threads, which are difficult to program with threading, but are of great use in some applications, such as multi-threaded database servers. Writing a thread program in a Linux system is not as common as writing multi-process programs, because processes in Linux are very lightweight, and writing multiple, collaborative processes is much easier than writing threads. The thread is introduced in the 12th chapter.
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
Linux programming-Processes and Signals (chapter 11th)