SIGCHID信号
待解决的问题
- 当子进程结束时,会给父进程发送一个SIGCHID信号,而我们之前的服务器程序默认是不处理的。此时子进程就会是半死不死的僵死程序,会占用系统进程资源。因此我们需要建立一个信号处理程序,每当发生这个SIGCHID信号时,都对其进行处理,消灭它。
- 系统自带的有wait和waitpid程序可以处理上述问题,但是两者有区别。前者可能不能处理多子进程同时结束的情况
- 信号处理函数返回时,如果是慢系统可能会返回一个EINT错误,如何对其进行处理。
信号处理函数
POSIX规定:
1. 信号处理函数安装(存在并且调用)一次,就一直安装着。(类似中断处理),因此在子进程创建之前建立就可以了。
2. 信号处理函数运行时,正在递交的信号全部被阻塞,同时可以阻塞其他信号(sigaction
函数中的sa_mask
,空集则表示不阻塞其他信号。)
wait和waitpid
wait函数比较简单,是后者的特殊情况。
进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
因此,如果放在while循环里面,那么一旦存在正在运行的子进程就会一直阻塞,导致无法退出信号处理函数。
后者可以指定选项参数,要求不阻塞就可以。
#include "unp.h"
int
main(int argc, char **argv)
{
int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
void sig_chld(int);
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
//新增的部分
Signal(SIGCHLD, sig_chld); /* must call waitpid() */
for ( ; ; ) {
clilen = sizeof(cliaddr);
//新增的部分
if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {
if (errno == EINTR)
continue; /* back to for() */
else
err_sys("accept error");
}
if ( (childpid = Fork()) == 0) { /* child process */
Close(listenfd); /* close listening socket */
str_echo(connfd); /* process the request */
exit(0);
}
Close(connfd); /* parent closes connected socket */
}
}
SIGPIPE信号
待解决的问题
1.当一个进程想某个已经收到RST的套接字执行写操作时,内核会向进程发送一个SIGPIPE信号。
如果不处理,则客户端进程难以知道是由于连接已经断开,而是认识是异常终止。与实际不符合。
处理方式
将写操作分两步,第一步引发RST,然后再写剩余的东西,可以收到一个SIGPIPE信号。
如果不想终止进程可以忽略。
struct sigaction act;
act.sa_handler = SIG_IGN;
if (sigaction(SIGPIPE, &act, NULL) == 0)
{
LOG("SIGPIPE ignore");
}