本文是UNP第五章学习笔记
一,wait和waitpid函数
#include<sys/wait.h>
pid_t wait(int* statloc);
pid_t waitpid(pid_t pid,int statloc,int options);
这两个函数用来等待子进程结束. 参数statloc返回进程的终止状态,返回值为终止的子进程id
waitpid是wait的功能加强版,增加了一些有用的功能,因此也更为常用:
- pid函数指定了具体等待哪个子进程id, -1表示第一个结束的子进程
- options可以指定附加选项,常用的是WNOHANG,当没有已终止子进程时不阻塞
- 因为linux不对信号量进行队列处理,使用wait可能丢失信号量,推荐用waitpid+WNOHNG处理
二, 捕获信号量
网络编程用signal函数捕获一些信号量进行处理,
typedef void(*sig_t) ( int );
sig_t signal(int signum,sig_t handler);
signum即信号量,如SIGPIPE等, handler为信号处理函数,格式为
void handler(int signum);
handler还可以是
SIG_IGN(忽略)或 SIG_DFL(默认效果)
三,EINTR错误
如果你的程序正在捕获信号, 那么一些阻塞函数(例如read,write,accept等)可能会因捕获并处理信号导致返回一个错误值,同时将errno设置成EINTR, 通常的处理办法是重新执行,例如:
Again:
int newfd = accept(fd,NULL,NULL);
if(newfd<0 && errno==EINTR)
goto Again;
......
四,SIGCHLD信号量
当子进程退出时,父进程会收到这个信号,默认效果是忽略. 这样的话子进程就会成为僵尸进程,多了的话可能导致占满进程号导致无法fork新的进程. 所以一般会处理掉.:
1是给SIGCHLD信号指定一个handler函数,在函数中使用waitpid结束子进程
void sig_chld(int signum)
{
pid_t pid;
while( (pid=waitpid(-1,NULL,WNOHANG))>0 )
printf("process %d close\n",pid);
}
2是干脆直接绑定SIGCHLD为SIG_IGN
signal(SIGCHLD,SIG_IGN);
五,SIGPIPE信号量
这个信号量是当对方关闭了连接,而你第二次对这个连接进行写操作时产生的,(第一次写会返回0). 正确的流程是不会产生这一信号的,因为第一次write返回0以后你就应该关闭这个连接.这个信号会终止程序,而许多时候我们应该忽略它
signal(SIGPIPE,SIG_IGN);
当你使用mysql等应用并产生大量连接时,最好忽略它,以免程序莫名其妙地退出
六,一个简单的多进程并发服务器程序示例
省略了一些出错处理
void sig_chld(int signo)
{
pid_t pid;
while((pid=waitpid(-1,NULL,WNOHANG))>0)
{
printf("child %d terminated\n",pid);
}
return ;
}
int main()
{
int ret;
int listenfd = socket(AF_INET,SOCK_STREAM,0);
sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(8003);
addr.sin_addr.s_addr=htonl(INADDR_ANY);
printf("read ret =0,close socket\n");
printf("read from newfd failed! ret=%d,%s\n",ret,strerror(errno));
ret = bind(listenfd,(sockaddr*)&addr,sizeof(sockaddr_in));
ret = listen(listenfd,5);
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, NULL, sizeof(int));
int newfd;
sockaddr_in client_addr;
socklen_t client_addr_len;
char* cli_ip;
int cli_port;
char recv_buf[100];
char send_buf[100];
signal(SIGCHLD,sig_chld);
while ( (newfd = accept(listenfd,(sockaddr*)&client_addr,&client_addr_len)) >0 )
{
pid=fork();
if(pid==0)
{
close(listenfd);
while(1)
{
ret = read(newfd,recv_buf,sizeof(recv_buf));
if(ret<0)
{
printf("read from newfd failed! ret=%d,%s\n",ret,strerror(errno));
break;
}else if(ret == 0)
{
close(newfd);
break;
}else{
recv_buf[ret]='\0';
printf("recv %d bytes:[%s]\n",ret,recv_buf);
strcpy(send_buf,"world");
ret = write(newfd,send_buf,strlen(send_buf));
printf("send %d bytes,[%s]\n",ret,send_buf);
}
}
close(newfd);
}
close(newfd);
}
return 0;
}
void sig_chld(int signum)
{
pid_t pid;
while( (pid=waitpid(-1,NULL,WNOHANG))>0 )
printf("process %d close\n",pid);
}
void sig_chld(int signum)
{
pid_t pid;
while( (pid=waitpid(-1,NULL,WNOHANG))>0 )
printf("process %d close\n",pid);
}