• 一、被中断的系统调用(EINTR)的理解

    • 1. 慢系统调用是?
    • 2. 慢系统调用的类别
    • 3. EINTR产生的原因
    • 5. 一般处理方法
  • 二、SIGCHLD信号的处理
    • 1. SIGCHLD信号的产生
    • 2. SIGCHLD信号的处理
    • 3. 不处理SIGCHLD的后果
  • 三、示例代码

一、被中断的系统调用(EINTR)的理解

1. 慢系统调用是?

慢系统调用指可能永远阻塞的系统调用。

也就是处于阻塞状态的系统调用,如果不收到需要的信息,就会一直阻塞在那里。例如accept:在服务器等待客户端建立连接时,如果没有客户端来请求连接,那么accept就会一直阻塞,直到有客户端请求连接为止。像这种系统调用,就称为慢系统调用。

2. 慢系统调用的类别
  • 对管道的读写
  • 对终端设备设备的读写
  • 对网络连接的读写
  • ……

值得注意的是,读写磁盘文件一般不会阻塞,一般会返回给调用者(在没有硬件故障的条件下)

3. EINTR产生的原因

当阻塞于某个慢系统调用的一个进程捕获某个信号且相应信号处理函数返回时,该系统调用可能返回一个EINTR错误。有些内核会自动重启某些被中断的系统调用。

对这句话的理解:
慢系统调用在阻塞状态中,如果收到了某个信号,该系统调用就会被中断,转而去执行对应的信号处理函数,在信号处理函数执行完后,被中断的系统调用就会返回EINTR。有的内核会在被中断的系统调用返回EINTR后重新执行。

accept为例,在服务器阻塞于accept时,这时之前已连接的客户端关闭了,对应该客户端的子进程就会退出,并发送SIGCHLD信号。如果定义了信号处理函数,就会造成accept的中断,然后执行该信号处理函数,执行完后accept就会返回EINTR

5. 一般处理方法

推荐将对accept的调用改为:

while( true ) {int clientlen = sizeof( cliaddr );int connfd = accept( sockfd, (struct sockaddr *)&cliaddr, &clietnlen );if( connfd < 0 ) {if( errnp == EINTR ) {  //如果返回EINTR,就重启该系统调用continue;} else {perror( "accept " );}}// do other things
}

还有两种方法:设置SA_RESTART属性、忽略信号,不过推荐使用上述方法。

二、SIGCHLD信号的处理

1. SIGCHLD信号的产生

在子进程退出时,会发送给父进程一个SIGCHLD信号来表明子进程已退出,此时可以使用wait/waitpid来等待子进程退出并回收子进程占用的资源,避免产生僵尸进程。该信号被默认为忽略。

2. SIGCHLD信号的处理

可以定义该信号的信号处理函数,并使用signal()来运行该信号对应的信号处理函数。

void sig_chld( int signo ) {pid_t pid;int stat;while( ( pid = waitpid( -1, &stat, WNOHANG ) ) > 0 ) {printf("child %d terminated\n", pid );}return ;
}signal( SIGCHLD, sig_chld );

其中,我们必须要为waitpid()函数设置WNOHANG属性,以便告知waitpid在仍有子进程尚未终止时不要阻塞。
循环中不能使用wait()函数,因为我们不能保证wait在还有子进程运行时不会发生阻塞。

在使用循环判断后,就可以将所有已结束的子进程的资源进行回收。

3. 不处理SIGCHLD的后果

如果我们在收到SIGCHLD信号后忽略,而且也没有在父进程最后wait/waitpid子进程结束,就会有僵尸进程产生。僵尸进程虽然不再占有内存等资源,但是会保留进程表中的表项,因此会造成内存泄漏。

linux下,可以使用ps命令查看,stat一栏标志为Z的就是僵尸进程。

三、示例代码

  • server.cpp
#include <iostream>
#include <string.h>
#include <sys/socket.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <errno.h>
#include <wait.h>
using namespace std;const int PORT = 8080;/* 接收客户端信息 */
void str_echo( int conn_fd ) {ssize_t n;char buf[1024] = {0};while( ( n = read( conn_fd, buf, 1024 ) ) > 0 ) {write( conn_fd, buf, n );}if( n < 0 && errno == EINTR ) {continue;} else if( n < 0 ) {perror( "read " );}
}void sig_chld( int signo ) {pid_t pid;int stat;while( ( pid = waitpid( -1, &stat, WNOHANG ) ) > 0 ) {printf("child %d terminated\n", pid );}return ;
}int main() {int sock_fd = socket( AF_INET, SOCK_STREAM, 0 );if( sock_fd < 0 ) {perror( "socket " );}struct sockaddr_in servaddr;memset( &servaddr, 0, sizeof( servaddr ) );servaddr.sin_family = AF_INET;servaddr.sin_port = htons( PORT );servaddr.sin_addr.s_addr = htonl( INADDR_ANY );int ret = bind( sock_fd, ( struct sockaddr * )&servaddr, sizeof( servaddr ) );if( ret < 0 ) {perror( "bind " );}ret = listen( sock_fd, 5 );if( ret < 0 ) {perror( "bind " );}signal( SIGCHLD, sig_chld );while( true ) {struct sockaddr_in clieaddr;socklen_t len;memset( &clieaddr, 0, sizeof( clieaddr ) );int conn_fd = accept( sock_fd, ( sockaddr * )&clieaddr, &len );if( conn_fd < 0 ) {if( errno == EINTR ) {cout << "EINTR\n";continue;} else {perror( "accept " );}}if( fork() == 0 ) {close( sock_fd );str_echo( conn_fd );exit(0);}close( conn_fd );}return 0;
}
  • client.cpp
#include <iostream>
#include <string.h>
#include <sys/socket.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;const int PORT = 8080;/* 给服务器发送、接收消息的函数 */
void str_cli( FILE *fp, int sock_fd ) {char recvline[1024] = {0}, sendline[1024] = {0};while( fgets( sendline, 1024, fp ) != NULL ) {write( sock_fd, sendline, strlen(sendline) );if( read( sock_fd, recvline, 1024 ) == 0 ) {perror( "readline " );}fputs( recvline, stdout );}
}int main( int argc, char **argv ) {if( argc != 2 ) {cout << "Usage : ./client + ip\n";return 0;}int sock_fd[5] = {0};for( int i = 0; i < 5; i++ ) {  // 创建5个连接sock_fd[i] = socket( AF_INET, SOCK_STREAM, 0 );if( sock_fd[i] < 0 ) {perror( "socket " );}sockaddr_in servaddr;memset( &servaddr, 0, sizeof( servaddr ) );servaddr.sin_family = AF_INET;servaddr.sin_port = htons( PORT );inet_pton( AF_INET, argv[1], &servaddr.sin_addr );int ret = connect( sock_fd[i], (sockaddr *)&servaddr, sizeof( servaddr ) );if( ret < 0 ) {perror( "connect " );}}str_cli( stdin, sock_fd[0] );return 0;
}

运行结果:

系统调用中断(EINTR)与SIGCHLD信号的处理相关推荐

  1. 【Linux信号专题】五、SIGCHLD信号详解

    欢迎关注博主 Mindtechnist 或加入[Linux C/C++/Python社区]一起探讨和分享Linux C/C++/Python/Shell编程.机器人技术.机器学习.机器视觉.嵌入式AI ...

  2. 处理SIGCHLD信号

    在上一讲中,我们使用fork函数得到了一个简单的并发服务器.然而,这样的程序有一个问题,就是当子进程终止时,会向父进程发送一个SIGCHLD信号,父进程默认忽略,导致子进程变成一个僵尸进程.僵尸进程一 ...

  3. Linux--信号signal、父子进程、SIGCHLD信号相关命令

    目录 1.概念: 2.信号的存储位置: 3.常见的信号的值以及对应的功能说明: 4.信号的值在系统源码中的定义: 5.响应方式: 6.改变信号的相应方式: (1)设置信号的响应方式: (2)默认:SI ...

  4. Linux进程信号(产生、保存、处理)/可重入函数概念/volatile理解/SIGCHLD信号

    首先区分一下Linux信号跟进程间通信中的信号量,它们的关系就犹如老婆跟老婆饼一样,没有一毛钱的关系. 信号的概念 信号的概念:信号是进程之间事件异步通知的一种方式,属于软中断.比如:红绿灯是一种信号 ...

  5. 第五讲 中断、异常和信号

    序 在第一讲中提到过异常的分类,根据同步或异步产生.无意或故意产生以及最终的的返回行为可以分为四类.但不管是哪种,CPU的响应过程基本一致.即CPU根据中断向量,在内存中找到相应的服务程序入口并调用该 ...

  6. linux下的僵尸进程处理SIGCHLD信号【转】

    转自:http://www.cnblogs.com/wuchanming/p/4020463.html 什么是僵尸进程? 首先内核会释放终止进程(调用了exit系统调用)所使用的所有存储区,关闭所有打 ...

  7. [ Linux ] 可重入函数,volatile 关键字,SIGCHLD信号

    目录 1.可重入函数 2.volatile 2.1从信号角度理解volatile的作用 2.2volatile的作用 3.SIGCHLD信号 3.1SIGCHLD信号的验证 1.可重入函数 在数据结构 ...

  8. Day53 Linux setitimer函数 信号集操作函数 信号捕捉 SIGCHLD信号

    目录 setitimer函数 信号集操作函数 1.信号集设定 2.igprocmask函数 3.sigpending函数 信号捕捉 1.signal函数 2.sigaction函数 SIGCHLD信号 ...

  9. 【Linux】SIGCHLD信号解决僵尸进程问题

    1. 基本信息 SIGCHLD信号产生的条件: 子进程终止时 子进程接收到SIGSTOP信号停止时 子进程处在停止态,接受到SIGCONT后唤醒时 以上三种条件都会给父进程发送SIGCHLD信号,父进 ...

最新文章

  1. 解吧源码解析重点看withWeight
  2. 闭包(匿名函数) php
  3. 函数的返回是返回给实参,然后由实参输出,返回值的作用是给输出的全部变为变量然后用.=连接好把变量存进数据库而不是输出完屏幕就拉倒了...
  4. RQNOJ103_最大利润
  5. python-自动发邮件
  6. mfc 消息消息队列概念_消息队列面试连环问
  7. Hadoop集群环境下网络架构的设计与优化
  8. 百度地图根据经纬度获取地址
  9. 怎么删除用户_误删的手机照片怎么恢复?三种快速恢复的方法
  10. Solaris Boot PROM 指令
  11. 关于linux下的iptables 的浅析命令和了解
  12. 《When Private Set Intersection Meets Big Data:An Efficient and Scalable Protocol》论文解读
  13. an error occurred while attempting to contact the server_cheney
  14. 【山科OJ】Problem A: 社交网络的好友推荐
  15. linux shell ifs,shell - IFS分隔符
  16. 手机App开发的基础概念
  17. Linux_Linux指令_lsof 指令
  18. MS VS+HIK海康机器人工业相机环境配置
  19. 怎么看谷歌seo运营效果
  20. 随笔-职场上的奇葩事儿

热门文章

  1. isis 网络 level 2 iih_ngspice实例介绍2--直流扫描分析
  2. RecyclerView的使用(二):添加头部和尾部
  3. DNS篇之DNS协议详解
  4. 数据仓库建设——主题和主题域的划分
  5. 网线连接olt配置计算机IP,OLT配置上行以太网端口属性
  6. ORACLE利用序列实现ID自增
  7. 给ACCESS2003加密码和去掉密码
  8. 仓库温度湿度控制措施_仓库温湿度管理规定_仓库温湿度监测管理制度
  9. C++四种cast的详细介绍
  10. 杭电ACM-LCY算法进阶培训班-专题训练(矩阵快速幂)