日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

linux系统中socket错误码:EINTR和EAGAIN的处理(Linux.org)

發布時間:2023/12/15 综合教程 30 生活家
生活随笔 收集整理的這篇文章主要介紹了 linux系统中socket错误码:EINTR和EAGAIN的处理(Linux.org) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

人為重啟被中斷的系統調用

安裝信號時設置 SA_RESTART屬性

忽略信號


永遠阻塞的系統調用,被信號中斷,導致其不繼續等待,轉而去執行signal_handler

1、什么是慢系統調用?

該術語適用于那些可能永遠阻塞的系統調用。永遠阻塞的系統調用是指調用永遠無法返回,多數網絡支持函數都屬于這一類。如:若沒有客戶連接到服務器上,那么服務器的accept調用就會永遠阻塞。

慢系統調用可以被永久阻塞,包括以下幾個類別:
(1)讀寫‘慢’設備(包括pipe,終端設備,網絡連接等)。讀時,數據不存在,需要等待;寫時,緩沖區滿或其他原因,需要等待。
(2)當打開某些特殊文件時,需要等待某些條件,才能打開。例如:打開中斷設備時,需要等到連接設備的modem響應才能完成。
(3)pause和wait函數。pause函數使調用進程睡眠,直到捕獲到一個信號。wait等待子進程終止。
(4)某些ioctl操作。
(5)某些IPC操作。

2、EINTR錯誤產生的原因-(阻塞的系統調用、或者非阻塞的系統調用)

如果進程在一個慢系統調用(slow system call)中阻塞時,當捕獲到某個信號且相應信號處理函數返回時,這個系統調用不再阻塞而是被中斷,就會調用返回錯誤(一般為-1)&&設置errno為EINTR(相應的錯誤描述為“Interrupted system call”)。

如下表所示的系統調用就會產生EINTR錯誤,當然不同的函數意義也不同。

系統調用函數 errno為EINTR表征的意義
write 由于信號中斷,沒寫成功任何數據。The call was interrupted by a signal before any data was written.
open 由于信號中斷,沒讀到任何數據。The call was interrupted by a signal before any data was read.
recv
sem_wait 函數調用被信號處理函數中斷。The call was interrupted by a signal handler.

3、解決辦法

既然系統調用會被中斷,那么別忘了要處理被中斷的系統調用。有三種處理方式:

解決方法1:重啟被中斷的系統調用

當碰到EINTR錯誤的時候,有一些可以重啟的系統調用要進行重啟,而對于有一些系統調用是不能夠重啟的。例如:accept、read、write、select、和open之類的函數來說,是可以進行重啟的。不過對于套接字編程中的connect函數是不能重啟的,若connect函數返回一個EINTR錯誤的時候,我們不能再次調用它,否則將立即返回一個錯誤。針對connect不能重啟的處理方法是,必須調用select來等待連接完成。

理解“重啟”?一些IO系統調用執行時,如 read 等待輸入期間,如果收到一個信號,系統將中斷read, 轉而執行信號處理函數. 當信號處理返回后, 系統遇到了一個問題: 是重新開始這個系統調用? 還是讓系統調用失敗?早期UNIX系統的做法是:中斷系統調用,并讓系統調用失敗, 比如read返回 -1, 同時設置 errno 為EINTR中斷了的系統調用是沒有完成的調用,它的失敗是臨時性的,如果再次調用則可能成功,這并不是真正的失敗,所以要對這種情況進行處理, 典型的方式為“重啟”,采用accept函數為例子,代碼如下

ACCEPT:
    clifd = accept(srvfd,(struct sockaddr*)&cliaddr,&cliaddrlen);
 
    if (clifd == -1) {
        if (errno == EINTR) {
            goto ACCEPT;
        } else {
            fprintf(stderr, "accept fail,error:%s\n", strerror(errno));
            return -1;
        }
    }

解決方法2:安裝信號時設置 SA_RESTART屬性(該方法對有的系統調用無效)

struct sigaction action;  
     
  action.sa_handler = handler_func;  
  sigemptyset(&action.sa_mask);  
  action.sa_flags = 0;  
  /* 設置SA_RESTART屬性 */  
  action.sa_flags |= SA_RESTART;  
     
  sigaction(SIGALRM, &action, NULL); 

解決方法3: 忽略信號(讓系統不產生信號中斷)

struct sigaction action;  
     
  action.sa_handler = SIG_IGN;  
  sigemptyset(&action.sa_mask);  
     
  sigaction(SIGALRM, &action, NULL); 

EAGAIN-(一般用于非阻塞的系統調用)

非阻塞的系統調用,由于資源限制/不滿足條件,導致返回值為EAGAIN

在Linux環境下開發經常會碰到很多錯誤(設置errno),其中EAGAIN是其中比較常見的一個錯誤(比如用在非阻塞操作中)。

如:首先是把套接字設置為異步的了,然后在使用write發送數據時采取的方式是循環發送大量的數據;由于是異步的,write\send將要發送的數據提交到發送緩沖區后是立即返回的,并不需要對端確認數據已接收。在這種情況下是很有可能出現發送緩沖區被填滿,導致write\send無法再向緩沖區提交要發送的數據。因此就產生了Resource temporarily unavailable的錯誤(資源暫時不可用),EAGAIN 的意思也很明顯,就是要你再次嘗試。

從字面上來看,是提示再試一次。這個錯誤經常出現在當應用程序進行一些非阻塞(non-blocking)操作(對文件或socket)的時候。

如:以 O_NONBLOCK的標志打開文件/socket/FIFO,如果連續做read操作而沒有數據可讀。此時程序不會阻塞起來等待數據準備就緒返回,read函數會返回一個錯誤EAGAIN,提示你的應用程序現在沒有數據可讀請稍后再試。
又例如,當一個系統調用(比如fork)因為沒有足夠的資源(比如虛擬內存)而執行失敗,返回EAGAIN提示其再調用一次(也許下次就能成功)。

Linux – 非阻塞socket編程處理EAGAIN錯誤

在linux進行非阻塞的socket接收數據時經常出現Resource temporarily unavailable,errno代碼為11(EAGAIN),這是什么意思?? ? ? 這表明在非阻塞模式下調用了阻塞操作,在該操作沒有完成就返回這個錯誤,這個錯誤不會破壞socket的同步,不用管它,下次循環接著recv就可以。對非阻塞socket而言,EAGAIN不是一種錯誤。在VxWorks和Windows上,EAGAIN的名字叫做EWOULDBLOCK。

iReadSizeOnce=read(iOpenCom,RxBuf+iReadSize,1024);
if (iReadSizeOnce != ZERO)
{
    if (iReadSizeOnce != EAGAIN)
    {
        continue;
    }
    else
    {
        //stCComApiLog.LogError("讀串口操作錯誤");
        return(FUN_ERROR);
    }
}

借鑒于:

1 http://blog.csdn.net/yanook/article/details/7226019 慢系統調用函數如何處理中斷信號EINTR
2 http://blog.csdn.net/benkaoya/article/details/17262053信號中斷 與 慢系統調用
3 http://1.guotie.sinaapp.com/?p=235 socket,accept,connect出現EINTR錯誤的解決方法

慢系統調用:可能永遠阻塞的系統調用,這很關鍵,不適用于非諸塞的情況。永遠阻塞的系統調用是指調用永遠無法返回,多數網絡支持函數都屬于這一類。如:若沒有客戶連接到服務器上,那么服務器的accept調用就會一直阻塞。
(以下為抄襲2原文)
EINTR說明:如果進程在一個慢系統調用(slow system call)中阻塞時,當捕獲到某個信號且相應信號處理函數返回時,這個系統調用被中斷,調用返回錯誤,設置errno為EINTR(相應的錯誤描述為“Interrupted system call”)。

怎么看哪些系統條用會產生EINTR錯誤呢?man 7 signal,在ubuntu 10.04上可以查看,哪些系統調用會產生EINTR錯誤。

如何處理被中斷的系統調用

既然系統調用會被中斷,那么別忘了要處理被中斷的系統調用。有三種處理方式:

◆ 人為重啟被中斷的系統調用

◆ 安裝信號時設置 SA_RESTART屬性(該方法對有的系統調用無效)

◆ 忽略信號(讓系統不產生信號中斷)

人為重啟被中斷的系統調用

人為當碰到EINTR錯誤的時候,有一些可以重啟的系統調用要進行重啟,而對于有一些系統調用是不能夠重啟的。例如:accept、read、write、select、和open之類的函數來說,是可以進行重啟的。不過對于套接字編程中的connect函數我們是不能重啟的,若connect函數返回一個EINTR錯誤的時候,我們不能再次調用它,否則將立即返回一個錯誤。針對connect不能重啟的處理方法是,必須調用select來等待連接完成。

這里的“重啟”怎么理解?

一些IO系統調用執行時,如 read 等待輸入期間,如果收到一個信號,系統將中斷read, 轉而執行信號處理函數. 當信號處理返回后, 系統遇到了一個問題: 是重新開始這個系統調用, 還是讓系統調用失???早期UNIX系統的做法是, 中斷系統調用,并讓系統調用失敗, 比如read返回 -1, 同時設置 errno 為EINTR中斷了的系統調用是沒有完成的調用,它的失敗是臨時性的,如果再次調用則可能成功,這并不是真正的失敗,所以要對這種情況進行處理, 典型的方式為:

connect處理方式,抄襲3原文,沒有測試過,處理方法是對的。
connect的問題,當connect遇到EINTR錯誤時,不能向上面那樣重新進入循環處理,原因是,connect的請求已經發送向對方,正在等待對方回應,這是如果重新調用connect,而對方已經接受了上次的connect請求,這一次的connect就會被拒絕,因此,需要使用select或poll調用來檢查socket的狀態,如果socket的狀態就緒,則connect已經成功,否則,視錯誤原因,做對應的處理。

connect處理方式,抄襲3原文,沒有測試過,處理方法是對的。
connect的問題,當connect遇到EINTR錯誤時,不能向上面那樣重新進入循環處理,原因是,connect的請求已經發送向對方,正在等待對方回應,這是如果重新調用connect,而對方已經接受了上次的connect請求,這一次的connect就會被拒絕,因此,需要使用select或poll調用來檢查socket的狀態,如果socket的狀態就緒,則connect已經成功,否則,視錯誤原因,做對應的處理。

#include poll.h

int check_conn_is_ok(socket_t sock) {
	struct pollfd fd;
	int ret = 0;
	socklen_t len = 0;

	fd.fd = sock;
	fd.events = POLLOUT;

	while ( poll (&fd, 1, -1) == -1 ) {
		if( errno != EINTR ){
			perror("poll");
			return -1;
		}
	}

	len = sizeof(ret);
	if ( getsockopt (sock, SOL_SOCKET, SO_ERROR,
                     &ret,
                     &len) == -1 ) {
    	        perror("getsockopt");
		return -1;
	}

	if(ret != 0) {
		fprintf (stderr, "socket %d connect failed: %s\n",
                 sock, strerror (ret));
		return -1;
	}

	return 0;
}

在調用connect時,這樣使用:

#include erron.h

....
if(connnect()) {
    if(errno == EINTR) {
        if(check_conn_is_ok() < 0) {
              perror();
              return -1;
        }
        else {
             printf("connect is success!\n");
        }
    }
    else {
         perror("connect");
         return -1;
    }
}

我一般使用continue或者goto來處理。

安裝信號時設置 SA_RESTART屬性

我們還可以從信號的角度來解決這個問題, 安裝信號的時候, 設置 SA_RESTART屬性,那么當信號處理函數返回后, 不會讓系統調用返回失敗,而是讓被該信號中斷的系統調用將自動恢復。

struct sigaction action;  
   
action.sa_handler = handler_func;  
sigemptyset(&action.sa_mask);  
action.sa_flags = 0;  
/* 設置SA_RESTART屬性 */  
action.sa_flags |= SA_RESTART;  
   
sigaction(SIGALRM, &action, NULL);  

但注意,并不是所有的系統調用都可以自動恢復。如msgsnd喝msgrcv就是典型的例子,msgsnd/msgrcv以block方式發送/接收消息時,會因為進程收到了信號而中斷。此時msgsnd/msgrcv將返回-1,errno被設置為EINTR。且即使在插入信號時設置了SA_RESTART,也無效。在man msgrcv中就有提到這點:

msgsnd and msgrcv are never automatically restarted after being interrupted by a signal handler, regardless of the setting of the SA_RESTART flag when establishing a signal handler.

忽略信號

當然最簡單的方法是忽略信號,在安裝信號時,明確告訴系統不會產生該信號的中斷。

struct sigaction action;  
   
action.sa_handler = SIG_IGN;  
sigemptyset(&action.sa_mask);  
   
sigaction(SIGALRM, &action, NULL);  
#include   
#include   
#include   
#include   
#include   
#include   
   
void sig_handler(int signum)  
{  
    printf("in handler\n");  
    sleep(1);  
    printf("handler return\n");  
}  
   
int main(int argc, char **argv)  
{  
    char buf[100];  
    int ret;  
    struct sigaction action, old_action;  
   
    action.sa_handler = sig_handler;  
    sigemptyset(&action.sa_mask);  
    action.sa_flags = 0;  
    /* 版本1:不設置SA_RESTART屬性 
     * 版本2:設置SA_RESTART屬性 */  
    //action.sa_flags |= SA_RESTART;  
   
    sigaction(SIGALRM, NULL, &old_action);  
    if (old_action.sa_handler != SIG_IGN) {  
        sigaction(SIGALRM, &action, NULL);  
    }  
    alarm(3);  
     
    bzero(buf, 100);  
   
    ret = read(0, buf, 100);  
    if (ret == -1) {  
        perror("read");  
    }  
   
    printf("read %d bytes:\n", ret);  
    printf("%s\n", buf);  
   
    return 0;  
}  

在ubuntu 10.04 上測試結果:
不設置SA_RESTART,執行結果如下:

說明接受信號處理完成以后,主函數收到EINTR信號,read函數返回-1,退出
設置SA_RESTART,執行結果如下:

說明設置SA_RESTART參數以后,自動重新調用read函數,沒有體現在應用層代碼中,在應用層看來,這個EINTR沒有造成任何影響。

個人認為下面的總結很重要:

慢系統調用(slow system call)會被信號中斷,系統調用函數返回失敗,并且errno被置為EINTR(錯誤描述為“Interrupted system call”)。

處理方法有以下三種:①人為重啟被中斷的系統調用;②安裝信號時設置 SA_RESTART屬性;③忽略信號(讓系統不產生信號中斷)。

有時我們需要捕獲信號,但又考慮到第②種方法的局限性(設置 SA_RESTART屬性對有的系統無效,如msgrcv),所以在編寫代碼時,一定要“人為重啟被中斷的系統調用”。

總結

以上是生活随笔為你收集整理的linux系统中socket错误码:EINTR和EAGAIN的处理(Linux.org)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。