gpt4 book ai didi

c - 带重定向的fgets()调用获取异常数据流

转载 作者:太空狗 更新时间:2023-10-29 17:23:27 24 4
gpt4 key购买 nike

我正准备用C语言写一个shell。下面是源代码:

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>

int
getcmd(char *buf, int nbuf)
{
memset(buf, 0, nbuf);
fgets(buf, nbuf, stdin);
printf("pid: %d, ppid: %d\n", getpid(), getppid());
printf("buf: %s", buf);
if(buf[0] == 0) {// EOF
printf("end of getcmd\n");
return -1;
}
return 0;
}

int
main(void)
{
static char buf[100];
int fd, r, ret;

// Read and run input commands.
while((ret = getcmd(buf, sizeof(buf))) >= 0){
if(fork() == 0)
exit(0);
wait(&r);
}
exit(0);
}

当我执行已编译的可执行文件并将标准输入重定向到名为 t.sh 的文件时,其内容为“1111\n2222\n”,如 ./myshell < t.sh,输出为:

pid: 2952, ppid: 2374
buf: 1111
pid: 2952, ppid: 2374
buf: 2222
pid: 2952, ppid: 2374
buf: 2222
pid: 2952, ppid: 2374
buf: end of getcmd

显然,函数 getcmd() 得到 3 行(1111、2222、2222),而 t.sh 中只有 2 行。当在 t.sh 中放入更多行时,这些情况会变得更糟。

而主进程是唯一执行getcmd的进程,我们可以通过pid的输出来判断。

顺便说一句,我发现如果去掉wait(&r)这行代码,输出可以正常。

最佳答案

wait确保子进程在父进程完成文件之前有时间运行。如果我strace Linux下的文件,我得到

% strace -f ./a.out
[lots of stuff]
wait4(-1, strace: Process 29317 attached
<unfinished ...>
[pid 29317] lseek(0, -2, SEEK_CUR) = 0
[pid 29317] exit_group(0) = ?
[pid 29317] +++ exited with 0 +++
<... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 29317
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=29317, si_uid=1000, si_status=0
_utime=0, si_stime=0} ---
[lots of stuff]

子进程倒带标准输入作为fork 之后的第一个操作之一。 ,之后它会立即退出。具体来说,它从流中倒回与 fgets 读入的字节一样多的字节。进入缓冲区但仍未使用libc 在 fork 后自动执行此操作。我还看到 child 进程刷新了 stdout .

我不确定对此有何看法...但很明显,如果您想编写一个 shell,您绝不能使用 <stdio.h> 与标准流进行交互 完全。如果lseek 没有发生,那么子进程将看到最多 4095 个字节的 stdin被跳过!您必须始终只使用 readwrite来自 <unistd.h>反而。或者,您可能会幸运地将以下调用添加到 main 的开头在从 stdin 读取任何内容之前:

if (setvbuf(stdin, NULL, _IONBF, 0) != 0) {
perror("setvbuf:");
exit(1);
}

这将设置 stdin流式传输到非缓冲模式,所以它不应该读取太多。尽管如此,Linux manual page for fgets 说:

It is not advisable to mix calls to input functions from the stdio library with low-level calls to read(2) for the file descriptor associated with the input stream; the results will be undefined and very probably not what you want.

顺便说一句,如果 stdin 则无法复制来自管道:

% echo -e '1\n2' | ./a.out  
pid: 498, ppid: 21285
buf: 1
pid: 498, ppid: 21285
buf: 2
pid: 498, ppid: 21285
buf: end of getcmd

但这自然会使另一个问题变得明显 - child 看到输入被跳过。


附言

您永远不会检查 fgets 的返回值因此您不知道何时会发生读取错误。

If a read error occurs during the operation, the array contents are indeterminate and a null pointer is returned.

关于c - 带重定向的fgets()调用获取异常数据流,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45656781/

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