popen()/fgets() 间歇性返回不完整的输出

2024-02-11

我遇到了一个奇怪的问题popen and fgetsLinux系统上的库函数。

演示该问题的简短程序如下:

  1. 安装信号处理程序SIGUSR1.
  2. 创建辅助线程来重复发送SIGUSR1到主线程。
  3. 在主线程中,通过以下方式重复执行一个非常简单的 shell 命令popen(),通过获取输出fgets(),并检查输出是否符合预期长度。

输出意外地间歇性被截断。为什么?

命令行调用示例:

$ gcc -Wall test.c -lpthread && ./a.out 
iteration 0
iteration 1
iteration 2
iteration 3
iteration 4
iteration 5
unexpected length: 0

我的机器的详细信息(该程序也将编译并运行这个在线C编译器 http://coliru.stacked-crooked.com/):

$ cat /etc/redhat-release
CentOS release 6.5 (Final)

$ uname -a
Linux localhost.localdomain 2.6.32-431.17.1.el6.x86_64 #1 SMP Wed May 7 23:32:49 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

# gcc 4.4.7
$ gcc --version
gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-4)
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

# glibc 2.12
$ ldd --version
ldd (GNU libc) 2.12
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.

该程序:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <errno.h>

void dummy_signal_handler(int signal);
void* signal_spam_task(void* arg);
void echo_and_verify_output();
char* fgets_with_retry(char *buffer, int size, FILE *stream);

static pthread_t main_thread;

/**
 * Prints an error message and exits if the output is truncated, which happens
 * about 5% of the time.
 *
 * Installing the signal handler with the SA_RESTART flag, blocking SIGUSR1
 * during the call to fgets(), or sleeping for a few milliseconds after the
 * call to popen() will completely prevent truncation.
 */
int main(int argc, char **argv) {

    // install signal handler for SIGUSR1
    struct sigaction sa, osa;
    sa.sa_handler = dummy_signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGUSR1, &sa, &osa);

    // create a secondary thread to repeatedly send SIGUSR1 to main thread
    main_thread = pthread_self();
    pthread_t spam_thread;
    pthread_create(&spam_thread, NULL, signal_spam_task, NULL);

    // repeatedly execute simple shell command until output is unexpected
    unsigned int i = 0;
    for (;;) {
        printf("iteration %u\n", i++);
        echo_and_verify_output();
    }

    return 0;
}

void dummy_signal_handler(int signal) {}

void* signal_spam_task(void* arg) {
    for (;;)
        pthread_kill(main_thread, SIGUSR1);
    return NULL;
}

void echo_and_verify_output() {

    // run simple command
    FILE* stream = popen("echo -n hello", "r");
    if (!stream)
        exit(1);

    // count the number of characters in the output
    unsigned int length = 0;
    char buffer[BUFSIZ];
       while (fgets_with_retry(buffer, BUFSIZ, stream) != NULL)
        length += strlen(buffer);

    if (ferror(stream) || pclose(stream))
        exit(1);

    // double-check the output
    if (length != strlen("hello")) {
        printf("unexpected length: %i\n", length);
        exit(2);
    }
}

// version of fgets() that retries on EINTR
char* fgets_with_retry(char *buffer, int size, FILE *stream) {
    for (;;) {
        if (fgets(buffer, size, stream))
            return buffer;
        if (feof(stream))
            return NULL;
        if (errno != EINTR)
            exit(1);
        clearerr(stream);
    }
}

如果错误发生在FILE阅读时流式传输fgets,之前读取的某些字节是否传输到缓冲区是未定义的fgets是否返回 NULL(C99 规范的 7.19.7.2)。因此,如果 SIGUSR1 信号发生在fgets调用并导致EINTR,某些字符可能会从流中丢失。

结果是你不能使用 stdio 函数来读/写FILE如果底层系统调用可能有可恢复的错误返回(例如EINTR or EAGAIN),因为无法保证标准库在发生这种情况时不会丢失缓冲区中的一些数据。您可以声称这是标准库实现中的一个“错误”,但这是 C 标准允许的错误。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

popen()/fgets() 间歇性返回不完整的输出 的相关文章

  • 如何在 Android NDK 中创建新的 NativeWindow 而无需 Android 操作系统源代码?

    我想编译一个 Android OpenGL 控制台应用程序 您可以直接从控制台启动 Android x86 运行 或者从 Android x86 GUI 内的 Android 终端应用程序运行 这个帖子 如何在 Android NDK 中创
  • C# 中的接口继承

    我试图解决我在编写应用程序时遇到的相当大的 对我来说 问题 请看这个 为了简单起见 我将尝试缩短代码 我有一个名为的根接口IRepository
  • 构造函数中显式关键字的使用

    我试图了解 C 中显式关键字的用法 并查看了这个问题C 中的explicit关键字是什么意思 https stackoverflow com questions 121162 但是 那里列出的示例 实际上是前两个答案 对于用法并不是很清楚
  • 对齐 GridView 中的行值

    我需要在 asp net 3 5 中右对齐 gridview 列中的值 我怎样才能做到这一点
  • JSON 数组到 C# 列表

    如何将这个简单的 JSON 字符串反序列化为 C 中的列表 on4ThnU7 n71YZYVKD CVfSpM2W 10kQotV 这样 List
  • 如何使用recv()检测客户端是否仍然连接(并且没有挂起)?

    我写了一个多客户端服务器程序C on SuSE Linux 企业服务器 12 3 x86 64 我为每个客户端使用一个线程来接收数据 我的问题是 我使用一个终端来运行服务器 并使用其他几个终端来运行服务器telnet到我的服务器 作为客户端
  • 从多个类访问串行端口

    我正在尝试使用串行端口在 arduino 和 C 程序之间进行通信 我对 C 编程有点陌生 该程序有多种用户控制形式 每一个都需要访问串口来发送数据 我需要做的就是从每个类的主窗体中写入串行端口 我了解如何设置和写入串行端口 这是我的 Fo
  • 检查算术运算中的溢出情况[重复]

    这个问题在这里已经有答案了 可能的重复 检测 C C 中整数溢出的最佳方法 https stackoverflow com questions 199333 best way to detect integer overflow in c
  • 在脚本内使用不带密码的 sudo

    由于某种原因 我需要作为用户在没有 sudo 的情况下运行脚本 script sh 该脚本需要 root 权限才能工作 我认为将 sudo 放入 script sh 中是唯一的解决方案 让我们举个例子 script sh bin sh su
  • Linux 上有关 getBounds() 和 setBounds() 的 bug_id=4806603 的解决方法?

    在 Linux 平台上 Frame getBounds 和 Frame setBounds 的工作方式不一致 这在 2003 年就已经有报道了 请参见此处 http bugs java com bugdatabase view bug do
  • 为什么我不应该对不是由 malloc() 分配的变量调用 free() ?

    我在某处读到 使用它是灾难性的free删除不是通过调用创建的对象malloc 这是真的 为什么 这是未定义的行为 永远不要尝试它 让我们看看当您尝试时会发生什么free 自动变量 堆管理器必须推断出如何获取内存块的所有权 为此 它要么必须使
  • 当模板类不包含可用的成员函数时,如何在编译时验证模板参数?

    我有以下模板struct template
  • 如何挤出平面 2D 网格并赋予其深度

    我有一组共面 连接的三角形 即二维网格 现在我需要将其在 z 轴上挤出几个单位 网格由一组顶点定义 渲染器通过与三角形数组匹配来理解这些顶点 网格示例 顶点 0 0 0 10 0 0 10 10 0 0 10 0 所以这里我们有一个二维正方
  • 如何一步步遍历目录树?

    我发现了很多关于遍历目录树的示例 但我需要一些不同的东西 我需要一个带有某种方法的类 每次调用都会从目录返回一个文件 并逐渐遍历目录树 请问我该怎么做 我正在使用函数 FindFirstFile FindNextFile 和 FindClo
  • 为什么拆箱枚举会产生奇怪的结果?

    考虑以下 Object box 5 int int int box int 5 int nullableInt box as int nullableInt 5 StringComparison enum StringComparison
  • 转到定义:“无法导航到插入符号下的符号。”

    这个问题的答案是社区努力 help privileges edit community wiki 编辑现有答案以改进这篇文章 目前不接受新的答案或互动 我今天突然开始在我的项目中遇到一个问题 单击 转到定义 会出现一个奇怪的错误 无法导航到
  • 运算符“==”不能应用于“int”和“string”类型的操作数

    我正在编写一个程序 我想到了一个数字 然后计算机猜测了它 我一边尝试一边测试它 但我不断收到不应该出现的错误 错误是主题标题 我使用 Int Parse 来转换我的字符串 但我不知道为什么会收到错误 我知道它说 不能与整数一起使用 但我在网
  • WinRT 定时注销

    我正在开发一个 WinRT 应用程序 要求之一是应用程序应具有 定时注销 功能 这意味着在任何屏幕上 如果应用程序空闲了 10 分钟 应用程序应该注销并导航回主屏幕 显然 执行此操作的强力方法是在每个页面的每个网格上连接指针按下事件 并在触
  • 用于 C# XNA 的 Javascript(或类似)游戏脚本

    最近我准备用 XNA C 开发另一个游戏 上次我在 XNA C 中开发游戏时 遇到了必须向游戏中添加地图和可自定义数据的问题 每次我想添加新内容或更改游戏角色的某些值或其他内容时 我都必须重建整个游戏或其他内容 这可能需要相当长的时间 有没
  • 匿名结构体作为返回类型

    下面的代码编译得很好VC 19 00 23506 http rextester com GMUP11493 标志 Wall WX Za 与VC 19 10 25109 0 标志 Wall WX Za permissive 这可以在以下位置检

随机推荐

  • 为什么我无法为对话框视图中的按钮设置 onClickListener?

    我有一个自定义对话框 如下所述 我的自定义对话框布局 my dialog xml 其中只包含一个 关闭 按钮
  • CSS动画闪烁,尝试了我能找到的所有技巧

    我正在 Codepen 中制作一个简单的动画 诗淡入 然后单词 也是按钮 淡入 用户单击单词 它会更改为诗的下一部分 我的问题是 在淡出开始之前 诗歌和单个单词会闪烁 我已经尝试了所有我能找到的技巧 并添加 webkit backface
  • Heroku pg:psql 停止运行

    当我跑步时heroku pg psql i get gt Connecting to postgresql cylindrical 38664 并熄火 一切看起来都很顺利 但它只是卡住了 什么也没做 我似乎在 gitbash 中遇到了同样的
  • 在 perl 中读取和写入文件

    this is just an example 假设上面是out txt 我想读书out txt并写入同一个文件
  • 在 Android 中从纬度/经度获取企业名称或地标

    好吧 我已经为此搜索了相当长的时间 我有一个距我新发现的位置的纬度 经度 geocoder 的 getFromLocation 从纬度 经度返回一定数量的地址 这一切都很好 然后我将它放入一个适配器中 该适配器填充了一个旋转器 也运行得很好
  • UIButton 在单元格被触摸时也会突出显示

    I ve a UIButton在我弹出的表格单元格上 touchUpInside a UIAlertView并询问用户是否要删除与该单元格关联的文件 否则 触摸单元格本身会突出显示该单元格 然后移动到下一个级别以显示内容 问题是当细胞sel
  • 如何在一定时间后重试功能请求

    如果用户数据为空 如何让它重试发送尝试 最多重试 2 次 10 秒后重试 1 次 public class UserHandler private List users new ArrayList public void addUser u
  • 绕过 requiredfieldvalidator

    我有一个网络表单 上面有验证器 当用户按下提交按钮时 验证器可以很好地工作 但是 当用户按下注销按钮时 验证器会停止该按钮的工作 关于如何解决这个问题有什么建议吗 您需要使用CausesValidation按钮上的属性
  • nf_conntrack_helper注册未注册端口返回错误

    我有以下代码从内核 3 18 取消注册和注册 sip conntrack static void nf conntrack sip fini void int i j for i 0 i lt ports c i for j 0 j lt
  • 在没有 gitosis/gitolite 的情况下通过 SSH 运行“安全”git 服务器?

    是否可以通过 ssh 运行 git 服务器 使用authorized keys and command 限制仅访问 git 存储库 而不使用 gitosis gitolite 是的 分配git shell http www kernel o
  • 获取 Youtube 数据 API 的 403 禁止错误

    我在对 YouTube 数据 API 进行 API 调用时收到 403 禁止错误 我尝试生成不同类型的密钥 Web 浏览器 服务器等 钥匙不受限制 我尝试从服务器和 Chrome 的邮递员拨打电话 请求 URL 和响应如下 https ww
  • 访问 AVRO GenericRecord (Java/Scala) 中的嵌套字段

    我有一个带有嵌套字段的 GenericRecord 当我使用genericRecord get 1 它返回一个包含嵌套 AVRO 数据的对象 我希望能够像这样访问该对象genericRecord get 1 get 0 但我不能 因为 AV
  • 在 woocommerce 中隐藏折扣信息而不取消优惠券

    我在 Woocommerce 购物车中使用优惠券进行计算 它会自动为总额添加折扣 以便可以将正确的金额发送到支付网关 我想向访客隐藏有关此优惠券 折扣的所有信息 Problem 我发现的唯一方法 见下文 隐藏优惠券字段 行 总计 和消息 但
  • COBOL:GDG 文件描述符 (FD) 可以引用多代吗?

    我有一个程序可以读取 GDG 文件并将数据移动到工作存储 我很想知道是否可以使用对文件定义的引用对多代 GDG 重复此过程 也许有一种方法可以在文件定义上使用下标 我的想法是必须有一种方法将不同的文件定义移动到引用变量中以访问文件 基于建议
  • 如何检测设备是否支持鼠标?

    我目前使用以下测试 取自 Modernizr 来检测触摸支持 function is touch device var bool if ontouchstart in window window DocumentTouch document
  • 我的树形图的 Arangodb 自定义过滤器/访问者

    我有一个带有两个边定义的图 如下所示 isDepartment organisation gt organisation hasAccess user gt organisation 组织嵌套在树中 无循环 有多个顶级组织 没有任何传入is
  • 使用 shell 脚本从 sql 脚本中选择列到局部变量中

    如何将从表中检索到的列值存储到 shell 脚本中的变量中 我有以下代码 usr bin ksh echo This script will try to connect to sql plus and displays the date
  • 为什么在释放指针后取消引用它时会得到不同的结果?

    我有一个关于 C 内存管理的问题 以及 Debian GNU Linux 下的 GCC 4 3 3 根据 K R 的 C 编程语言书籍 第 7 8 5 章 当我释放指针然后取消引用它时 会出现错误 但我有一些疑问 因为我注意到有时 正如我在
  • 在 Flask 中,设置 cookie,然后重定向用户

    看起来在 Flask 中 cookie 是通过直接修改响应对象来设置的 如何返回响应对象 同时在成功登录后将用户重定向到不同的页面 我想专门重定向用户而不是呈现不同的页面 以防用户点击刷新 这是我当前的代码 它仅显示同一页面 login h
  • popen()/fgets() 间歇性返回不完整的输出

    我遇到了一个奇怪的问题popen and fgetsLinux系统上的库函数 演示该问题的简短程序如下 安装信号处理程序SIGUSR1 创建辅助线程来重复发送SIGUSR1到主线程 在主线程中 通过以下方式重复执行一个非常简单的 shell