- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
fgets()
用于读取一些字符串,直到 EOF
或 \n
发生。例如,读取文本配置文件非常方便,但也存在一些问题。
首先,它可能会在信号传递的情况下返回 EINTR
,因此应该用循环检查来包装它。
第二个问题更糟糕:至少在 glibc 中,它将返回 EINTR
并丢失所有已读取的数据,以防它在中间传递。这不太可能发生,但我认为这可能是某些守护进程中一些复杂漏洞的来源。
在信号上设置 SA_RESTART
标志似乎有助于避免这个问题,但我不确定它涵盖了所有平台上的所有可能情况。是吗?
如果不是,有没有办法完全避免这个问题?
如果不是,似乎 fgets()
不能用于在守护进程中读取文件,因为它可能导致随机数据丢失。
测试示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
static char buf[1000000];
static volatile int do_exit = 0;
static void int_sig_handle(int signum) { do_exit = 1; }
void try(void) {
char * r;
int err1, err2;
size_t len;
memset(buf,1,20); buf[20]=0;
r = fgets(buf, sizeof(buf), stdin);
if(!r) {
err1 = errno;
err2 = ferror(stdin);
printf("\n\nfgets()=NULL, errno=%d(%s), ferror()=%d\n", err1, strerror(err1), err2);
len = strlen(buf);
printf("strlen()=%u, buf=[[[%s]]]\n", (unsigned)len, buf);
} else if(r==buf) {
err1 = errno;
err2 = ferror(stdin);
len = strlen(buf);
if(!len) {
printf("\n\nfgets()=buf, strlen()=0, errno=%d(%s), ferror()=%d\n", err1, strerror(err1), err2);
} else {
printf("\n\nfgets()=buf, strlen()=%u, [len-1]=0x%02X, errno=%d(%s), ferror()=%d\n",
(unsigned)len, (unsigned char)(buf[len-1]), err1, strerror(err1), err2);
}
} else {
printf("\n\nerr\n");
}
}
int main(int argc, char * * argv) {
struct sigaction sa;
sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sa.sa_handler = int_sig_handle;
sigaction(SIGINT, &sa, NULL);
printf("attempt 1\n");
try();
printf("\nattempt 2\n");
try();
printf("\nend\n");
return 0;
}
此代码可用于测试“尝试 1”中间的信号传递,并确保其部分读取的数据在此之后完全丢失。
如何测试:
read()
系统调用完成了一些数据SIGINT
fread()
返回 NULL
,“attempt 2”并输入一些数据并按 Enter 键FreeBSD 11 libc:相同的行为
FreeBSD 8 libc:第一次尝试返回部分读取的数据并设置 ferror() 和 errno
编辑:根据@John Bollinger 的建议,我在 NULL 返回后添加了缓冲区转储。结果:
glibc 和 FreeBSD 11 libc:缓冲区包含部分读取的数据但不是 NULL-TERM,因此获取其长度的唯一方法是在调用 fgets() 之前清除整个缓冲区,这看起来不像预期的用途
FreeBSD 8 libc:仍然返回正确的空终止部分读取数据
最佳答案
对于中断信号处理程序,stdio 确实不能合理地使用。
根据 ISO C 11 7.21.7.2 fgets 函数,第 3 段:
The fgets function returns s if successful. If end-of-file is encountered and no characters have been read into the array, the contents of the array remain unchanged and a null pointer is returned. If a read error occurs during the operation, the array contents are indeterminate and a null pointer is returned.
EINTR
是一个读取错误,因此返回后数组内容不确定。
理论上,可以为 fgets
指定行为,您可以通过在操作之前适本地设置缓冲区来有意义地从操作中间的错误中恢复调用,因为您知道 fgets
不会写入 '\n'
除非作为空终止前的最后一个字符(类似于使用 fgets
的技术> 带有嵌入式 NUL)。但是,它没有那样指定,并且没有类似的方法来处理其他 stdio 函数,例如 scanf
,它们无处存储状态以在 EINTR
之后恢复它们。
实际上,信号只是一种非常落后的做事方式,而中断信号是一种更加落后的工具,充满了竞争条件和其他令人不快且无法修复的极端情况。如果你想以一种安全和现代的方式做这种事情,你可能需要一个通过管道或套接字转发标准输入的线程,并关闭信号处理程序中管道或套接字的写入端,以便主要从中读取的部分程序会收到 EOF。
关于c - fgets()、信号 (EINTR) 和输入数据完整性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56414520/
许多 POSIX 阻塞函数在有信号的情况下返回 EINTR。这个想法是信号处理程序首先设置一个标志(在 SIGINT 的情况下说“停止”标志),然后阻塞函数取消阻塞返回 EINTR,并且应用程序看到该
如果读取大量数据,是否需要检查errno == EINTR?我使用 pread() 函数来读取。在我的一生中,我从未见过 EINTR 返回,但我在网上看到了一些代码,其中明确地检查了它。 那么真的有必
tl;博士 在 Python 中读取管道时,我是否应该处理 EINTR“系统调用中断”错误?如果是,我该如何测试此类代码? 描述 在下面的回溯中,self._dataq 是一个 multiproces
我正在运行一个 epoll 循环,有时我对 epoll_wait 的调用返回 -1,errno 设置为 EINTR。有时,我希望这样可以结束 epoll 循环,例如 SIGTERM 或 SIGINT。
简而言之,您如何对系统调用中的 EINTR 等错误条件进行单元测试。 我正在处理的一个特定示例(它本身可能就是一个案例)是当 fclose 返回带有 (errno==EINTR) 的 EOF 时是否有
我看到了处理 EINTR 错误的代码(读、写系统调用)。我了解中断发生在两种情况下。首先,它发生在调度的每个时间片上。其次,它是通过信号发生的。但是 stackoverflow 中的每个人都说只是信号
关闭。这个问题是not reproducible or was caused by typos .它目前不接受答案。 这个问题是由于错别字或无法再重现的问题引起的。虽然类似的问题可能是on-topi
在我的程序中,我有一个系统调用“sendmsg()”。我想测试如果这个系统调用被中断会发生什么。我应该怎么做? int test(args) { ----- /
我有一个线程负责轮询各种fds。我正在使用设置了超时的 epoll_wait。以下是代码片段: do { n = epoll_wait(epollFd, eventsList, eventsT
我研究发现用socket编程时会出现信号中断的情况。我搜索了一下,发现如果信号中断,我们应该重试。也就是说,我必须捕获错误并重试。我必须像这样创建套接字。 int create_sock() {
有没有专家可以帮助我解决以下问题? 我在 C 中有以下系统调用: access() unlink() setsockopt() fcntl() setsid() socket() bind() lis
来自 man -e 2 wait : The call wait(&status) is equivalent to: waitpid(-1, &status, 0); 错误号值: EINTR WNO
使用 timer_create,我们向等待 select 函数的线程发送实时信号。 这个信号在线程中被捕获和处理。基于事实,当捕获到信号时,选择将被中断,如果 select 失败并显示错误号 EINT
我发现这段代码被使用了好几次(还有一个类似的代码,它使用 open() 而不是 write())。 int c = write(fd, &v, sizeof(v)); if (c == -1 && e
我正在为嵌入式 Linux 系统编写用户应用程序,并且我正在为设备使用诸如打开、关闭、读取、ioctl 等常用功能。现在,我阅读了有关 EINTR 的信息,表明该函数被信号中断,但我不确定其含义。在我
有一些系统调用支持的 I/O 函数(我最近发现至少两个:stat() 和 mkdir() 但几乎可以肯定还有更多)根据 POSIX,可能的 errno 值中没有 EINTR,但肯定可能由于磁盘繁忙或网
在规范和两个实现中: 根据 POSIX,dup2()可能返回 EINTR。 linux 手册页将其列为允许的。 FreeBSD 手册页表明它从未返回。这是一个错误吗 - 因为它的紧密实现可以 EINT
到目前为止我的网络代码工作正常,但我有点担心我隐藏在地毯下的东西: accept、close、connect、recv 和 send 的手册页> 提到 errno.EINTR 可以在系统调用被信号中断
注意: This question与我的相当接近,但我至少可以对提供的解决方案使用一些工作示例,也许 ZeroMQ 会带来一些我不知道的魔力。 目前,我在阻止 ZeroMQ 调用时对异常使用react
我是信号量的新手,想在我的程序中添加多线程,但我无法解决以下问题:只要我没有设置 SA_RESTART,sem_wait() 应该能够接收 EINTR 并解锁旗帜。我正在向在 sem_wait() 中
我是一名优秀的程序员,十分优秀!