POSIX 部分 write() 和信号中断
POSIX partial write() and Signal Interrupts
来自 write()
的手册页
Note that a successful write() may transfer fewer than count bytes.
Such partial writes can occur for various reasons; for example,
because there was insufficient space on the disk device to write all
of the requested bytes, or because a blocked write() to a socket,
pipe, or similar was interrupted by a signal handler after it had
transferred some, but before it had transferred all of the requested
bytes. In the event of a partial write, the caller can make another
write() call to transfer the remaining bytes. The subsequent call
will either transfer further bytes or may result in an error (e.g.,
if the disk is now full).
我有以下问题
1) 在部分传输后 write()
被信号处理程序中断的情况下, write() 会将 errno
设置为 EINTR
?
2) 如果未设置 errno
,是否可以通过额外的代码来识别此类事件(例如安装信号处理并将标志值设置为 true
)?
注意:进一步的write()
调用在信号中断事件后成功传输剩余字节。
回答您的个人编号问题:
errno
仅在标准函数之一之后才有意义 returns 一个指示错误的值 - 对于 write
, -1 - 并且在任何其他标准之前调用可能会破坏它的函数或应用程序代码。所以不,如果 write
return 是一个短写,errno
将不会被设置为任何有意义的东西。如果等于EINTR
,正好是;这不是你可以解释的有意义的东西。
识别此类事件的方法是 return 值严格小于 nbytes
参数。这实际上并没有告诉您短写的 原因 ,所以它可能是 space 中的 运行 之类的东西。如果你需要知道,你需要安排信号处理程序通知你。但在几乎所有情况下,您实际上都不需要知道。
关于注释,如果 write
在信号到达后 return 完整 nbytes
,则信号处理程序是 非中断的 .这是任何现代 libc(glibc、musl,基本上除了 libc5 之外的任何东西)在 Linux 上的默认值,而且它几乎总是正确的。如果你真的想要中断信号,你必须安装带有 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 %d\n", bufsize, EINTR);
alarm(1);
errno = 0;
written = write(fds[1], buf, bufsize);
printf("write returned %td and errno is %d\n", written, errno);
return 0;
}
该程序创建一个永远不会读取任何内容的管道,向其写入大于内核缓冲区的数据,并在写入阻塞时安排信号处理程序 运行。在我的系统上,它打印如下:
will attempt to write 65546 bytes and EINTR is 4
write returned 65536 and errno is 0
因此,"In the case of write() being interrupted by signal handler after a partial transfer, will write() set the errno to EINTR?"的答案是"no, it won't"。
来自 write()
Note that a successful write() may transfer fewer than count bytes. Such partial writes can occur for various reasons; for example, because there was insufficient space on the disk device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or similar was interrupted by a signal handler after it had transferred some, but before it had transferred all of the requested bytes. In the event of a partial write, the caller can make another write() call to transfer the remaining bytes. The subsequent call will either transfer further bytes or may result in an error (e.g., if the disk is now full).
我有以下问题
1) 在部分传输后 write()
被信号处理程序中断的情况下, write() 会将 errno
设置为 EINTR
?
2) 如果未设置 errno
,是否可以通过额外的代码来识别此类事件(例如安装信号处理并将标志值设置为 true
)?
注意:进一步的write()
调用在信号中断事件后成功传输剩余字节。
回答您的个人编号问题:
errno
仅在标准函数之一之后才有意义 returns 一个指示错误的值 - 对于write
, -1 - 并且在任何其他标准之前调用可能会破坏它的函数或应用程序代码。所以不,如果write
return 是一个短写,errno
将不会被设置为任何有意义的东西。如果等于EINTR
,正好是;这不是你可以解释的有意义的东西。识别此类事件的方法是 return 值严格小于
nbytes
参数。这实际上并没有告诉您短写的 原因 ,所以它可能是 space 中的 运行 之类的东西。如果你需要知道,你需要安排信号处理程序通知你。但在几乎所有情况下,您实际上都不需要知道。
关于注释,如果 write
在信号到达后 return 完整 nbytes
,则信号处理程序是 非中断的 .这是任何现代 libc(glibc、musl,基本上除了 libc5 之外的任何东西)在 Linux 上的默认值,而且它几乎总是正确的。如果你真的想要中断信号,你必须安装带有 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 %d\n", bufsize, EINTR);
alarm(1);
errno = 0;
written = write(fds[1], buf, bufsize);
printf("write returned %td and errno is %d\n", written, errno);
return 0;
}
该程序创建一个永远不会读取任何内容的管道,向其写入大于内核缓冲区的数据,并在写入阻塞时安排信号处理程序 运行。在我的系统上,它打印如下:
will attempt to write 65546 bytes and EINTR is 4
write returned 65536 and errno is 0
因此,"In the case of write() being interrupted by signal handler after a partial transfer, will write() set the errno to EINTR?"的答案是"no, it won't"。