日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務,提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

1、什么是慢系統(tǒng)調用?

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

慢系統(tǒng)調用可以被永久阻塞,包括以下幾個類別:

(1)讀寫‘慢’設備(包括pipe,終端設備,網(wǎng)絡連接等)。讀時,數(shù)據(jù)不存在,需要等待;寫時,緩沖區(qū)滿或其他原因,需要等待。

(2)當打開某些特殊文件時,需要等待某些條件,才能打開。例如:打開中斷設備時,需要等到連接設備的modem響應才能完成。

(3)pause和wait函數(shù)。pause函數(shù)使調用進程睡眠,直到捕獲到一個信號。wait等待子進程終止。

(4)某些ioctl操作。

(5)某些IPC操作。

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

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

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

linux系統(tǒng)中socket錯誤碼:eintr和eagain的處理方法

 

3、解決辦法

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

解決方法1:重啟被中斷的系統(tǒng)調用

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

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

需要C/C++ linux服務器架構師學習資料私信“資料”(資料包括C/C++,Linux,golang技術,Nginx,ZeroMQ,MySQL,redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協(xié)程,DPDK,ffmpeg等),免費分享

linux系統(tǒng)中socket錯誤碼:eintr和eagain的處理方法

 

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

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

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: 忽略信號(讓系統(tǒng)不產生信號中斷)

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

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

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

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

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

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

如:以 O_NONBLOCK的標志打開文件/socket/FIFO,如果連續(xù)做read操作而沒有數(shù)據(jù)可讀。此時程序不會阻塞起來等待數(shù)據(jù)準備就緒返回,read函數(shù)會返回一個錯誤EAGAIN,提示你的應用程序現(xiàn)在沒有數(shù)據(jù)可讀請稍后再試。

又例如,當一個系統(tǒng)調用(比如fork)因為沒有足夠的資源(比如虛擬內存)而執(zhí)行失敗,返回EAGAIN提示其再調用一次(也許下次就能成功)。

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

在linux進行非阻塞的socket接收數(shù)據(jù)時經(jīng)常出現(xiàn)Resource temporarily unavailable,errno代碼為11(EAGAIN),這是什么意思? ⇒ ⇒ ⇒ 這表明在非阻塞模式下調用了阻塞操作,在該操作沒有完成就返回這個錯誤,這個錯誤不會破壞socket的同步,不用管它,下次循環(huán)接著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);
    }
}

慢系統(tǒng)調用:可能永遠阻塞的系統(tǒng)調用,這很關鍵,不適用于非諸塞的情況。永遠阻塞的系統(tǒng)調用是指調用永遠無法返回,多數(shù)網(wǎng)絡支持函數(shù)都屬于這一類。如:若沒有客戶連接到服務器上,那么服務器的accept調用就會一直阻塞。

EINTR說明:如果進程在一個慢系統(tǒng)調用(slow system call)中阻塞時,當捕獲到某個信號且相應信號處理函數(shù)返回時,這個系統(tǒng)調用被中斷,調用返回錯誤,設置errno為EINTR(相應的錯誤描述為“Interrupted system call”)。

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

如何處理被中斷的系統(tǒng)調用

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

◆ 人為重啟被中斷的系統(tǒng)調用

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

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

人為重啟被中斷的系統(tǒng)調用

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

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

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

connect處理方式,抄襲3原文,沒有測試過,處理方法是對的。

connect的問題,當connect遇到EINTR錯誤時,不能向上面那樣重新進入循環(huán)處理,原因是,connect的請求已經(jīng)發(fā)送向對方,正在等待對方回應,這是如果重新調用connect,而對方已經(jīng)接受了上次的connect請求,這一次的connect就會被拒絕,因此,需要使用select或poll調用來檢查socket的狀態(tài),如果socket的狀態(tài)就緒,則connect已經(jīng)成功,否則,視錯誤原因,做對應的處理。

linux系統(tǒng)中socket錯誤碼:eintr和eagain的處理方法

 

#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: %sn",
                 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屬性,那么當信號處理函數(shù)返回后, 不會讓系統(tǒng)調用返回失敗,而是讓被該信號中斷的系統(tǒng)調用將自動恢復。

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);  

但注意,并不是所有的系統(tǒng)調用都可以自動恢復。如msgsnd喝msgrcv就是典型的例子,msgsnd/msgrcv以block方式發(fā)送/接收消息時,會因為進程收到了信號而中斷。此時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.

 

忽略信號

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

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 handlern");  
    sleep(1);  
    printf("handler returnn");  
}  
   
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("%sn", buf);  
   
    return 0;  
}  

在linux測試結果:

不設置SA_RESTART,執(zhí)行結果如下:

linux系統(tǒng)中socket錯誤碼:eintr和eagain的處理方法

 

說明接受信號處理完成以后,主函數(shù)收到EINTR信號,read函數(shù)返回-1,退出

設置SA_RESTART,執(zhí)行結果如下:

linux系統(tǒng)中socket錯誤碼:eintr和eagain的處理方法

 

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

總結:

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

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

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

分享到:
標簽:linux
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定