2012-08-07 68 views
4

我一直在閱讀EINTR關於write(2)等,並試圖確定我是否需要在我的程序中檢查它。作爲一個完整的檢查,我試圖編寫一個可以運行的程序。該程序永遠循環,重複寫入文件。爲什麼不寫(2)返回EINTR?

然後,在一個單獨的外殼,我運行:

while true; do pkill -HUP test; done 

然而,我從test.c的看到的唯一輸出是從信號處理程序中的.秒。爲什麼SIGHUP不會導致write(2)失敗?

test.c的:

#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <fcntl.h> 
#include <signal.h> 
#include <string.h> 
#include <errno.h> 

#include <sys/types.h> 

void hup_handler(int sig) 
{ 
    printf("."); 
    fflush(stdout); 
} 

int main() 
{ 
    struct sigaction act; 
    act.sa_handler = hup_handler; 
    act.sa_flags = 0; 
    sigemptyset(&act.sa_mask); 

    sigaction(SIGHUP, &act, NULL); 

    int fd = open("testfile", O_WRONLY); 

    char* buf = malloc(1024*1024*128); 

    for (;;) 
    { 
     if (lseek(fd, 0, SEEK_SET) == -1) 
     { 
      printf("lseek failed: %s\n", strerror(errno)); 
     } 
     if (write(fd, buf, sizeof(buf)) != sizeof(buf)) 
     { 
      printf("write failed: %s\n", strerror(errno)); 
     } 
    } 
} 
+0

由於這個'write'並沒有I/O,這是不可能中斷I/O。你只是修改緩存中的頁面。嘗試寫入套接字,寫入NFS服務器,或者使用比RAM更大的寫入大小寫入另一個'mmap'文件的文件。 – 2012-08-07 11:04:54

回答

8

李nux傾向於在寫入/讀取文件時避免EINTR;見discussion here。當進程在磁盤寫入時被阻塞時,它可能被置於uninterruptible sleep狀態(進程代碼D),表示此時不能被中斷。這取決於設備驅動程序; online copy of Linux Device Drivers, 3rd Edition對於從內核方面如何出現這是一個很好的參考。

您仍然需要爲EINTR處理其他可能表現不一樣的平臺,或者確定可能發生EINTR的管道和套接字。

請注意,你一次只能寫sizeof(void *)字節:

char* buf = malloc(1024*1024*128); 

    if (write(fd, buf, sizeof(buf)) != sizeof(buf)) 

這應該是

const size_t BUF_SIZE = 1024*1024*128; 
char* buf = malloc(BUF_SIZE); 

    if (write(fd, buf, BUF_SIZE) != BUF_SIZE) 
+0

如果將其更改爲FIFO,則寫入不會完成,但它不會設置EINTR:輸出看起來像「.write failed:Success \ n」反覆出現。不過,我想我可以在某個時候讓EINTR發生。 – 2012-08-07 11:07:07

+0

有EINTR,你必須在進入'write'調用和第一個字節被放置在內部緩衝區之間的短時間內中斷進程,這是一個非常不可能發生的事件。用shell實現kill循環是非常不可能的。 – 2012-08-07 11:19:24

+0

請注意,除非您正在安裝中斷信號處理程序(即,缺少'SA_RESTART'標誌)或嘗試解決非常舊的Linux版本上的錯誤,否則不需要處理'EINTR'。 – 2012-08-07 13:27:07

0

如果您檢查manual pageEINTR

的呼叫信號中斷任何數據被寫入

而且之前從signal(7) manual page

在「慢」設備上調用read(2),readv(2),write(2),writev(2)和ioctl(2)調用。 「慢速」設備是I/O呼叫可能無限期阻塞的設備,例如終端,管道或插座。 (根據此定義,磁盤不是一個慢速設備。)如果慢速設備上的I/O調用在信號處理程序中斷時已經傳輸了一些數據,則該調用將返回成功狀態(通常,傳輸的字節數)。

綜合這兩者結合起來,如果一個磁盤上寫入文件,並write已經開始編寫(哪怕只有一個單字節已經寫入)從write調用的返回一定會取得成功。

+0

我的代碼檢查數據是否全部寫入,所以它會檢測到這種情況。 – 2012-08-07 11:00:17

+0

@RodrigoQueiro是的,但它仍然不是一個_error_,它的具體原因不會導致'EINTR'。正如cnicutar所指出的那樣,你使用'sizeof'運算符是錯誤的,所以'write'調用只寫入4或8個字節(取決於你是否在32位或64位平臺上)。 – 2012-08-07 11:02:56

5

有以下兩種方式:

  • 你寫了很少的字節,因爲你濫用sizeof操作。因此,write瞬間發生,它永遠不會被中斷 - 你只是在一個時間

  • 不知何故系統調用被重新寫入4首或8個字節,因爲如果應用SA_RESTARTsigaction


在你的代碼,因爲buf是一個指針,sizeof(buf)產生指針的大小你的機器上,而不是(更大)分配空間

+0

看上sizeof()錯誤:我最初在堆棧上有buf,但忘了改變它。然而,解決這個問題不會改變整體行爲(除了讓事情變慢)。 – 2012-08-07 11:02:42