4

我一直在阅读EINTRwrite(2),并试图确定是否需要在我的程序中检查它。作为健全性检查,我尝试编写一个会运行它的程序。程序永远循环,反复写入文件。

然后,在一个单独的 shell 中,我运行:

while true; do pkill -HUP test; done

但是,我从 test.c 看到的唯一输出是.来自信号处理程序的 s。为什么不是SIGHUP导致write(2)失败的原因?

测试.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));
        }
    }
}
4

3 回答 3

10

Linux 倾向于避免EINTR写入/读取文件;请参阅此处的讨论。当进程在磁盘写入时阻塞时,它可能会处于不可中断的睡眠状态(进程代码D),这表明它当时不能被中断。这取决于设备驱动程序;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)
于 2012-08-07T10:58:31.573 回答
5

有2种可能:

  • 你写的字节很少,因为你误用了sizeofoperator。因此,这种write情况会立即发生并且永远不会被打断——你一次只写 4 或 8 个字节

  • 系统调用以某种方式重新启动,就好像您申请SA_RESTARTsigaction


在您的代码中,因为buf是一个指针,所以会sizeof(buf)产生您机器上指针的大小,而不是(更大的)分配空间

于 2012-08-07T10:57:31.923 回答
1

如果您查看手册EINTR

在写入任何数据之前调用被信号中断

同样来自信号(7)手册页

read(2)、readv(2)、write(2)、writev(2) 和 ioctl(2) 在“慢”设备上调用。“慢”设备是 I/O 调用可能无限期阻塞的设备,例如终端、管道或套接字。(根据此定义,磁盘不是慢速设备。)如果慢速设备上的 I/O 调用在被信号处理程序中断时已经传输了一些数据,则调用将返回成功状态(通常,传输的字节数)。

将这两者结合起来,如果写入磁盘上的文件,并且write已经开始写入(即使只写入了一个字节),则该write调用的返回将是成功的。

于 2012-08-07T10:57:01.863 回答