tcp客户/服务器回射程序之二-----处理SIGCHILD信号

来源:互联网 发布:征途单机版数据库修改 编辑:程序博客网 时间:2024/05/17 05:17

tcp客户/服务器回射程序之二-----处理SIGCHLD信号

先说一下关于信号的基本知识:

信号(signal)就是通知某个进程发生了某个事件,有时也称为软件中断。信号通常是异步发生的,也就是说进程预先不知道信号准确发生的时间。

信号可以由一个进程发给另一个进程(或自身),也可以由内核发给某个进程。SIGCHILD信号就是由内核在任何一个进程终止时发给它的父进程的一个信号。

每个信号都有一个与之关联的处置(disposition),也称为行为(action).我们通过调用sigaction函数来设定一个信号的处置,并有3种选择。

1).可以提供一个函数,它将在特定信号发生的任何时刻被调用。这样的函数称为信号处理函数,这种行为称为捕获信号。有两个信号不能被捕获,它们是SIGKILL和SIGSTOP.信号处理函数由信号值这个单一的参数来调用,且没有返回值。

2).可以把某个信号的处置设定为SIG_IGN来忽略它。SIG_KILL和SIG_STOP这两个信号不能被忽略。

3).可以把某个信号的处置设定为SIG_DFL来启用它的缺省处置。缺省处置通常是收到信号后终止进程,其中某些信号还在当前工作目录产生一个进程的核心映像。另有个别信号的缺省处置是忽略。

僵死进程:

设置僵死状态的目的是维护子进程的信息,以便父进程在以后某个时候获取。这些信息包括子进程的进程id、终止状态以及资源利用信息。如果一个进程终止,而该进程有子进程处于僵死状态,那么它的所有僵死子进程的父进程id被重置为1(init进程)。继承这些子进程的init进程将清理它们。

处理僵死进程:僵死进程占用内核中的空间,最终可能导致我们耗尽进程资源。使用wait或者waitpid来处理僵死进程。但是使用wait并不足以防止出现僵死进程。因为unix的信号是不排队的,若几个SIG_CHILD信号同时传递,则信号处理函数执行的次数不一定。正确的解决办法是调用waitpid而不是wait。我们在一个循环内调用waitpid,以获取所有终止子进程的状态。我们必须指定WNOHANG选项,它告知waitpid在有尚未终止的子进程在运行时不阻塞。

总结:在网络编程中可能遇到的3种情况:

1).当fork子进程时,必须捕获SIG_CHLD信号。

2).当捕获信号时,必须处理被中断的系统调用。

3).SIG_CHLD的信号处理函数必须正确编写,应使用waitpid函数以免留下僵死进程。

改进后的代码为:

客户端程序:

#include <sys/socket.h>#include <unistd.h>#include <string.h>#include <stdlib.h>#include <stdio.h>#include <netinet/in.h>#include <errno.h>#include <sys/types.h>#include "readline.c"#define MAXLINE 500void str_cli(FILE*, int);int main(char argc, char ** argv) {int i,connfd[5];struct sockaddr_in servaddr;if(argc != 2)perror("usage:tcpcli<IPaddress>");for(i=0; i<5; ++i) {if((connfd[i] = socket(AF_INET, SOCK_STREAM, 0)) < 0)perror("socket error");bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(9999);inet_pton(AF_INET, argv[1], &servaddr.sin_addr.s_addr);if((connect(connfd[i], (struct sockaddr*)&servaddr, sizeof(servaddr))) < 0)perror("connect error");}str_cli(stdin, connfd[0]);exit(0);}void str_cli(FILE* fp, int connfd) {char sendline[MAXLINE], recvline[MAXLINE];//printf("str_cli has been used.\n");while((fgets(sendline, MAXLINE, fp)) != NULL) {//printf("ok\n");write(connfd, sendline, strlen(sendline));//printf("oo=%s\n", sendline);if((readline(connfd, recvline, MAXLINE)) == 0) {//printf("ooo\n");perror("str_cli:server terminated prematurely");}//printf("jjjjj\n");fputs(recvline, stdout);}}

服务器程序:

#include <sys/socket.h>#include <stdlib.h>#include <stdio.h>#include <sys/types.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <netinet/in.h>#include <sys/wait.h>//#include "unp.h"void str_echo(int);void sig_chld(int);int main(char argc, char ** argv) {int listenfd, connfd;pid_t childpid;socklen_t clilen;struct sockaddr_in cliaddr, servaddr;if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)perror("socket error");bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(9999);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);if((bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr))) < 0)perror("bind error");if(listen(listenfd, 5) < 0)perror("listen error");signal(SIGCHLD, sig_chld);printf("listenning....\n");for(;;) {//printf("....\n");clilen = sizeof(cliaddr);//printf("o\n");//connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen);if((connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen)) < 0) {if(errno == EINTR) continue;elseperror("accept error");}//printf("ooo\n");if((childpid = fork()) == 0) {close(listenfd);str_echo(connfd);//printf("done.\n");exit(0);}close(connfd);}exit(0);}void sig_chld(int signo) {pid_t pid;int stat;//printf("signal ...\n");while((pid=waitpid(-1, &stat, WNOHANG)) > 0)printf("child %d terminated.\n", pid);return;}void str_echo(int connfd) {int n;char buff[500];printf("str_echo has been used.\n");again:while((n = read(connfd, buff, 500)) > 0) { //printf("writing.....\n");write(connfd, buff, n);}//printf("here..\n");if(n < 0 && errno == EINTR)goto again;else if(n < 0)perror("str_echo: read error");}


原创粉丝点击