这篇博客记录一下我在编写一个简单的多进程回声服务器的时候出现的问题。
这个问题就在于忽略了几个有关于信号处理函数的基本常识:
- 用通俗的话讲信号注册函数(signal、sigaction)的功能:进程告诉操作系统,当以后收到向信号注册函数传入的信号时,你帮我调用一下信号处理函数。
- 当该进程在之后收到指定的信号之后,操作系统就会帮助进程调用指定的信号处理函数。但是,如果该进程处于阻塞状态,那么操作系统会强制唤醒进程!并且不会再陷入之前的阻塞!
下面是我所写的代码中的一个片段(C和C++混编出来的屎,别喷):
void start()
{
// 开始接受客户端请求
struct sigaction act;
act.sa_handler = wait_child_proc;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGCHLD,&act,0);
sockaddr_in client;
socklen_t len = sizeof(client);
while(true)
{
int sockfd = accept(_listenfd,(sockaddr *)&client,&len);
if(sockfd == -1)
{
printf("accept error!\n");
continue;
}
std::cout << "有新的客户端..." << std::endl;
pid_t id = fork();
if(id == 0)
{
close(_listenfd);//子进程不负责监听业务
int recv_len = 0;
char buffer[1024] = {0};
while((recv_len = read(sockfd,buffer,sizeof(buffer))) != 0)
{
std::cout << "recv_len = " << recv_len << std::endl;
write(sockfd,buffer,recv_len);
}
close(sockfd);
std::cout << "子进程处理完业务!退出!" << getpid() << std::endl;
exit(0);
}
else if(id > 0)
{
close(sockfd);// 父进程不处理业务
}
else
{
std::cout << "fork error!" << std::endl;
exit(-1);
}
}
}
这段代码会有两个地方陷入阻塞:一是父进程调用accept时,二是子进程调用read时。而子进程并没有调用信号注册函数,因此子进程与本篇文章研究的问题无关。
这段代码的意思大概是这样:调用start()后,调用sigaction()注册了一个SIGCHLD信号,然后继续往下走直到accept()阻塞。如果accept()返回了一个正确的套接字,那么主进程就创建一个子进程去处理I/O。子进程当中的read()如果返回值为0,就说明连接已经断开了,此时子进程就会退出。同时,在子进程处理I/O的时候,父进程已经在accept()处阻塞了,此时如果子进程处理的I/O连接断开,子进程就会退出,父进程就会收到SIGCHLD信号,然而此时的父进程正处于阻塞状态,所以操作系统会强制唤醒父进程以便调用信号处理函数,而父进程的accept()没有收到任何可用连接并且又从accept()处唤醒,因此accept()的返回值为-1,所以会打印一个"accept error!"。文章来源:https://www.toymoban.com/news/detail-539970.html
虽然这段代码并不会给服务器带来任何功能上的差错,但是一个连接断开就打印一次"accept error!"确实是比较奇怪的。因此写下该篇文章以做记录。文章来源地址https://www.toymoban.com/news/detail-539970.html
到了这里,关于Linux——信号处理函数与阻塞状态的进程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!