gpt4 book ai didi

c - 使用 fgets 读取输入在 C 中返回重复行

转载 作者:太空狗 更新时间:2023-10-29 15:31:39 24 4
gpt4 key购买 nike

我正在试验一些用于 shell 实现的 C 代码,发现 fgets() 在我 fork 一个进程后返回重复的行,这是我无法理解的,我将不胜感激任何帮助。

我的问题是: fork 是否会改变父进程中任何打开文件的偏移量?这似乎发生在我的程序中。

来自下面@Vadim Ponomarev 的回答和我的理解:fgets() 不是线程安全的(或者严格来说,它是,但是 fork ​​一个进程会导致 stdin 以某种方式被初始化,从而导致共享文件偏移量的变化)。

代码是这样的:

int main() {

char buf[200];
int r;
pid_t pid = 0;

while(getcmd(buf, 200, pid) >= 0) {
fprintf(stderr, "current pid: %d\n", getpid());
pid = fork();
// Without forking the fgets() reads all lines normally
if(pid == 0)
exit(0);

wait(&r);
}

return 0;
}

getcmd() 函数只是一个包装器:

int
getcmd(char *buf, int nbuf, pid_t pid)
{
memset(buf, 0, nbuf);
if (fgets(buf, nbuf, stdin) == NULL) {
fprintf(stderr, "EOF !!!\n");
return -1;
}
fprintf(stderr, "pid: %d -- getcmd buf ======= --> %s\n", getpid(), buf);
return 0;
}

我还有一个包含一些随机文本的输入文件 temp:

line 1
line 2
line 3

编译后,我运行a.out ,输出显示打印了 6 行,通常有些行是重复的。但是如果我删除该行

pid = fork()
...

然后输出就正常了(只是一行一行显示所有行,这意味着 fgets() 被调用了 3 次)。

知道出了什么问题吗?

输出(这就是得到的):

pid: 10361 -- getcmd buf ======= --> line1

current pid: 10361
pid: 10361 -- getcmd buf ======= --> line2

current pid: 10361
pid: 10361 -- getcmd buf ======= --> line3

current pid: 10361
pid: 10361 -- getcmd buf ======= --> line2

current pid: 10361
pid: 10361 -- getcmd buf ======= --> line3

current pid: 10361
pid: 10361 -- getcmd buf ======= --> line3

current pid: 10361
EOF !!!

我希望看到这个:

current pid: 10361
pid: 10361 -- getcmd buf ======= --> line1

current pid: 10361
pid: 10361 -- getcmd buf ======= --> line2

current pid: 10361
pid: 10361 -- getcmd buf ======= --> line3

EOF

供引用的编译版本:

#include <stdio.h>
#include <stdlib.h>
#include <wait.h>
#include <zconf.h>
#include <unistd.h>
#include <memory.h>

int
getcmd(char *buf, int nbuf, pid_t pid)
{
memset(buf, 0, nbuf);
if (fgets(buf, nbuf, stdin) == NULL) {
fprintf(stderr, "EOF !!!\n");
return -1;
}
fprintf(stderr, "pid: %d -- getcmd buf ======= --> %s\n", getpid(), buf);
return 0;
}

int main() {

char buf[200];
int r;
pid_t pid = 0;

while(getcmd(buf, 200, pid) >= 0) {
fprintf(stderr, "current pid: %d\n", getpid());
pid = fork();
// Without forking the fgets() reads all lines normally
if(pid == 0)
exit(0);

wait(&r);
}

return 0;
}

谢谢!

最佳答案

  1. 已经提到父子正在共享文件描述符 0 (stdin) 的当前位置
  2. 似乎流(stdin、stdout、stderr)的 libc 运行时初始化包含一些改变当前 stdin 位置的东西:

    > strace -f ./a.out < temp 2>&1 | less
    ....
    write(2, "pid: 29487 -- getcmd buf ======="..., 45pid: 29487 -- getcmd buf ======= --> line 1
    clone(child_stack=0,flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD,child_tidptr=0x7f34940f19d0) = 29488
    Process 29488 attached
    [pid 29487] wait4(-1, <unfinished ...>
    [pid 29488] lseek(0, -14, SEEK_CUR) = 7
    [pid 29488] exit_group(0) = ?
    [pid 29488] +++ exited with 0 +++
    <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 29488

请注意 child (pid 29488) 中的 lseek(0, -14, SEEK_CUR)

  1. 结果,在我的环境中(openSUSE Leap 42.2,glibc-2.22-4.3.1)程序无限循环,根本没有 EOF

  2. 将示例中的 fgets() 更改为 read()

    ....
    if (read(0, buf, nbuf) == 0) {
    ....
    while(getcmd(buf, 7, pid) >= 0) {
    ....

程序按预期运行(三行和 EOF)

  1. 并再次运行 strace -f - child 中不再有 lseek()!

  2. 结论 - 似乎在多进程环境中使用流函数(在 stdio.h 中声明)必须非常小心,因为有很多副作用(如本例中所示)

关于c - 使用 fgets 读取输入在 C 中返回重复行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44016803/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com