代码之家  ›  专栏  ›  技术社区  ›  rybosome

管道和工艺

  •  6
  • rybosome  · 技术社区  · 16 年前

    编写一个程序,向用户查询两个输入字符串。每个输入字符串都应该是一个unix命令,并且允许参数。例如,输入1可以是 ls -l 输入2可能是 more . 然后程序将创建一个管道和两个子进程。第一个子进程将运行第一个输入中指定的命令。它将输出到管道而不是标准输出。第二个子进程将运行第二个输入中指定的命令。它将从管道中获取输入,而不是标准输入。父进程将等待其两个子进程完成,然后整个过程将重复。当输入“@”符号作为第一个命令时,执行将停止。以下是我的代码:

    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    int main(){
    
        /* Program Termination Symbol */
        const char terminate = '@';
    
        /* String delimiter */
        const char delimiter = ' ';
    
        /* Pipe file ID's */
        int fileID[2];
    
        /* Parent ID's */
        int pid1, pid2;
    
        /* String token */
        char * token, * token2;
    
        /* User input */
        char * user_input, line[100];
    
        user_input = (char *) malloc(100);
    
        /* Unix Commands */
        char * command1[10], *command2[10];
    
        for (int i=0; i<10; i++)
        {
        command1[i] = (char *)malloc(100*sizeof(char));
        command2[i] = (char *)malloc(100*sizeof(char));
        }
    
        /* Begin main program logic */
    
        printf("Please enter the first command: \n");
    
        user_input = gets(line);
    
        while (user_input[0] != terminate)
        {
        token = (char *) malloc(100*sizeof(char));
    
        for (int i=0; i<10; i++)
            {
            if (i == 0)
            {
            token = strtok(user_input, &delimiter); 
            } else {
            token = strtok(NULL, &delimiter);
            }
    
            if (token != NULL)
            {
            strcpy(command1[i], token);
            } else {
            command1[i] = 0;
            }
            }
    
        printf("Please enter the second command: \n");  
        user_input = gets(line);
    
        token2 = (char *) malloc(100*sizeof(char));
    
        for (int i=0; i<10; i++)
        {
            if (i == 0)
            {
            token2 = strtok(user_input, &delimiter);
            } else {
            token2 = strtok(NULL, &delimiter);
            }
    
            if (token2 != NULL)
            {
            strcpy(command2[i], token2);
            } else {
            command2[i] = 0;
            }
        }   
    
    
        /* Pipe and execute user commands */
    
        /* Create pipe */
        pipe(fileID);
    
        /* Create child processes */
    
        pid1 = fork();
    
        if (pid1 != 0)
        {
            pid2 = fork();
        }
    
        /* First child process */
        if (pid1 == 0)
        {
            dup2(fileID[1], 1);
            execvp(command1[0], command1);
        }
    
        /* Second child process */
        if (pid2 == 0)
        {
            dup2(fileID[0], 0);
            execvp(command2[0], command2);
        }   
    
        /* Wait for children to terminate */
        wait(&pid1);
        wait(&pid2);
    
        /* Repeat */
            printf("Please enter the first command: \n");
        user_input = gets(line);
        }
    
        return 0;
    }
    

    我遇到的问题是我的等待。如果两个都有,这对我来说是有意义的(每个孩子等待一次),那么程序在执行第一个管道后就会冻结。如果我删除第二个等待,那么程序将再次开始循环,但将不接受除enter以外的键盘输入,并将产生segfault。所以,在两种等待下,输入和输出都是。。。

    Please enter the first command:
    ls
    Please enter the second command:
    more
    Pipe
    Pipe.c
    Pipe.c~
    

    …然后锁起来。如果我删除第二个等待,输入/输出是。。。

    Please enter the first command:
    ls
    Please enter the second command:
    more
    Pipe
    Pipe.c
    Pipe.c~
    Please enter the first command:
    (I hit enter, nothing else will work)
    Segmentation fault
    

    有人有什么建议吗?这显然与等待这两个过程有关,但我不知道如何处理它。


    3 回复  |  直到 12 年前
        1
  •  5
  •   Brandon Horsley    16 年前

    我同意托拉克所说的一切,但为了解决你的问题,你需要关闭你的管道。我想你是在“吊”因为管子还开着。

    所以在父级,在“等待”之前,我会关闭管道。

    close(fileID[0]);
    close(fileID[1]);
    wait(&pid_status);
    wait(&pid_status);
    

    然后,在每个execvp之前,我会关闭孩子不会使用的管道的末端:

    close(fileID[0]);
    dup2(fileID[1], 1);
    execvp(command1[0], command1);
    
    
    close(fileID[1]);
    dup2(fileID[0], 0);
    execvp(command2[0], command2);
    

    那会解决你的绞刑问题。除了torak提出的建议之外,我还建议使用fgets而不是get来防止缓冲区溢出。

        2
  •  4
  •   torak    16 年前

    1. wait http://linux.die.net/man/2/wait 它不接受pid指针作为参数。

    2. token token2 ,但我没有看到相应的内存释放。

    3. 最后,可能与第3点有关,下面两行代码在代码中出现两次。再说一遍,这不是一个错误,而是不必要的重复和不雅。

      printf(“请输入第一个命令:\n”);

        3
  •  1
  •   rbp zifot    16 年前

    首先,你在打电话 wait 也从子进程[edit:no,您不是,因为每个子进程都调用execvp]。

    也, 等待 不会将指针指向子进程的pid,而是指向进程状态将写入的变量(这意味着您将丢弃子进程的pid)。

    waitpid 使用“WNOHANG”选项。它不会挂起,您可以在执行其他操作时将两者都放在一个循环中,并且可以通过检查状态变量来检查子进程是否已退出。 man waitpid .