建站加盟,南京百度,公司官网制作方案,深圳网站建设制作订做一、被中断的系统调用(EINTR)的理解1. 慢系统调用是#xff1f;2. 慢系统调用的类别3. EINTR产生的原因5. 一般处理方法 二、SIGCHLD信号的处理1. SIGCHLD信号的产生2. SIGCHLD信号的处理3. 不处理SIGCHLD的后果 三、示例代码 一、被中断的系统调用(EINTR)的理解
1. 慢系…一、被中断的系统调用(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;
}
运行结果