【发布时间】:2012-02-13 00:40:22
【问题描述】:
下面的简短程序旨在遍历从命令行传递的 argv 并执行每个参数。这不是我的作业,而是我正在做的准备做作业的事情。
第一个参数从 STDIN 和 STDOUT 获取输入,并写入管道。在每次迭代结束时(最后一次除外),文件描述符被交换,以便下一个 exec 写入的管道将被下一个读取。例如,我打算以这种方式为
./a.out /bin/pwd /usr/bin/wc
只打印工作目录的长度。代码如下
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
main(int argc, char * argv[]) {
int i;
int left[2], right[2], nbytes; /* arrays for file descriptors */
/* pointers for swapping */
int (* temp);
int (* leftPipe) = left;
int (* rightPipe) = right;
pid_t childpid;
char readbuffer[80];
/* for the first iteration, leftPipe is STDIN */
leftPipe[0] = STDIN_FILENO;
leftPipe[1] = STDOUT_FILENO;
for (i = 1; i < argc; i++) {
/* reopen the right pipe (is this necessary?) */
pipe(rightPipe);
fprintf(stderr, "%d: %s\n", i, argv[i]);
fprintf(stderr, "%d %d %d %d\n", leftPipe[0], leftPipe[1], rightPipe[0], rightPipe[1]);
if ((childpid = fork()) == -1) {
perror("fork");
exit(1);
}
if (childpid == 0) {
/* read input from the left */
close(leftPipe[1]); /* close output */
dup2(leftPipe[0], STDIN_FILENO);
close(leftPipe[0]); /* is this necessary? A tutorial seemed to be doing this */
/* write output to the right */
close(rightPipe[0]); /* close input */
dup2(rightPipe[1], STDOUT_FILENO);
close(rightPipe[1]);
execl(argv[i], argv[i], NULL);
exit(0);
}
wait();
/* on all but the last iteration, swap the pipes */
if (i + 1 < argc) {
/* swap the pipes */
fprintf(stderr, "%d %d %d %d\n", leftPipe[0], leftPipe[1], rightPipe[0], rightPipe[1]);
temp = leftPipe;
leftPipe = rightPipe;
rightPipe = temp;
fprintf(stderr, "%d %d %d %d\n", leftPipe[0], leftPipe[1], rightPipe[0], rightPipe[1]);
}
}
/* read what was last written to the right pipe */
close(rightPipe[1]); /* the receiving process closes 1 */
nbytes = read(rightPipe[0], readbuffer, sizeof(readbuffer));
readbuffer[nbytes] = 0;
fprintf(stderr, "Received string: %s\n", readbuffer);
return 0;
}
更新:在以下所有测试用例中,我最初都使用了 /bin/wc,但哪个 wc 发现抽水马桶根本不在我想的地方。我正在修改结果。
普通情况下的输出(./a.out /bin/pwd)与预期的一样:
1: /bin/pwd
Received string: /home/zeigfreid/Works/programmatical/Langara/spring_2012/OS/labs/lab02/play
使用第一个示例运行此程序的输出 (./a.out /bin/pwd /usr/bin/wc):
1: /bin/pwd
0 1 3 4
3 4 0 1
2: /bin/wc
此时,终端挂起(可能正在等待输入)。
如您所见,没有收到字符串。我想象的是我在上面做错了什么,或者在交换指针时,或者我不理解 unix 文件描述符。最后,我的任务将是解释任意长的管道,这是我解决问题的想法之一。我很难判断我是否走在了树上的正确轨道上。我了解 unix 文件描述符吗?
更新:
使用 /bin/ls 作为第二个参数运行它,我得到以下结果(数字是不同点的文件描述符):
1: /bin/pwd
0 1 3 4
0 1 3 4
3 4 0 1
2: /bin/ls
3 4 5 6
Received string: a.out
log
pipe2.c
play.c
@
最后还是有一些垃圾,但是我现在更担心我看不懂指针!这两个命令虽然相互独立,但它们并没有真正使用管道。
UPDATE:垃圾字符来自未关闭字符串。现在我关闭它,没有垃圾。
【问题讨论】:
-
我想建议将您所有的
printf(...)呼叫更改为fprintf(stderr,...)。将标准 IO (printf(3)) 与较低级别的例程 (pipe(2),dup2(2),close(2)) 混合使用起来麻烦而不值得。 -
注意!我想夹板会同意的。
-
在打印之前不要终止字符串,这解释了垃圾。在
read之后尝试readbytes[nbytes] = 0。 -
所以,在我看来,在交换之后,正在执行的进程无法从交换的管道中读取。如果没有交换,我们可以从管道中读取。如果第二个进程没有从管道读取数据,那么它运行良好并将输出放入管道。
标签: c pipe file-descriptor io-redirection