前一节讲述了最基本的客户端-服务器,本节讲述如何解决僵尸子进程回收问题。
我们知道,子进程退出后,父进程可以通过wait获取子进程的状态,而后清楚子进程剩余内核空间,从而避免僵尸进程造成的内存泄露。
对于wait函数,一般使用wait或waitpid两个函数,其区别主要是,waitpid可以指定pid获取子进程状态,并且waitpid具有非阻塞进程选项。我们这里使用waitpid函数。
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *statloc, int option);
若成功,返回子进程id。出错返回0或-1。
服务器代码如下:(客户端复用第一版)
1 #include <sys/socket.h> 2 #include <sys/wait.h> 3 #include <netinet/in.h> 4 #include <stdio.h> 5 #include <string.h> 6 #include <unistd.h> 7 #include <stdlib.h> 8 #include <errno.h> 9 10 #define MAXLINE 1024 11 12 extern int errno; 13 14 void str_echo(int); 15 16 void sig_chld(int); 17 18 int main() { 19 int sockfd; 20 sockfd = socket(AF_INET, SOCK_STREAM, 0); 21 22 struct sockaddr_in servaddr, cliaddr; 23 bzero(&servaddr, sizeof(servaddr)); 24 servaddr.sin_family = AF_INET; 25 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 26 servaddr.sin_port = htons(7070); 27 bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); 28 listen(sockfd, 1024); 29 signal(SIGCHLD, sig_chld); 30 31 for (;;) { 32 int connfd, childPid; 33 socklen_t len = sizeof(cliaddr); 34 connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &len); 35 36 if ((childPid = fork()) == 0) { 37 close(sockfd); 38 printf("connected with client. "); 39 str_echo(connfd); 40 exit(0); 41 } 42 } 43 44 printf("server end! "); 45 return 0; 46 } 47 48 void str_echo(int sockfd) { 49 ssize_t n; 50 char buf[MAXLINE]; 51 52 again: 53 54 while ((n = read(sockfd, buf, MAXLINE)) > 0) { 55 printf("n:%ld ", n); 56 write(sockfd, buf, n); 57 bzero(buf, MAXLINE); 58 59 if (n < 0 && errno == EINTR) { 60 goto again; 61 } else if (n < 0) { 62 printf("str_echo:read error "); 63 } 64 } 65 } 66 67 void sig_chld(int signo) { 68 pid_t pid; 69 int stat; 70 while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) { 71 printf("child %d terminated ", pid); 72 } 73 return; 74 }