我正在尝试实现一个小型 C 程序,其执行方式类似于 Linux shell 命令提示符 $ sort < Names.txt | uniq | wc - l
。为此,我使用 execlp 来运行命令
生成的程序将对任意名称列表进行排序并删除重复项。它对列表进行排序,因为它需要相邻的重复行才能被删除。然后只计算行数。
我已经发布了我的代码,它目前在我编译后挂起gcc -o sortuniqwc sortuniqwc.c
并运行./sortuniqwc < Names.txt
。如果我注释掉 fd 的管道,每个系统调用似乎都能正确执行。我不确定为什么它没有正确地将进程传递给系统调用
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
int main(int argc, char *arv[])
{
pid_t pid;
int fd1[2]; //making file descriptor 1
if (pipe(fd1) == -1)
{
fprintf(stderr, "pipe failed");
return 1;
}
pid = fork(); //first child for sort
//printf("the pid for pipe parent is %d and the child pid is %d", getppid(), getpid());
if (pid < 0)
{
fprintf(stderr, "fork error");
return 1;
}
if (pid == 0)
{
dup2(fd1[1], 1);
close(fd1[0]);
//printf("the child process running sort is %d\n", getpid());
execlp("sort", "sort", NULL);
printf("sort exec - should not be here");
exit(0);
}
wait(0);
int fd2[2];
if (pipe(fd2) == -1)
{
fprintf(stderr, "pipe failed");
return 1;
}
pid = fork(); //second child for uniq
if (pid < 0)
{
fprintf(stderr, "fork error\n");
return 1;
}
if (pid == 0)
{
dup2(fd1[0], 0);
dup2(fd2[1], 1);
close(fd1[1]);
close(fd2[0]);
//printf("the child process running uniq is %d\n", pid);
execlp("/usr/bin/uniq", "uniq", NULL);
printf("uniq exec - you shouldnt be here");
exit(0);
}
wait(0);
pid = fork(); //3rd child process for wc
if (pid < 0)
{
fprintf(stderr, "fork failed\n");
return 1;
}
if (pid == 0)
{
dup2(fd2[0], 0);
close(fd2[1]);
close(fd1[0]);
close(fd1[1]);
//printf("the child process running wc is %d\n", getpid());
execlp("wc", "wc", "-l", NULL);
printf("wc exec - you shouldnt be here\n");
exit(0);
}
//parent
close(fd1[0]);
close(fd1[1]);
close(fd2[0]);
close(fd2[1]);
wait(NULL);
printf("CHILD COMPLETE \n");
}
最佳答案
TL;DR - 父级需要close()
其附加到sort
输出的管道写入端的副本。在第一次等待之前添加 close(fd1[1])
可“修复”问题。
程序在第二次调用 wait()
时“挂起”(等待 uniq
子进程退出1)。但是,uniq 永远不会退出,因为它连接到 fd1 管道读取端的标准输入永远不会关闭。该文件描述符在系统中有两个副本:第一个属于 exec
的 sort
的子进程,并且它确实按照 的预期关闭了排序
。但另一个副本属于父进程,它不会 close()
它。由于管道的写入端至少还有一个打开的文件描述符,因此管道并未关闭。
此解决方案还需要将整个排序输出缓冲在管道中(即在内核中)。对于重要的输入,最好以相反的顺序 fork 子项,连接它们的所有管道,并让它们并行运行。这更接近真实、健壮的 shell 的功能。
1 或者接收信号等,健壮的 shell 应该检查这些信号。
关于C 程序模拟命令行提示符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53548243/