我是unix的新手。在下面的代码中,我从命令行“〜$ foo last sort more”传递三个参数,以便复制“〜$ last | sort | more”。我正在尝试创建一个程序,它将采用三个参数(现在至少3个)。父将分叉三个进程。第一个进程将写入管道。第二个进程将读取和写入管道,第三个进程将从管道读取并写入stdout(终端)。第一个进程将执行“最后”,第二个进程将执行“排序”,第三个进程将执行“更多”,并且进程将休眠1,2和3秒以便同步。我很确定我在创建管道和重定向输入和输出时遇到了麻烦。我没有得到任何输出到终端,但我可以看到已创建进程。我将不胜感激。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#define FOUND 1
#define NOT_FOUND 0
#define FIRST_CHILD 1
#define LAST_CHILD numargc
#define PATH_1 "/usr/bin/"
#define PATH_2 "/bin/"
#define DUP_READ() \
if (dup2(fdes[READ], fileno(stdin)) == -1) \
{ \
perror("dup error"); \
exit(4); \
}
#define DUP_WRITE() \
if (dup2(fdes[WRITE], fileno(stdout)) == -1) \
{ \
perror("dup error"); \
exit(4); \
}
#define CLOSE_FDES_READ() \
close(fdes[READ]);
#define CLOSE_FDES_WRITE() \
close(fdes[WRITE]);
#define EXEC(x, y) \
if (execl(arraycmds[x], argv[y], (char*)NULL) == -1) \
{ \
perror("EXEC ERROR"); \
exit(5); \
}
#define PRINT \
printf("FD IN:%d\n", fileno(stdin)); \
printf("FD OUT:%d\n", fileno(stdout));
enum
{
READ, /* 0 */
WRITE,
MAX
};
int cmdfinder( char* cmd, char* path); /* 1 -> found, 0 -> not found */
int main (int argc, char* argv[])
{
int numargc=argc-1;
char arraycmds[numargc][150];
int i=1, m=0, sleeptimes=5, numfork;
int rc=NOT_FOUND;
pid_t pid;
int fdes[2];
if(pipe(fdes) == -1)
{
perror("PIPE ERROR");
exit(4);
}
while(i <= numargc)
{
memset(arraycmds[m], 0, 150);
rc=cmdfinder(argv[i], arraycmds[m]);
if (rc)
{
printf("Command found:%s\n", arraycmds[m]);
}
i++;
m++;
}
i=0; //array index
numfork=1; //fork number
while(numfork <= numargc)
{
if ((pid=fork()) == -1)
{
perror("FORK ERROR");
exit(3);
}
else if (pid == 0)
{
/* Child */
sleep(sleeptimes);
if (numfork == FIRST_CHILD)
{
DUP_WRITE();
EXEC(i, numfork);
}
else if (numfork == LAST_CHILD)
{
DUP_READ();
CLOSE_FDES_WRITE();
EXEC(i, numfork);
}
else
{
DUP_READ();
DUP_WRITE();
CLOSE_FDES_READ();
CLOSE_FDES_WRITE();
EXEC(i, numfork);
}
}
else
{
/* Parent */
printf("pid:%d\n", pid);
i++;
numfork++;
sleeptimes++;
}
}
PRINT;
printf("i:%d\n", i);
printf("numfork:%d\n", numfork);
printf("DONE\n");
return 0;
}
int cmdfinder(char* cmd, char* path)
{
DIR* dir;
struct dirent *direntry;
char *pathdir;
int searchtimes=2;
while (searchtimes)
{
pathdir = (char*)malloc(250);
memset(pathdir, 0, 250);
if (searchtimes==2)
{
pathdir=PATH_1;
}
else
{
pathdir=PATH_2;
}
if ((dir = opendir(pathdir)) == NULL)
{
perror("Directory not found");
exit (1);
}
else
{
while (direntry = readdir(dir))
{
if (strncmp( direntry->d_name, cmd, strlen(cmd)) == 0)
{
strcat(path, pathdir);
strcat(path, cmd);
//searchtimes--;
return FOUND;
}
}
}
closedir(dir);
searchtimes--;
}
printf("%s: Not Found\n", cmd);
return NOT_FOUND;
}
答案 0 :(得分:0)
所有的宏都比你直接写它更难阅读。特别是当他们引用局部变量时。要了解EXEC
发生了什么,我的眼睛必须从它的使用位置跳到它定义的位置,找出它使用的本地阵列,然后跳回去查看该访问如何适应{ {1}}。这是一个迷宫般的宏。
哇,main
?你自己的cmdfinder
查询,只有它是硬编码的$PATH
?双哇,/usr/bin:/bin
,只是为了找出一个文件是否存在,其名称已经确定?只需readdir
它!或者不做任何事情,只需执行它并通过尝试下一个来处理stat
。或者使用ENOENT
这就是它的用途!
关于要点...你没有足够的管道,而且你没有关闭所有未使用的描述符。
execlp
是由2个管道连接的3个命令的管道。你不能用一根烟斗做。第一个命令应该写入第一个管道,中间命令应该读取第一个管道并写入第二个管道,最后一个命令应该读取第二个管道。
您可以先创建两个管道,然后执行所有分支,这样可以使事情变得简单,但在每个子进程中都需要大量last | sort | more
,因为它们都将继承所有管道fds。或者,您可以使用更复杂的循环,在分配将使用它的第一个进程之前创建每个管道,并在创建相关子进程后立即关闭父级中的每个描述符。我不想看到你用了多少个宏。
每个成功的dup之后都应该关闭已复制的描述符。 close
是“复制”的缩写,而不是“移动”。完成后,您还有一个额外的描述符,所以不仅仅是dup
- 后来也是dup2(fdes[1], fileno(stdout)
。 (要非常强大,您应该检查close(fdes[1])
是否已经过了,在这种情况下,请跳过fdes[1]==fileno(stdout)
和dup2
。
关注问题
您不能将一个管道用于3个进程,因为无法区分哪个数据应该到达哪个目标。当第一个进程写入管道时,当其他两个进程都尝试从中读取时,其中一个进程将获取数据,但您将无法预测哪个进程。您需要中间进程来读取第一个进程写入的内容,以及读取中间进程写入内容的最后一个进程。
你在分叉后共享文件描述符是正确的。实际管道对象是共享的。这就是整个系统运作的原因。但文件描述符 - 由小整数指定的端点,如标准输出为1,标准输入为0,等等 - 与您建议的方式不相关。相同的管道对象可以在两个进程中与相同的文件描述符号相关联,这些关联是独立的。在一个过程中关闭fd 1不会导致fd 1在任何其他过程中关闭,即使它们是相关的。
共享fd表,以便一个任务中的一个关闭在另一个任务中生效,是“pthread”功能集的一部分,而不是“fork”功能集。