Linux 慢系統調用與EINTR(被中斷)


1. 慢系統調用

UNP 5.9提到, 慢系統調用(slow system call)指不會立即返回的系統調用, 可能永遠阻塞而無法返回. 諸如多數網絡支持函數, 包括read/write, connect, accept等, 都屬於這一類.
慢系統調用, 主要分為以下類別:

  1. 讀寫"慢"設備
    包括pipe, fifo, 終端設備, 網絡連接等. 讀時, 數據不存在, 需要等待緩沖區有數據輸入; 寫時, 緩沖區滿, 需要等待緩沖區有空閑位置.
    注意: 讀寫磁盤文件一般不會阻塞.

  2. 打開某些特殊文件時, 需要等待某些條件才能打開
    如打開終端設備, 需要等待連接設備的modern響應, 才能打開

  3. pause和wait系統調用
    pause阻塞進程, 直到收到信號喚醒;
    wait等待任意子進程終止;

  4. 某些ioctl操作

  5. 某些IPC操作
    如pipe, fifo, 沒有指定NON_BLOCKING選項時的寫操作, 如果管道緩沖區滿, write阻塞;
    互斥鎖, 條件變量, 信號量, 記錄鎖等等.

2. EINTR

EINTR是什么?
EINTR指Interrupted system call, 表示由於中斷而錯誤返回.

適用於滿系統調用的基本規則: 當阻塞某個慢系統調用的一個進程捕獲某個信號, 且相應信號處理函數返回時, 該系統調用返回一個EINTR錯誤.
通常, 也會置errno = EINTR

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

編寫捕獲信號的程序時, 必須對慢系統調用返回EINTR有所准備. 通常, 有三種處理方式:

1) 人為啟動被中斷的系統調用
有些系統調用被中斷后能重啟, 有些不能. 所謂重啟, 是指再次調用系統調用, 對於那些由於中斷導致臨時返回錯誤的情況, 可以再次調用獲得正確結果; 而對於那些已經修改了內部狀態的情況如connect會修改套接字狀態, 則不能再次調用.
如open, read, write, select(源自Berkeley的實現不自動重啟select), accept/recvfrom(有些實現不重啟) , 能人為重啟;
而如connect不能重啟, 因為connect失敗時, 可能導致socket狀態改變, 不能直接重新調用connect. 可以調用select等待連接完成; 當然也可以創建新socket, 然后再次調用connect.

下面這段人為重啟被中斷系統調用的代碼, 適用於能重啟的系統調用:

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

2) 安裝信號時設置SA_RESTART屬性, 實現自動重啟(非適用於所有被中斷系統調用)
只能針對設置的信號, 發生捕獲返回后, 恢復被中斷的系統調用.
比如, 針對捕獲SIGALRM信號導致中斷系統調用后, 利用設置SA_RESTART屬性自動重啟系統調用:

struct sigaction sa

sa.sa_handler = 
sigsetempty(&sa.sa_mask);

sa.sa_flags = 0;
sa.sa_flags |= SA_RESTART;

sigaction(SIGALRM, &sa, NULL);

注意: 並不是所有系統調用都支持SA_RESTART屬性, 自動重啟, 比如msgsnd/msgrcv(System V隊列 消息發送/接收).

a signal is caught, in which case the system call fails with errno set to  EINTR;see  sig‐
         nal(7).   (msgsnd()  is  never automatically restarted after being interrupted by a signal
         handler, regardless of the setting of the SA_RESTART flag when establishing a signal  han‐
         dler.)

      * The calling process catches a signal.  In this case, the system call fails with errno  set
         to  EINTR.  (msgrcv() is never automatically restarted after being interrupted by a signal
         handler, regardless of the setting of the SA_RESTART flag when establishing a signal  han‐
         dler.)

3) 忽略信號
設置信號處置方式為忽略(SIG_IGN), 告知進程不會處理對應信號, 也就不會因為該信號而中斷了.
例如, 忽略SIGALRM信號

signal(SIGALRM, SIG_IGN);

參考 信號中斷 與 慢系統調用 | CSDN


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM