管道和进程间通信

4
这个项目的目标是使用管道和进程之间的通讯,来执行一个已经以多进程方式编写的行数统计程序(每个参数一个进程)。我正在努力使单个进程运行成功,然后再扩展到处理多个参数。
给定两个可执行文件 lc1 和 lc2,我希望 lc2 建立一个到 lc1 的 stdout 文件描述符的管道,这样当调用 execlp("lc1", argv[1], NULL) 时,输出将会被 while ((c= read(pipefd[0], readin, SIZE)) > 0) 读取。
根据我的 Unix 书籍,我应该使用 open、dup2、close 方法将 stdout 重定向到 stdin,这是我的代码:
int pid, c, i;
char *readin= (char *)malloc(sizeof(SIZE));

if (pipe(pipefd)== -1)
  perror("Can't open a pipe\n");

for (i=1; i< argc; i++){
if ((pid= fork())==-1)
        perror("Can't fork\n");

  run(argv[i]);

}

//close pipe
close(1);
if (dup2(pipefd[0], 0)==-1)
  perror("Can't redirect stdin");
close(pipefd[1]);

for (i=1; i< argc; i++){
    if ((wait(NULL))== -1)
        perror("Wait error");

    while ((c= read(pipefd[0], readin, SIZE)) > 0){;
        //print buf count
        total += atoi(readin);
    }
}

run函数是

void run(char *f){
  int fp;
  if ((fp= open(f, O_RDONLY)) == -1)
      perror("Can't open the file");

  close(pipefd[0]);
  dup2(pipefd[1], 1);
  close(pipefd[1]);
  execlp("ls1", f, NULL);
} 

当我尝试执行这段代码时,出现了一个标准输入重定向错误,显示坏的文件描述符。为什么会出现这种情况?请提供任何有用的提示来解决这个问题。


1
你的malloc语句有误,我认为你想要的是char *readin= (char *)malloc(SIZE); - GWW
1
在C语言中,永远不要强制转换malloc()的返回值。请参见https://dev59.com/g3NA5IYBdhLWcg3wdtld#954785。 - unwind
这并不是你当前的问题,但你真的想要分配 malloc(sizeof(SIZE)) 吗?我假设 SIZE 是一个常量,所以你正在分配大约 4 或 8 字节。你能发布稍微完整一点的代码吗?按照发布的方式,这有点难以理解。 - Duck
SIZE 实际上是一个整数 4096,所以我使用它来分配 4MB 的内存。 - Jason
2
malloc(sizeof(SIZE)) 将会在你的机器上分配一个整数的大小。malloc(SIZE) 其中SIZE的值为4096将会分配4k。 - Duck
显示剩余2条评论
2个回答

2

由于没有根据返回的PID分配功能,因此parent和child都会执行run(argv[i]),因此一个关闭后另一个可能已经关闭。 请参见下面的代码示例,它可能很方便,我将在这种情况下使用代码示例。

int main()
{
    int pipe_fd[2] = {0};
    int pid = -1;
    int status = -1;
    int ret_value = INVALID_CMD;
    int cmd_output_len = -1;
    status = pipe(pipe_fd);
    if(status<0)
    {
        perror("pipe create err");
    }
    else
    {
        pid = fork();
        if(pid<0)
        {
        }
        else if (pid == 0)
        {
            /*Child functionality*/
            child_func(pipe_fd, cmd);
        }
        else
        {
            /*Parent functionality*/
            cmd_output_len = parent_fun(pid, pipe_fd);
        }
    }
    return ret_value;
}

int child_func(int pipe_fd[], const char * cmd)
{
    int status = 5;
    int read_fd = pipe_fd[0];       /*read file descriptor*/
    int write_fd = pipe_fd[1];      /*write file descriptor*/

    int exit_status = 0;

    /*close read fd*/
    close(read_fd);

    /*dup2 stdout to write fd*/
    //status = dup2(1, write_fd);
    status = dup2(write_fd, 1);
    if(status<0)
    {
        exit(-1);
    }
    else
    {
        system(cmd);
        exit(0);
    }
}


int parent_fun(int child_id, int pipe_fd[])
{
    int status = -1;
    int len = 0;
    bool_e break_loop = FALSE;
    int read_fd = pipe_fd[0];       /*read file descriptor*/
    int write_fd = pipe_fd[1];      /*write file descriptor*/

    /*close write fd*/
    close(write_fd);

    while(1)
    {
        sleep(1);
        status = waitpid(child_id, &status, WNOHANG);
        switch(status)
        {
            case 0:
                    /*Child is still active*/
                    printf("No process waiting to exit..\n");
                    len = do_ur_fun(read_fd);
                    write(1, output, len);
                break;
            /*case EINTR:
            case ECHILD:
            case EINVAL:
                    perror("waitpid error");
                    break_loop = TRUE;
                break;*/
            default:
                if(status<0)
                {
                    perror("waitpid error");
                    break_loop = TRUE;
                    len = -1;
                }
                else if(child_id == status)
                {
                    /*Valid staus from child*/
                    len = read_output(read_fd, output);
                    //write(1, output, len);
                    break_loop = TRUE;
                }
                else
                {
                }
                break;
        }
        if(TRUE == break_loop)
        {
            break;
        }
    }
    return len;
}




int do_ur_fun (int read_fd)
{
        /*Do your exec*/
}

1

MaheshGupta024在您的代码中发现了一个非常重要的问题;我假设您会修复它。

另一个问题区域是:

close(1);
if (dup2(pipefd[0], 0)==-1)
    perror("Can't redirect stdin");
close(pipefd[1]);

for (i=1; i< argc; i++){
    if ((wait(NULL))== -1)
        perror("Wait error");

    while ((c= read(pipefd[0], readin, SIZE)) > 0){;
        //print buf count
        total += atoi(readin);
    }
}

第一个close关闭了进程的标准输出,这通常不是一个好主意。下一行将管道的读端复制到标准输入 - 这很好。如上面的评论中所述,perror()不会退出。然后关闭管道的写端 - 这是正确的;但是你应该关闭管道的读端,因为你已经设置它来自管道。

你的循环开始得很好;在wait()行中有多余的括号。你从pipefd[0]而不是标准输入中读取 - 所以也许你不想关闭pipefd[0],但你也不需要将其复制到标准输入。然后你有一个嵌套循环,在子进程还有更多数据要读取时在管道上读取 - 你不绝对需要wait()代码及其循环,因为内部while不会终止,直到所有子进程都死亡。另一方面,这也没有什么大碍 - 在第一个子进程死亡后,你将从所有其他子进程读取数据,然后进入外部循环并等待每个其他子进程,内部循环立即终止,因为没有剩余数据可读取。

所以:

  • 不要关闭stdout。
  • 不要将管道读取复制到stdin。
  • 决定是否要清理循环 - 它可以工作,但可能更干净。

run() 函数是:

void run(char *f){
  int fp;
  if ((fp= open(f, O_RDONLY)) == -1)
      perror("Can't open the file");

  close(pipefd[0]);
  dup2(pipefd[1], 1);
  close(pipefd[1]);
  execlp("ls1", f, NULL);
}

参数应该是const char *f(或者使用namefile代替f)。我还会将pipefd数组传递给函数,而不是使用全局变量。
不要将文件描述符命名为fp;这个名称通常表示FILE *类型的变量,而不是int
但是,您不需要首先打开文件-除非您希望调用程序执行错误报告而不是调用程序。但是,如果您确实希望调用程序执行错误报告,则应在继续之前关闭文件描述符。(我已经评论了perror()返回)。
execlp()之后打印错误消息是一个好主意;函数返回的唯一时间是失败时,因此无需测试其返回值。您可能还想退出-而不是让失败的函数在调用run()后继续执行主程序的其余部分。
好的地方:您确实关闭了两个管道文件描述符。
因此:
void run(const char *file, int *pipefd)
{
    close(pipefd[0]);
    dup2(pipefd[1], 1);
    close(pipefd[1]);
    execlp("ls1", f, NULL);
    perror("Failed to exec ls1");
    exit(EXIT_FAILURE);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接