c-POSIX部分写入()和信号中断



来自write()的手册页

请注意,成功的write((传输的字节数可能少于count。这种部分写入可能由于各种原因而发生;例如因为磁盘设备上没有足够的空间写入所有请求的字节数,或者因为阻止了对套接字的write((,管道或类似程序在转移了一些,但在转移所有请求之前字节。在部分写入的情况下,调用者可以进行另一次写入write((调用来传输剩余的字节。后续呼叫将进一步传输字节或者可能导致错误(例如。,如果磁盘现在已满(。

我有以下问题

1( 如果write()在部分传输后被信号处理程序中断,write((会将errno设置为EINTR吗?

2( 如果没有设置errno,有没有办法在没有额外代码的情况下识别这样的事件(比如安装信号处理并将标志值设置为true(?

注意:信号中断事件后,进一步的write()调用成功传输剩余字节。

要回答您的个人编号问题:

  1. errno只有在其中一个标准函数返回一个指示错误的值(对于write,-1(之后,以及在调用任何其他可能破坏它的标准函数或应用程序代码之前才有意义。所以不,如果write返回一个短写,errno将不会被设置为任何有意义的内容。如果它等于EINTR,那么它恰好是;这不是你能理解的有意义的东西。

  2. 识别此类事件的方法是返回值严格小于nbytes参数。这实际上并没有告诉你短写的原因,所以它可能是其他原因,比如空间不足。如果你需要知道,你需要安排信号处理程序通知你。但在几乎所有情况下,你实际上并不需要知道。

关于注释,如果write在信号到达后返回完整的nbytes,则信号处理程序为非中断。这是Linux上任何现代libc(glibc、musl,基本上除了libc5之外的任何东西(的默认设置,而且它几乎总是正确的。如果你真的想要中断信号,你必须安装带有sigaction和清除SA_RESTART标志的信号处理程序。(相反,如果您正在安装信号处理程序,您希望具有正常、合理、不中断的行为,为了可移植性,您应该使用sigaction并设置SA_RESTART标志,而不是使用传统函数signal(。

让我们试试看:

#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
static void handle_sigalrm(int sig) {
}
int main(void) {
struct sigaction act;
memset(&act, 0, sizeof act);
act.sa_handler = handle_sigalrm;
sigaction(SIGALRM, &act, NULL);
int fds[2];
pipe(fds);
int bufsize = fcntl(fds[1], F_GETPIPE_SZ) + 10;
char *buf = calloc(bufsize, 1);
ssize_t written;
printf("will attempt to write %d bytes and EINTR is %dn", bufsize, EINTR);
alarm(1);
errno = 0;
written = write(fds[1], buf, bufsize);
printf("write returned %td and errno is %dn", written, errno);
return 0;
}

该程序生成一个任何东西都无法读取的管道,对其进行比内核缓冲区大的写入,并安排一个信号处理程序在写入阻塞时运行。在我的系统上,它会打印以下内容:

will attempt to write 65546 bytes and EINTR is 4
write returned 65536 and errno is 0

因此,"在部分传输后,write((被信号处理程序中断的情况下,是否会将errno设置为EINTR?"的答案是"不会,不会"。

最新更新