我的文件服务器程序已经停止僵尸进程

我有一个基于套接字的文件服务器程序,它使用fork为多个客户端提供服务。 运行这个服务器程序后,当我做ps -ef我得到了很多我的服务器的<defunct>subprocess。 我该如何解决这个问题? 以下是我的服务器代码。 提前致谢。

 /* This is the server for a very simple file transfer service. This is a "concurrent server" that can handle requests from multiple simultaneous clients. For each client: - get file name and check if it exists - send size of file to client - send file to client, a block at a time - close connection with client */ #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <time.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <math.h> #define MY_PORT_ID 60841 #define MAXLINE 256 #define MAXSIZE 262144 #define ACK 2 #define NACK 3 #define REQUESTFILE 100 #define COMMANDNOTSUPPORTED 150 #define COMMANDSUPPORTED 160 #define BADFILENAME 200 #define FILENAMEOK 400 int writen(int sd,char *ptr,int size); int readn(int sd,char *ptr,int size); main() { int sockid, newsd, pid, clilen; struct sockaddr_in my_addr, client_addr; long int milsecS, milsecE; //printf("server: creating socket\n"); if ((sockid = socket(AF_INET,SOCK_STREAM,0)) < 0) {printf("server: socket error : %d\n", errno); exit(0); } //printf("server: binding my local socket\n"); bzero((char *) &my_addr,sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_port = htons(MY_PORT_ID); my_addr.sin_addr.s_addr = htons(INADDR_ANY); if (bind(sockid ,(struct sockaddr *) &my_addr,sizeof(my_addr)) < 0) {printf("server: bind error :%d\n", errno); exit(0); } //printf("server: starting listen \n"); if (listen(sockid,5) < 0) { printf("server: listen error :%d\n",errno);exit(0);} while(1==1) { /* ACCEPT A CONNECTION AND THEN CREATE A CHILD TO DO THE WORK */ /* LOOP BACK AND WAIT FOR ANOTHER CONNECTION */ //printf("server: starting accept\n"); clilen=sizeof((struct sockaddr *) &client_addr); if ((newsd = accept(sockid ,(struct sockaddr *) &client_addr, &clilen)) < 0) {printf("server: accept error :%d\n", errno); exit(0); } /* START TIME */ struct timeval tvBegin, tvEnd; struct timeval *tvB, *tvE; gettimeofday(&tvBegin, NULL); tvB=&tvBegin; milsecS=(tvB->tv_sec)*1000000L-tvB->tv_usec; //printf("Start in milliseconds: %ld\n", milsecS); //printf("server: return from accept, socket for this ftp: %d\n", //newsd); if ( (pid=fork()) == 0) { /* CHILD PROC STARTS HERE. IT WILL DO ACTUAL FILE TRANSFER */ close(sockid); /* child shouldn't do an accept */ doftp(newsd, milsecS); close (newsd); exit(0); /* child all done with work */ } /* PARENT CONTINUES BELOW HERE */ close(newsd); /* parent all done with client, only child */ } /* will communicate with that client from now on */ } /* CHILD PROCEDURE, WHICH ACTUALLY DOES THE FILE TRANSFER */ doftp(int newsd, long int milsecS) { int i,fsize,fd,msg_ok,fail,fail1,req,c,ack; int no_read ,num_blks , num_blks1,num_last_blk,num_last_blk1,tmp; char fname[MAXLINE]; char out_buf[MAXSIZE]; FILE *fp; long int sMilsec, milsecE; sMilsec=milsecS; float schDelay; //To calculate schedule delay char schDelayS[30]; struct timeval tvEnd; struct timeval *tvE; no_read = 0; num_blks = 0; num_last_blk = 0; /* START SERVICING THE CLIENT */ /* get command code from client.*/ /* only one supported command: 100 - get a file */ req = 0; if((readn(newsd,(char *)&req,sizeof(req))) < 0) {printf("server: read error %d\n",errno);exit(0);} req = ntohs(req); //printf("Req is: %d\n", req); //printf("server: client request code is: %d\n",req); if (req!=REQUESTFILE) { printf("server: unsupported operation. goodbye\n"); /* reply to client: command not OK (code: 150) */ msg_ok = COMMANDNOTSUPPORTED; msg_ok = htons(msg_ok); if((writen(newsd,(char *)&msg_ok,sizeof(msg_ok))) < 0) {printf("server: write error :%d\n",errno);exit(0);} exit(0); } /* reply to client: command OK (code: 160) */ msg_ok = COMMANDSUPPORTED; msg_ok = htons(msg_ok); if((writen(newsd,(char *)&msg_ok,sizeof(msg_ok))) < 0) {printf("server: write error :%d\n",errno);exit(0);} fail = FILENAMEOK; if((read(newsd,fname,MAXLINE)) < 0) { printf("server: filename read error :%d\n",errno); fail = BADFILENAME ; } /* IF SERVER CANT OPEN FILE THEN INFORM CLIENT OF THIS AND TERMINATE */ //printf("File name is: %s\n", fname); if((fp = fopen(fname,"r")) == NULL) /*cant open file*/ fail = BADFILENAME; tmp = htons(fail); if((writen(newsd,(char *)&tmp,sizeof(tmp))) < 0) {printf("server: write error :%d\n",errno);exit(0); } if(fail == BADFILENAME) {printf("server cant open file\n"); close(newsd);exit(0);} //printf("server: filename is %s\n",fname); req = 0; if ((readn(newsd,(char *)&req,sizeof(req))) < 0) {printf("server: read error :%d\n",errno);exit(0);} //printf("server: start transfer command, %d, received\n", ntohs(req)); /*SERVER GETS FILESIZE AND CALCULATES THE NUMBER OF BLOCKS OF SIZE = MAXSIZE IT WILL TAKE TO TRANSFER THE FILE. ALSO CALCULATE NUMBER OF BYTES IN THE LAST PARTIALLY FILLED BLOCK IF ANY. SEND THIS INFO TO CLIENT, RECEIVING ACKS */ //printf("server: starting transfer\n"); fsize = 0;ack = 0; while ((c = getc(fp)) != EOF) {fsize++;} num_blks = fsize / MAXSIZE; num_blks1 = htons(num_blks); num_last_blk = fsize % MAXSIZE; num_last_blk1 = htons(num_last_blk); if((writen(newsd,(char *)&num_blks1,sizeof(num_blks1))) < 0) {printf("server: write error :%d\n",errno);exit(0);} //printf("server: told client there are %d blocks\n", num_blks); if((readn(newsd,(char *)&ack,sizeof(ack))) < 0) {printf("server: ack read error :%d\n",errno);exit(0); } if (ntohs(ack) != ACK) { printf("client: ACK not received on file size\n"); exit(0); } if((writen(newsd,(char *)&num_last_blk1,sizeof(num_last_blk1))) < 0) {printf("server: write error :%d\n",errno);exit(0);} //printf("server: told client %d bytes in last block\n", num_last_blk); if((readn(newsd,(char *)&ack,sizeof(ack))) < 0) {printf("server: ack read error :%d\n",errno);exit(0); } if (ntohs(ack) != ACK) { printf("server: ACK not received on file size\n"); exit(0); } rewind(fp); /* ACTUAL FILE TRANSFER STARTS BLOCK BY BLOCK*/ gettimeofday(&tvEnd, NULL); tvE=&tvEnd; milsecE=(tvE->tv_sec)*1000000L-tvE->tv_usec; //printf("End time in milliseconds: %ld\n", milsecE); schDelay=fabs((milsecE-sMilsec)/1000); htonl(schDelay); //printf("Delay is: %f\n", schDelay); for(i= 0; i < num_blks; i ++) { no_read = fread(out_buf,sizeof(char),MAXSIZE,fp); if (no_read == 0) {printf("server: file read error\n");exit(0);} if (no_read != MAXSIZE) {printf("server: file read error : no_read is less\n");exit(0);} if((writen(newsd,out_buf,MAXSIZE)) < 0) {printf("server: error sending block:%d\n",errno);exit(0);} if((readn(newsd,(char *)&ack,sizeof(ack))) < 0) {printf("server: ack read error :%d\n",errno);exit(0);} if (ntohs(ack) != ACK) { printf("server: ACK not received for block %d\n",i); exit(0); } // printf(" %d...",i); } if (num_last_blk > 0) { //printf("%d\n",num_blks); no_read = fread(out_buf,sizeof(char),num_last_blk,fp); if (no_read == 0) {printf("server: file read error\n");exit(0);} if (no_read != num_last_blk) {printf("server: file read error : no_read is less 2\n");exit(0);} if((writen(newsd,out_buf,num_last_blk)) < 0) {printf("server: file transfer error %d\n",errno);exit(0);} if((readn(newsd,(char *)&ack,sizeof(ack))) < 0) {printf("server: ack read error %d\n",errno);exit(0);} if (ntohs(ack) != ACK) { printf("server: ACK not received last block\n"); exit(0); } } // else printf("\n"); /* FILE TRANSFER ENDS */ //printf("server: FILE TRANSFER COMPLETE on socket %d\n",newsd); fclose(fp); close(newsd); } /* TO TAKE CARE OF THE POSSIBILITY OF BUFFER LIMMITS IN THE KERNEL FOR THE SOCKET BEING REACHED (WHICH MAY CAUSE READ OR WRITE TO RETURN FEWER CHARACTERS THAN REQUESTED), WE USE THE FOLLOWING TWO FUNCTIONS */ int readn(int sd,char *ptr,int size) { int no_left,no_read; no_left = size; while (no_left > 0) { no_read = read(sd,ptr,no_left); if(no_read <0) return(no_read); if (no_read == 0) break; no_left -= no_read; ptr += no_read; } return(size - no_left); } int writen(int sd,char *ptr,int size) { int no_left,no_written; no_left = size; while (no_left > 0) { no_written = write(sd,ptr,no_left); if(no_written <=0) return(no_written); no_left -= no_written; ptr += no_written; } return(size - no_left); } 

成为僵尸进程:

当一个子进程完成它的工作,并终止,父进程存在并没有调用wait(),然后它成为僵尸。

如何清除僵尸:

  • 让父进程在子进程上调用wait() ,然后内核会正确清除终止进程,而不是让它变成僵尸。
  • 或者终止父进程,则init进程将成为其父进程,并在终止时正确终止并清除子进程。

在你的情况下:

所以,可能你用fork()创建了子进程,而没有为父进程创建wait() ,并且父进程没有终止。 于是,那么子进程就完成了它的工作,就成了僵尸。

您可以尝试在子进程上使用wait()


对你的问题发表评论

在哪里添加wait()

  • 首先你需要根据你的逻辑来决定使用哪一个等待,还有wait() / waitpid() / waitid() ,其中wait()是最简单的,只有最少的控制。 如果可以在等待时阻塞父进程,那么wait()就可以,否则需要waitpid()waitid() 。 有关每个功能的详细信息,请参阅man页。
  • 在决定了哪个等待使用之后,通常在fork() ,在main()结束之前添加wait函数,可能会用一个循环来处理多个子进程,并检查返回的wait值来决定下一步。

书籍: man页相当不错,但是Linux编程书(如TLPI )可能会给予很大的帮助。


代码示例

这是一个简单的测试程序,我在学习linux编程的时候写的,它创建了多个子进程并等待它们。

wait_test.c

 // wait() test #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <sys/wait.h> int wait_test() { pid_t cpid; int i, max_child = 5; // create child process, for(i=1; i<=max_child; i++) { switch(cpid=fork()) { case -1: // failed printf("error while fork()\n"); exit(errno); case 0: // success, child process goes here sleep(i); printf("child [%d], going to exit\n",(int)getpid()); _exit(EXIT_SUCCESS); break; default: // success, parent process goes here printf("parent [%d], child created [%d]\n", (int)getpid(), (int)cpid); break; } } // wait child to terminate int status; while(1) { if((cpid = wait(&status)) == -1) { if(errno == ECHILD) { printf("no more child\n"); exit(EXIT_SUCCESS); } else { printf("error while wait()\n"); exit(-1); } } printf("parent [%d], child [%d] exit with [%d]\n", (int)getpid(), (int)cpid, status); } return 0; } int main(int argc, char *argv[]) { wait_test(); return 0; } 

在linux上编译: gcc -Wall wait_test.c

执行: ./a.out

它给出了一个基本的观点,看看孩子的过程和等待是如何工作的


@Update:SIGCHLD信号

正如@alk评论的那样,使用SIGCHLD信号处理程序是调用wait的另一种方式,可能比较简单,但似乎有一些技巧,而且容易出错。

以下是基于我有限的使用SIGCHLD信号的知识的一些描述,只是把它作为一个提示,而不是确切的样本。

如何使用SIGCHID signal

默认情况下, SIGCHID信号被进程忽略,但可以在其上注册一个处理程序。

由于标准信号被暂时阻塞而未被排队,意味着多个信号可能被发送,并且该过程可能只发生一次,因此无论何时接收到该信号,都应该使用带有WNOHANG选项的waitpid()循环来处理所有终止的孩子处理。

处理函数可能如下所示:

 void sigchld_handler() { int local_errno = errno; // backup errno, while (waitpid(-1, NULL, WNOHANG) > 0) continue; errno = local_errno; // restore errno, } 

然后在父进程上注册处理程序,在调用folk()之前,通过:

 if(signal(SIGCHLD, sigchld_handler) == SIG_ERR) { printf("error signal()"); return errno; } 

然后,当一个子进程终止的时候,父进程会收到一个SIGCHLD信号,并调用处理器函数,等待子进程,从而帮助清除子进程而不是让它成为僵尸。