当使用 fork() 时,getline() 会重复读取文件

2024-03-15

我正在开发一个简单的 shell 程序,一个命令行解释器,我想从文件中逐行读取输入,所以我使用了 getline() 函数。第一次,程序工作正常,但是,当它到达文件末尾时,它没有终止,而是开始从头开始读取文件,并且无限运行。 下面是 main 函数中与 getline() 相关的一些代码:

int main(int argc,char *argv[]){
    int const IN_SIZE = 255;
    char *input = NULL;
    size_t len = IN_SIZE;
    // get file address
    fileAdr = argv[2];

    // open file
    srcFile = fopen(fileAdr, "r");

    if (srcFile == NULL) {
        printf("No such file!\n");
        exit(-1);
    }

    while (getline( &input, &len, srcFile) != -1) {
        strtok(input, "\n");
        printf("%s\n", input);
        // some code that parses input, firstArgs == input
        execSimpleCmd(firstArgs);            
    }
    fclose(srcFile);
}

我在程序中使用 fork() ,很可能它会导致这个问题。

void execSimpleCmd(char **cmdAndArgs) {

    pid_t pid = fork();
    if (pid < 0) {
        // error
        fprintf(stderr, "Fork Failed");
        exit(-1);
    } else if (pid == 0) {
        // child process
        if (execvp(cmdAndArgs[0], cmdAndArgs) < 0) {
            printf("There is no such command!\n");
        }
        exit(0);
    } else {
        // parent process
        wait(NULL);
        return;
    }
}

此外,有时程序会读取并打印多行的组合。例如,如果输入文件如下:

ping
ww    
ls
ls -l
pwd

它打印诸如 pwdg、pwdww 等内容。如何修复它?


看来关闭一个FILE在某些情况下,会将底层文件描述符返回到应用程序实际读取的位置,从而有效地消除读取缓冲的影响。这很重要,因为父级和子级的操作系统级别文件描述符指向相同的文件描述,特别是相同的文件偏移量。

The POSIX 描述fclose() http://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/fclose.html有这样一句话:

[CX] [Option Start] 如果文件尚未位于 EOF,并且该文件能够查找,底层打开文件描述的文件偏移量应设置为流的文件位置如果流是底层文件描述的活动句柄。

(Where CX 表示 ISO C 标准的扩展 http://pubs.opengroup.org/onlinepubs/9699919799.2018edition/help/codes.html#CX, and exit()当然运行fclose()在所有流上。)

我可以用这个程序重现奇怪的行为(在 Debian 9.8 上):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char *argv[]){
    FILE *f;
    if ((f = fopen("testfile", "r")) == NULL) {
        perror("fopen");
        exit(1);
    }

    int right = 0;
    if (argc > 1)
        right = 1;

    char *line = NULL;
    size_t len = 0;
    // first line 
    getline(&line, &len, f);
    printf("%s", line);

    pid_t p = fork();
    if (p == -1) {
        perror("fork");
    } else if (p == 0) {
        if (right)
            _exit(0);  // exit the child 
        else
            exit(0);   // wrong way to exit
    } else {
        wait(NULL);  // parent
    }

    // rest of the lines
    while (getline(&line, &len, f) > 0) {
        printf("%s", line);
    }

    fclose(f);
}

Then:

$ printf 'a\nb\nc\n' > testfile
$ gcc -Wall -o getline getline.c
$ ./get
getline   getline2  
$ ./getline
a
b
c
b
c

运行它strace -f ./getline清楚地显示孩子正在寻找文件描述符:

clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f63794e0710) = 25117
strace: Process 25117 attached
[pid 25116] wait4(-1,  <unfinished ...>
[pid 25117] lseek(3, -4, SEEK_CUR)      = 2
[pid 25117] exit_group(1)               = ?

(我没有看到使用不涉及分叉的代码进行寻回,但我不知道为什么。)

因此,主程序上的 C 库从文件中读取数据块,然后应用程序打印第一行。 fork之后,子进程退出,并查找fd回到应用程序级文件指针所在的位置。然后父级继续,处理读取缓冲区的其余部分,完成后,它继续从文件中读取。由于已查找文件描述符,因此从第二行开始的行再次可用。

在你的情况下,重复的fork()每次迭代似乎都会导致无限循环。

Using _exit()代替exit()在孩子身上解决了问题在这种情况下, since _exit()仅退出进程,它不会对 stdio 缓冲区执行任何操作。

With _exit(),任何输出缓冲区也不会被刷新,所以你需要调用fflush()手动开启stdout以及您正在写入的任何其他文件。

但是,如果您以相反的方式执行此操作,即子级读取和缓冲的内容多于其处理的内容,那么子级查找 fd 将很有用,以便父级可以从子级实际离开的位置继续。

另一个解决方案是不混合stdio with fork().

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

当使用 fork() 时,getline() 会重复读取文件 的相关文章

  • 何时使用 =default 使析构函数默认?

    尽管对构造函数使用 default 对我来说很清楚 即强制编译器在其他构造函数存在时创建默认构造函数 但我仍然无法理解这两种类型的析构函数之间的区别 那些使用 default 的 那些没有显式定义并由编译器自动生成的 我唯一想到的是 gro
  • FileStream 构造函数和默认缓冲区大小

    我们有一个使用 NET 4 用 C 编写的日志记录类 我想添加一个构造函数参数 该参数可以选择设置文件选项 WriteThrough http msdn microsoft com en us library system io fileo
  • 为什么在创建矩阵类时使用向量不好?

    对于我的矩阵类 我做了 template
  • SONAR - 使用 Cobertura 测量代码覆盖率

    我正在使用声纳来测量代码质量 我不知道的一件事是使用 Cobertura 测量代码覆盖率的步骤 我按照以下步骤操作http cobertura sourceforge net anttaskreference html http cober
  • 根据 N 个值中最小的一个返回不同的结果

    不确定如何使标题更具描述性 所以我只是从一个例子开始 我使用下面的代码位 它从枚举中选择一个方向 具体取决于四个轴中哪一个与给定方向相比形成最小角度 static Direction VectorToDirection Vector2 di
  • 信号处理程序有单独的堆栈吗?

    信号处理程序是否有单独的堆栈 就像每个线程都有单独的堆栈一样 这是在 Linux C 环境中 来自 Linux 手册页signal 7 http kernel org doc man pages online pages man7 sign
  • 找不到 assimp-vc140-mt.dll ASSIMP

    我已经从以下位置下载了 Assimp 项目http assimp sourceforge net main downloads html http assimp sourceforge net main downloads html Ass
  • ASP.Net Core 内容配置附件/内联

    我正在从 WebAPI 控制器返回一个文件 Content Disposition 标头值自动设置为 附件 例如 处置 附件 文件名 30956 pdf 文件名 UTF 8 30956 pdf 当它设置为附件时 浏览器将要求保存文件而不是打
  • 类的成员复制

    在学习 复制成员 概念时 书中给出了如下说法 此外 如果非静态成员是引用 const 或没有复制赋值的用户定义类型 则无法生成默认赋值 我不太明白这个声明到底想传达什么 或者说这个说法指的是哪一种场景 谢谢 该语句与编译器自动为您编写的类
  • 如何获取 QTableView 的标题列表?

    我有一个QTableView我的对话框中的对象 我需要访问该表的水平标题并将它们放入QStringList object 尽管进行了大量搜索 但我在 Qt 文档中找不到如何获取此标头列表 编辑 我发现的最接近的地方是this https w
  • 为什么 set_symmetry_difference 无法与比较器一起使用?

    Example program include
  • 检查 RoutedEvent 是否有任何处理程序

    我有一个自定义 Button 类 当单击它时 打开特定窗口 它总是执行相同的操作 我添加了一个可以在按钮的 XAML 中分配的 Click 事件 就像常规按钮一样 当它被单击时 我想执行 Click 事件处理程序 如果已分配 否则我想执行默
  • AES 输出是否小于输入?

    我想加密一个字符串并将其嵌入到 URL 中 因此我想确保加密的输出不大于输入 AES 是可行的方法吗 不可能创建任何始终会创建比输入更小的输出的算法 但可以将任何输出反转回输入 如果您允许 不大于输入 那么基本上您只是在谈论同构算法alwa
  • 每个租户的唯一用户名和电子邮件

    我正在使用以下代码编写多租户应用程序ASP NET Core 2 1 我想覆盖默认的与用户创建相关的验证机制 目前我无法创建多个具有相同的用户UserName My ApplicationUser模型有一个名为TenantID 我想要实现的
  • 是否有相当于 Clang/LLVM 的 .spec 文件,在哪里可以找到参考?

    The gcc驱动程序可以配置为使用特定的链接器 特定的选项和其他细节 例如覆盖系统头 specs files 当前 截至撰写本文时 GCC 版本 4 9 0 的手册此处描述了规范文件 https gcc gnu org onlinedoc
  • 如何分析组合的 python 和 c 代码

    我有一个由多个 python 脚本组成的应用程序 其中一些脚本正在调用 C 代码 该应用程序现在的运行速度比以前慢得多 因此我想对其进行分析以查看问题所在 是否有工具 软件包或只是一种分析此类应用程序的方法 有一个工具可以将 python
  • C# 中的 strstr() 等效项

    我有两个byte 我想找到第二个的第一次出现byte 在第一个byte 或其中的一个范围 我不想使用字符串来提高效率 翻译第一个byte to a string会效率低下 基本上我相信就是这样strstr 在 C 中做 最好的方法是什么 这
  • 新任务中使用的依赖注入服务

    我在需要时使用依赖项注入来访问我的服务 但我现在想要创建一个并发任务 但这会由于依赖项注入对象及其生命周期而导致问题 我读过这篇文章 标题 防止多线程 Link http mehdi me ambient dbcontext in ef6
  • QFileDialog::getSaveFileName 和默认的 selectedFilter

    我有 getSaveFileName 和一些过滤器 我希望当用户打开 保存 对话框时选择其中之一 Qt 文档说明如下 可以通过将 selectedFilter 设置为所需的值来选择默认过滤器 我尝试以下变体 QString selFilte
  • ASP.NET Core MVC 视图组件搜索路径

    在此处的文档中 https learn microsoft com en us aspnet core mvc views view components view aspnetcore 2 2 https learn microsoft

随机推荐

  • 单击浏览器的后退按钮时重定向到特定页面

    这是一个有点菜鸟的问题 但事实就是这样 我有一个网站 用户只能通过下一页和后退按钮从一个页面导航到另一个页面 即有一个用户必须遵循的向导 向导的一部分包含不同的页面 一部分包含相同的页面但具有更改的 div 但从用户体验来看 它是不同的页面
  • Stream_Copy_To_Stream() 的替代方案 php

    我现在正在开发一个文件共享网站 遇到了一个小问题 我正在使用上传脚本 uploadify 它工作得很好 但如果用户想要 我希望上传的文件被加密 现在我有可以执行此操作的工作代码 如下所示 但我的服务器只有 1GB 或内存 并且使用strea
  • 如何在 WooCommerce 3+ 中进行调试

    我正在使用本教程为 Woocommerce 创建自定义运输方法https docs woocommerce com document shipping method api https docs woocommerce com docume
  • 如何“重置”给定 HTML 元素的样式?

    我正在开发一个可嵌入的 javascript 它将 HTML 元素插入未知页面 我无法控制要插入 HTML 的页面的样式表 问题是我插入的 HTML 会被页面错误地风格化 我想防止这种情况发生 确保我插入的元素是最不冗长和 或资源密集的ex
  • 使用 pd.read_clipboard 复制多索引数据帧?

    Given a 像这样的数据框 https stackoverflow com questions 17921010 how to query multiindex index columns values in pandas C A B
  • Swift Codable:使用未知键解码字典

    Codable当您了解 JSON 数据的关键格式时 这会非常有用 但是如果您不知道密钥怎么办 我目前面临这个问题 通常我希望 JSON 数据像这样返回 id lt 123 gt data id
  • 如何用颜色填充位图对象中的封闭区域

    给区域内的点赋予颜色来填充区域 类似于油漆桶功能中的 绘图 这 NET Framework 中 没有直接的等效项 但我希望使用 C 来做到这一点 是否可以 这是一个非常简单的洪水填充算法 应该可以帮助您入门 void Form1 Paint
  • 在不知道父元素的情况下访问多维数组元素

    我有返回以下多维数组的函数 我无法控制数组的形成方式 我正在尝试访问 结果 元素 这个问题是 父元素的名称不断变化 Result 元素的位置始终相同 因为名称为 Result 是否可以在不知道父元素名称的情况下访问该元素 Array sHe
  • UITableViewCell 异步加载图像问题 - Swift

    在我的应用程序中 我构建了自己的异步图像加载类 我传入一个对象 然后它检查缓存 NSCache 是否有图像 如果没有 它将检查文件系统是否已保存图像 如果图像尚未保存 它将在后台下载图像 NSOperations 帮助 到目前为止 效果很好
  • 带和不带 ( ) 的构造函数调用之间的区别

    我是 C 初学者 想了解为什么 return std list
  • Netbeans 7.1 和 python

    我曾经使用 Netbeans 6 9 进行 Python 开发 以及 Java 和 PHP 我有一个很酷的 PHP 调试器 带有 xDebug 还有良好的 Python 支持 没有任何抱怨 我搬到另一台计算机下载了最新的 netbeans
  • 相当于“Dim As String * 1”VB6 到 VB.NET

    我有一些VB6代码需要迁移到VB NET 我想询问一下这行代码 看看是否有办法在 NET中实现它 Dim strChar1 As String 1 智能感知不断告诉我声明即将结束 这就是所谓的 固定长度 字符串 VB NET 中没有完全等效
  • Java中通过Proxy发送SOAP消息

    我需要知道如何设置代理并确认它正在工作 我制作了一个如下所示的测试程序 您可以在其中指定代理地址和端口号 我在以下位置找到了地址和端口 http www freeproxylists net http www freeproxylists
  • MongoDB:查找字段名称以以下开头的对象

    查询 MongoDB 从给定的集合 参见下面的示例 中 我只需要列出包含字段的对象 其中字段名称以 need 开头 包含三个对象的集合示例 1 id 1 need some A need more 1 website id 12345678
  • 从网络加载(并显示).XIB

    我希望能够下载 通过 NSURLConnection 请求 XIB 文件 并将其呈现在视图中 我已经实现了 NSURLConnection 当然 当连接完成时 我留下了一个代表 XIB 文件的 XML 数据的 NSString 示例 只是许
  • Gradle:未指定资源类型(在“text”处,值为“@{user.name}”)。数据绑定不起作用

    我试图将适当的数据绑定功能添加到我的项目中 但由于某种原因它仍然失败 build gradle文件看起来像这样 apply plugin com android application buildscript repositories ma
  • Java 变量如何与其自身不同?

    我想知道这个问题是否可以用 Java 解决 我是该语言的新手 这是代码 class Condition you can change in the main public static void main String args int x
  • Java Swing 更新 JList

    我想知道是否有任何方法可以在用户添加或删除项目后以及用户对其进行排序后更新 Jlist 有什么方法可以编写标准化方法来根据数组或向量中的项目顺序以及当用户从 JList 所基于的数组中删除或添加对象时更新显示 谢谢 应该更新 ListMod
  • 异常处理实践顶层与每个函数

    我见过几种异常处理方法 我见过的两种最常见的模式是 在每个函数上尝试捕获 记录异常并重新抛出 在最顶层尝试捕获 如主函数 记录异常并重新抛出 如果有的话 哪一种是更好的做法 或者在什么情况下您会选择一种方法而不是另一种 这取决于您的应用程序
  • 当使用 fork() 时,getline() 会重复读取文件

    我正在开发一个简单的 shell 程序 一个命令行解释器 我想从文件中逐行读取输入 所以我使用了 getline 函数 第一次 程序工作正常 但是 当它到达文件末尾时 它没有终止 而是开始从头开始读取文件 并且无限运行 下面是 main 函