Socket进程处理被中断的系统调用及Accept函数返回EINTR错误处理

Posted ljbguanli

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Socket进程处理被中断的系统调用及Accept函数返回EINTR错误处理相关的知识,希望对你有一定的参考价值。

我们用慢系统调用来描写叙述那些可能永远阻塞的系统调用(函数调用),如:accept。read等。永远阻塞的系统调用是指调用有可能永远无法返回。多数网络支持函数都属于这一类。比如,假设没有客户连接到server上,则server对accept的调用就没有返回保证。类似的。假设客户从未发送过一行要求server回射的文本。则server对read的调用将永不返回。其它慢系统调用的样例是对管道和终端设备的读写。

有一个例外,就是磁盘IO。他一般都返回调用者。

当一个进程阻塞与慢系统调用时捕获到一个信号。等到信号处理程序返回时,系统调用可能返回一个EINTR错误。有些内核自己主动重新启动某些被中断的系统调用。

为了便于移植,当我们编写一个捕获信号的程序时(多数并发server捕获SIGCHLD)。我们必须对慢系统调用返回EINTR有所准备。

为了处理一个被中断的accept,我们对accept的调用尽心了处理,其它慢系统调用函数也能够照此思路进行处理:

第一种方法: 用continue进入for的下一次循环,从而重新启动被中断的系统调用。

for( ; ; ) 
{
    clilen = sizeof(cliaddr);
    if((connfd = accept(listenfd, (SA *)&cliaddr, &clilen)) < 0) {
        if(errno == EINTR) 
            continue;
        else 
            err_sys("accept error");
    }
}

或者 用goto来实现一样的功能,也相同让被中断的系统调用重新启动;

Again:
for( ; ; ) 
{
    clilen = sizeof(cliaddr);
    if((connfd = accept(listenfd, (SA *)&cliaddr, &clilen)) < 0) {
        if(errno == EINTR) 
            goto Again;
        else 
            err_sys("accept error");
    }
}

另外要说明的是:

适用于慢系统调用的基本规则是:当堵塞于某个慢系统调用的一个进程捕获某个信号切对应信号处理函数返回时,该系统调用可能返回一个EINTR错误;而有些系统内核会自己主动重新启动某些被中断的系统调用; 这点要注意;


在这段代码中,我们所做的就是自己重新启动被中断的系统调用,这对于accept以及其它诸如read。write。select和open这种函数是合适的,但有一个函数我们不能自己重新启动:connect。假设这个函数返回INTER。我们就不能再调用他,否则返回错误。

当connet被一个捕获的信号中断并且不自己主动重新启动时,我们必须调用select来等待连接完毕。


最后。当我们编写处理accept返回EINTR错误的TCPserver终于版本号的时候。首先要注意幾個問題:

>>> 当fork子进程时,必须捕获SIGCHLD信号(SIGCHLD信号是子进程结束时。向内核发送的信号

>>> 当捕获信号时。必须处理被中断的系统调用

>>> SIGCHLD的信号处理函数(sig_chld)必须正确编写,应使用waitpid函数杀死僵死进程;


下面就是 “处理accept函数返回EINTR错误的TCPserver程序终于版本号”:

#include <unp.h>

int
main(int argc, char **argv)
{
    int listenfd, connfd;
    pid_t child_pid;
    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);
    
    for( ; ; ) {
        clilen = sizeof(cliaddr);
        if( (connfd = accept(listenfd, (SA *)&cliaddr, &clilen)) < 0 ) {
            if(errno == EINTR) 
                continue;
            else 
                err_sys("accept error");
        }
        if( (child_pid = Fork()) == 0 ) {
            Close(listenfd);
            str_cli(connfd);
            exit(0);
        }
        Close(connfd);
    }
}

近期都忙着看书,好少时间写博客和刷题了~~

以上是关于Socket进程处理被中断的系统调用及Accept函数返回EINTR错误处理的主要内容,如果未能解决你的问题,请参考以下文章

Socket与系统调用深度分析

Socket与系统调用深度分析

linux中对EINTR错误的处理

Socket与系统调用深度分析

Socket与系统调用深度分析

Linux中断的系统调用