【发布时间】:2017-10-16 10:35:03
【问题描述】:
我正在试验一些用于 shell 实现的 C 代码,发现 fgets() 在我 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;
}
谢谢!
【问题讨论】:
-
您能否编辑您的问题以显示实际(和预期)输出(完整,复制粘贴为文本)?还请包括一个实际的Minimal, Complete, and Verifiable Example,我们可以轻松地复制和测试自己。
-
@Someprogrammerdude 嗨,我添加了输出。总之,我没想到会看到重复的行被读取。
-
父子共享同一个文件描述符,你对stdin的fork有什么期望?
-
@Ôrel 感谢您提供信息。但是在我的代码中,我在创建孩子后立即终止。这将如何更改父级中的文件偏移量?
-
这应该可以按预期工作。你确定你编译了发布的代码吗?看起来你在测试中删除了对
exit的调用。