Socket進程處理被中斷的系統調用及Accept函數返回EINTR錯誤處理 (轉)

 

轉自 http://blog.csdn.net/keshacookie/article/details/40717059?utm_source=tuicool服務器

咱們用慢系統調用來描述那些可 能永遠堵塞的系統調用(函數調用),如:accept,read等。永遠堵塞的系統調用是指調用有可能永遠沒法返回,多數網絡支持函數都屬於這一類。例 如,若是沒有客戶鏈接到服務器上,則服務器對accept的調用就沒有返回保證。相似的,若是客戶從未發送過一行要求服務器回射的文本,則服務器對 read的調用將永不返回。其餘慢系統調用的例子是對管道和終端設備的讀寫。有一個例外,就是磁盤IO,他通常都返回調用者。

當一個進程堵塞與慢系統調用時捕獲到一個信號,等到信號處理程序返回時,系統調用可能返回一個EINTR錯誤。有些內核自動重啓某些被中斷的系統調用。爲 了便於移植,當咱們編寫一個捕獲信號的程序時(多數併發服務器捕獲SIGCHLD),咱們必須對慢系統調用返回EINTR有所準備。
cookie

爲了處理一個被中斷的accept,咱們對accept的調用盡心了處理,其餘慢系統調用函數也能夠照此思路進行處理:網絡

第一種方法: 用continue進入for的下一次循環,從而重啓被中斷的系統調用;併發

 

 
  1. for( ; ; )   
  2. {  
  3.     clilen = sizeof(cliaddr);  
  4.     if((connfd = accept(listenfd, (SA *)&cliaddr, &clilen)) < 0) {  
  5.         if(errno == EINTR)   
  6.             continue;  
  7.         else   
  8.             err_sys("accept error");  
  9.     }  
  10. }  


或者 用goto來實現同樣的功能,也一樣讓被中斷的系統調用重啓;
函數

 

 
  1. Again:  
  2. for( ; ; )   
  3. {  
  4.     clilen = sizeof(cliaddr);  
  5.     if((connfd = accept(listenfd, (SA *)&cliaddr, &clilen)) < 0) {  
  6.         if(errno == EINTR)   
  7.             goto Again;  
  8.         else   
  9.             err_sys("accept error");  
  10.     }  
  11. }  


另外要說明的是:
ui

適用於慢系統調用的基本規則是:當阻塞於某個慢系統調用的一個進程捕獲某個信號切相應信號處理函數返回時,該系統調用可能返回一個EINTR錯誤;而有些系統內核會自動重啓某些被中斷的系統調用; 這點要注意;spa


在這段代碼中,咱們所作 的就是本身重啓被中斷的系統調用,這對於accept以及其餘諸如read,write,select和open這樣的函數是合適的,但有一個函數咱們不 能本身重啓:connect。若是這個函數返回INTER,咱們就不能再調用他,不然返回錯誤。當connet被一個捕獲的信號中斷並且不自動重啓時,我 們必須調用select來等待鏈接完成。.net


最後,當咱們編寫處理accept返回EINTR錯誤的TCP服務器最終版本的時候,首先要注意幾個問題:blog

>>> 當fork子進程時,必須捕獲SIGCHLD信號(SIGCHLD信號是子進程結束時,向內核發送的信號)進程

>>> 當捕獲信號時,必須處理被中斷的系統調用

>>> SIGCHLD的信號處理函數(sig_chld)必須正確編寫,應使用waitpid函數殺死僵死進程;


如下就是 「處理accept函數返回EINTR錯誤的TCP服務器程序最終版本」:

 

 
    1. #include <unp.h>  
    2.   
    3. int  
    4. main(int argc, char **argv)  
    5. {  
    6.     int listenfd, connfd;  
    7.     pid_t child_pid;  
    8.     socklen_t clilen;  
    9.     struct sockaddr_in cliaddr, servaddr;  
    10.     void sig_chld(int);  
    11.       
    12.     listenfd = Socket(AF_INET, SOCK_STREAM, 0);  
    13.     bzero(&servaddr, sizeof(servaddr));  
    14.     servaddr.sin_family = AF_INET;  
    15.     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
    16.     servaddr.sin_port = htons(SERV_PORT);  
    17.       
    18.     Bind(listenfd, (SA *)&servaddr, sizeof(servaddr));  
    19.     listen(listenfd, LISTENQ);  
    20.     Signal(SIGCHLD, sig_chld);  
    21.       
    22.     for( ; ; ) {  
    23.         clilen = sizeof(cliaddr);  
    24.         if( (connfd = accept(listenfd, (SA *)&cliaddr, &clilen)) < 0 ) {  
    25.             if(errno == EINTR)   
    26.                 continue;  
    27.             else   
    28.                 err_sys("accept error");  
    29.         }  
    30.         if( (child_pid = Fork()) == 0 ) {  
    31.             Close(listenfd);  
    32.             str_cli(connfd);  
    33.             exit(0);  
    34.         }  
    35.         Close(connfd);  
    36.     }  
相關文章
相關標籤/搜索