Linux network programming wait () and waitpid ()

Source: Internet
Author: User
Tags socket error
This article describes the differences and relationships between wait and waitpid. To avoid zombie processes, the main process needs to wait for the sub-process to return no matter when we create a sub-process, so that the sub-process can be cleaned up. Therefore, after the SIGCHLD signal processing client is disconnected, a large number of zombie processes exist on the server side. This is because after the child process of the server is terminated, the SIGCHLD signal is sent to the parent process, which is ignored by default. To avoid zombie processes, the main process needs to wait for the sub-process to return no matter when we create a sub-process, so that the sub-process can be cleaned up. Therefore, we add the SIGCHLD signal processing function to the server program.

Copy codeThe code is as follows:
# Include
# Include
# Include
# Include
# Include
# Include
# Include
# Include
# Include
# Define SERV_PORT 1113
# Define LISTENQ 32
# Define MAXLINE 1024
/*** Connection handler ***/
Void str_echo (int fd );
Void
Sig_chld (int signo)
{
Pid_t pid;
Int stat;
Pid = wait (& stat); // Obtain the sub-process ID
Printf ("child % d terminated \ n", pid );
Return;
}
Int
Main (int argc, char * argv []) {
Int listenfd, connfd;
Pid_t childpid;
Socklen_t clilen;
Struct sockaddr_in servaddr;
Struct sockaddr_in cliaddr;
// Struct sockaddr_in servaddr;
// Struct sockaddr_in cliaddr;
If (listenfd = socket (AF_INET, SOCK_STREAM, 0) =-1 ){
Fprintf (stderr, "Socket error: % s \ n \ a", strerror (errno ));
Exit (1 );
}
/* Fill in the sockaddr structure on the server */
Bzero (& servaddr, sizeof (servaddr ));
Servaddr. sin_family = AF_INET;
Servaddr. sin_addr.s_addr = htonl (INADDR_ANY );
Servaddr. sin_port = htons (SERV_PORT );
Signal (SIGCHLD, sig_chld); // process the SIGCHLD signal
/* Bind the listenfd descriptor */
If (bind (listenfd, (struct sockaddr *) (& servaddr), sizeof (struct sockaddr) =-1 ){
Fprintf (stderr, "Bind error: % s \ n \ a", strerror (errno ));
Exit (1 );
}
/* Listen to the listenfd descriptor */
If (listen (listenfd, 5) =-1 ){
Fprintf (stderr, "Listen error: % s \ n \ a", strerror (errno ));
Exit (1 );
}
For (;;){
Clilen = sizeof (cliaddr );
/* Server blocking until the client program establishes a connection */
If (connfd = accept (listenfd, (struct sockaddr *) (& cliaddr), & clilen) <0 ){
/* When a sub-process is terminated, the signal processing function sig_chld is executed,
When this function returns, the accept system call may return an EINTR error,
Some kernels automatically restart interrupted system calls. for ease of transplantation, EINTR processing will be considered */
If (errno = EINTR)
Continue;
Fprintf (stderr, "Accept error: % s \ n \ a", strerror (errno ));
Exit (1 );
}
// After a client establishes a connection
If (childpid = fork () = 0) {/* sub-process */
Close (listenfd);/* close the listening socket */
Str_echo (connfd);/* process the client request */
Exit (0 );
}
Close (connfd);/* the parent process closes the connection socket and waits for other connections to arrive */
}
}
Void str_echo (int sockfd ){
Ssize_t n;
Char buf [MAXLINE];
Again:
While (n = read (sockfd, buf, MAXLINE)> 0)
Write (sockfd, buf, n );
If (n <0 & errno = EINTR) // interrupted, reentrant
Goto again;
Else if (n <0) {// error
Fprintf (stderr, "read error: % s \ n \ a", strerror (errno ));
Exit (1 );
}
}

After the code is modified, when the client is disconnected, the parent process on the server receives the SIGCHLD signal from the child process and runs the sig_chld function. after the child process is cleared, no zombie process will appear. After a client is disconnected, the server outputs the following information:
Child 12306 terminated
Wait and waitpid
In the sig_chld function in the above program, we use wait () to clear the terminated child process. There is also a similar function wait_pid. Let's take a look at the two function prototypes:
Pid_t wait (int * status );
Pid_t waitpid (pid_t pid, int * status, int options );
Official description: All of these system CILS are used to wait for state changes in a child of the calling process, and obtain information about the child whose state has changed. A state change is considered to be: the child ter minated; the child was stopped by a signal; or the child was resumed by a signal. in the case of a terminated child, please Ming a wait allows the system to release the resources associated with the child; if a wait is not completed MED, then the terminated child remains in a "zombie" state (see NOTES below ).
Differences and relationships between wait and waitpid:
The wait () system call suspends execution of the calling process until one of its children terminates. The call wait (& status) is equivalent:
Waitpid (-1, & status, 0 );
The waitpid () system call suspends execution of the calling process until a child specified by pid argument has changed state. by default, waitpid () waits only for terminated children, but this behavior is modifiable via the options argument, as described below.
That is to say, the wait () system call suspends the calling process until any of its sub-processes terminate. The effect of calling wait (& status) is the same as that of calling waitpid (-1, & status, 0.
Waitpid () suspends the calling process until the status of the process specified by the pid parameter changes. by default, waitpid () only waits for the termination status of the sub-process. If necessary, you can set the options value to handle non-terminated conditions. For example:
The value of options is an OR of zero or more of the following constants:
WNOHANG return immediately if no child has exited.
WUNTRACED also return if a child has stopped (but not traced via ptrace (2). Status for traced children which have stopped is provided even if this option is not specified.
WCONTINUED (since Linux 2.6.10) also return if a stopped child has been resumed by delivery of SIGCONT.
Wait until it is not terminated.
Now let's look at the difference between wait () and waitpid () through the instance.
By modifying the client program, you can create five sockets at a time in the client program to connect to the server. The status is shown in (with code ):





Copy codeThe code is as follows:
# Include
# Include
# Include
# Include
# Include
# Include
# Include
# Include
# Include
# Define SERV_PORT 1113
# Define MAXLINE 1024
Void str_cli (FILE * fp, int sockfd );
Int
Main (int argc, char ** argv)
{
Int I, sockfd [5];
Struct sockaddr_in servaddr;
If (argc! = 2 ){
Fprintf (stderr, "usage: tcpcli \ N \ ");
Exit (0 );
}
For (I = 0; I <5; ++ I) {// establish five connections with the server so that the server creates five sub-processes
If (sockfd [I] = socket (AF_INET, SOCK_STREAM, 0) =-1 ){
Fprintf (stderr, "Socket error: % s \ n \ a", strerror (errno ));
Exit (1 );
}

/* Fill in the information of the client program on the server */
Bzero (& servaddr, sizeof (servaddr ));
Servaddr. sin_family = AF_INET;
Servaddr. sin_port = htons (SERV_PORT );
If (inet_ton (AF_INET, argv [1], & servaddr. sin_addr) <= 0 ){
Fprintf (stderr, "inet_ton Error: % s \ a \ n", strerror (errno ));
Exit (1 );
}
/* The client program initiates a connection request */
If (connect (sockfd [I], (struct sockaddr *) (& servaddr), sizeof (struct sockaddr) =-1 ){
Fprintf (stderr, "connect Error: % s \ a \ n", strerror (errno ));
Exit (1 );
}
}
Str_cli (stdin, sockfd [0]);/* only use the first socket to interact with the server */
Exit (0 );
}
Void
Str_cli (FILE * fp, int sockfd)
{
Int nbytes = 0;
Char sendline [MAXLINE], recvline [MAXLINE];
While (fgets (sendline, MAXLINE, fp )! = NULL) {// read a row from the standard input
Write (sockfd, sendline, strlen (sendline); // send this row to the server
If (nbytes = read (sockfd, recvline, MAXLINE) = 0) {// read data from the server from sockfd
Fprintf (stderr, "str_cli: server terminated prematurely \ n ");
Exit (1 );
}
Recvline [nbytes] = '\ 0 ';
Fputs (recvline, stdout );
}
}

When the customer terminates, the opening descriptor is automatically closed by the kernel. Therefore, five connections occur at the same time, which is equivalent to triggering five FIN requests to the server at the same time, this causes the five server sub-processes to terminate at the same time, resulting in the delivery of five SIGCHLD signals to the server's parent process almost simultaneously, as shown below:

That is to say, at almost the same time, five SIGCHLD signals are delivered to the parent process, which will lead to the appearance of the Zombie process. Unix generally does not queue signals, which leads to five SIGCHLD submissions. Only one sig_chld function is executed, and the remaining four sub-processes become zombie processes. In this case, the correct method is to call waitpid () instead of wait ().
Therefore, the signal processing function in our final server code is changed to the following:

Copy codeThe code is as follows:
Void
Sig_chld (int signo)
{
Pid_t pid;
Int stat;
While (pid = waitpid (-1, & stat, WNOHANG)> 0)
Printf ("child % d terminated \ n", pid );
Return;
}

So far, we have solved three possible situations in network programming:
1. when the dispatch process occurs, the SIGCHLD signal must be captured. Code snippet: signal (SIGCHLD, sig_chld );
2. when the signal is captured, the interrupted system call must be processed. Code snippet: if (errno = EINTR) continue;
3. the SIGCHLD signal processing function must be correctly written to prevent zombie processes. Code snippet: while (pid = waitpid (-1, & stat, WNOHANG)> 0)
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.