c - 试图在 C 中实现管道 - shell 挂起并且没有运行命令
问题描述
我正在尝试运行此命令ps -j | more
。我想我已经正确设置了管道,但由于某种原因它只是挂起:
我正在调用一个运行的叉子ps -j
和第二个运行的叉子more
并将它们与管道连接。
由于某种原因,这仍然无法按预期工作。
下面的代码:
#define BUF_SIZE 100
int main() {
pid_t pid1, pid2;
int save_stdin, save_stdout;
int fd[2];
char *argv1[] = { "/bin/ps", "-j", NULL };
char *argv2[] = { "/bin/more", NULL };
char prompt[] = "Press enter: ";
char buffer[BUF_SIZE];
write(STDOUT_FILENO, prompt, sizeof(prompt) - 1);
ssize_t readIn = readIn = read(STDIN_FILENO, buffer, BUF_SIZE);
buffer[readIn - 1] = '\0';
printf("readIn: %d\n", readIn);
pipe(fd);
pid1 = fork();
if (pid1 == 0) { // child
close(fd[0]);
save_stdout = dup(1);
dup2(fd[1], STDOUT_FILENO);
execvp(argv1[0], argv1);
close(fd[1]);
} else { // parent
pid2 = fork();
if(pid2 == 0) {
save_stdin = dup(0);
dup2(fd[0], STDIN_FILENO);
execvp(argv2[0], argv2);
close(fd[0]);
} else {
}
}
dup2(save_stdin, 0);
dup2(save_stdout, 1);
close(save_stdin);
close(save_stdout);
int i = 1;
do {
wait(NULL);
} while (i-- > 0);
exit(0);
}
非常感谢任何帮助!
编辑:
我试过在之后dup2()
但之前关闭管道execvp()
,但它仍然挂起:
pipe(fd);
pid1 = fork();
if (pid1 == 0) { // child
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
close(fd[0]);
int res1 = execvp(argv1[0], argv1);
printf("exec1: %d\n", res1);
} else { // parent
pid2 = fork();
if(pid2 == 0) {
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
int res2 = execvp(argv2[0], argv2);
printf("exec2: %d\n", res2);
} else {
}
}
close(fd[1]);
close(fd[0]);
printf("finished\n");
int i = 1;
do {
wait(NULL);
} while (i-- > 0);
解决方案
您没有关闭子级管道或父级管道中的任何管道的足够文件描述符。
经验法则:如果您
将管道的一端连接到标准输入或标准输出,请
尽快dup2()
关闭返回的两个原始文件描述符
。pipe()
特别是,您应该在使用任何
exec*()
函数系列之前关闭它们。
如果您使用
dup()
或
复制描述符,该规则也fcntl()
适用F_DUPFD
这是正确关闭管道的工作代码。execvp()
我删除了命令的路径组件,因为 (a) 它们对我的机器来说是错误的,并且 (b)如果您指定命令的路径,则使用毫无意义。我报告了大多数错误(但我不检查read()
andwrite()
调用的结果。我还报告了孩子的退出状态。如果孩子们无法执行命令,他们会报告并退出。你的带有“save_stdin”等的代码是进一步混淆了事情,因为变量在父级中未初始化,这是序列的位置:
dup2(save_stdin, 0);
dup2(save_stdout, 1);
close(save_stdin);
close(save_stdout);
被处决。你和我都不知道这两个dup2()
调用做了什么——但很可能它们失败了,或者它们对你的标准 I/O 通道做了一些奇怪的事情。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define BUF_SIZE 100
int main(void)
{
pid_t pid1, pid2;
int fd[2];
char *argv1[] = { "ps", "-j", NULL }; // Remove PATH because using execvp()
char *argv2[] = { "more", NULL }; // Remove PATH because using execvp()
char prompt[] = "Press enter: ";
char buffer[BUF_SIZE];
write(STDOUT_FILENO, prompt, sizeof(prompt) - 1);
ssize_t readIn = readIn = read(STDIN_FILENO, buffer, BUF_SIZE);
buffer[readIn - 1] = '\0';
printf("readIn: %zd\n", readIn);
if (pipe(fd) < 0)
{
fprintf(stderr, "failed to create pipe\n");
exit(EXIT_FAILURE);
}
if ((pid1 = fork()) < 0)
{
fprintf(stderr, "failed to fork - 1\n");
exit(EXIT_FAILURE);
}
if (pid1 == 0) // child
{
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
close(fd[1]);
execvp(argv1[0], argv1);
fprintf(stderr, "failed to execute %s\n", argv1[0]);
exit(EXIT_FAILURE);
}
if ((pid2 = fork()) < 0)
{
fprintf(stderr, "failed to fork - 2\n");
exit(EXIT_FAILURE);
}
if (pid2 == 0)
{
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
execvp(argv2[0], argv2);
fprintf(stderr, "failed to execute %s\n", argv2[0]);
exit(EXIT_FAILURE);
}
close(fd[0]);
close(fd[1]);
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
fprintf(stderr, "%d: child %d exited with status 0x%.4X\n",
(int)getpid(), corpse, (unsigned)status);
return 0;
}
该程序是pipe41
(对输出进行了一些小的编辑,以删除您实际上不需要知道我在其他窗口中所做的事情):
$ ./pipe41
Press enter:
readIn: 1
USER PID PPID PGID SESS JOBC STAT TT TIME COMMAND
jonathanleffler 6618 6617 6618 0 1 S s000 0:00.11 -bash
jonathanleffler 6629 6628 6629 0 1 S+ s001 0:00.04 -bash
jonathanleffler 6660 6645 6660 0 1 S+ s002 0:00.04 -bash
jonathanleffler 6716 6695 6716 0 1 S+ s003 0:00.04 -bash
jonathanleffler 6776 6746 6776 0 1 S+ s004 0:00.04 -bash
jonathanleffler 6800 6771 6800 0 1 S+ s005 0:00.04 -bash
jonathanleffler 7487 7486 7487 0 1 S s006 0:00.04 -bash
jonathanleffler 9558 9557 9558 0 1 S s007 0:00.06 -bash
jonathanleffler 10375 9558 10375 0 1 S+ s007 0:00.01 ./pipe41
jonathanleffler 10377 10375 10375 0 1 S+ s007 0:00.00 more
(END)10375: child 10376 exited with status 0x0000
10375: child 10377 exited with status 0x0000
$
推荐阅读
- ruby-on-rails - PaperTrail whodunnit 设置名称?
- macos - 通过 Applescript 将 Apple Mail 附件附加到 Apple Notes
- ios - Xamarin.Forms:在 iPhoneSimulator 上调试
- r - 不在 R 中编写 shapefile
- javascript - 如何从数据库中显示模态内容?
- java - 用于连接两个或多个 JSONPath 值的 JSONPath 表达式
- c# - 如何为使用包装器的 Azure Functions 编写单元测试?
- c - 具有嵌入式元信息结构的动态数组
- javascript - 使用 Js 正则表达式验证输入值以仅允许整数和十进制值
- php - 如何在循环中显示标签。例如标签 1、标签 2 等等