管道和流程

发布于 2024-09-15 11:50:52 字数 3288 浏览 11 评论 0原文

前提: 编写一个程序来向用户查询两个输入字符串。每个输入字符串应该是一个 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 之外的键盘输入,并且会产生段错误。因此,在等待过程中,输入和输出都是……

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

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


该程序现已 100% 正常运行 - 非常感谢大家的帮助!堆栈溢出一直是互联网上最好的资源之一。非常感谢大家花时间查看我的代码并给我建议。

Premise:
Write a program to query the user for two input strings. Each input string should be a unix command, with arguments allowed. For example, input 1 could be ls -l and input 2 could be more. The program will then create a pipe, and two child processes. The first child process will run the command specified in the first input. It will output to the pipe instead of standard output. The second child process will run the command specified in the second input. It will take its input from the pipe rather than standard input. The parent process will wait on its two children to complete, then the whole thing will repeat. Execution will stop when the '@' symbol is entered as the first command. Here is the code I have:

#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;
}

The problem I'm running into is with my waits. If I have both, which would make sense to me (one wait per child) then the program freezes after executing the first pipe. If I remove the second wait, then the program will begin its loop again, but will not accept keyboard input other than enter, and will produce a segfault. So, with both waits, input and output is...

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

...and then it locks up. If I remove the second wait, input/output is...

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

Anyone have any suggestions? It's clearly related to waiting on the two processes, but I'm at a loss as to how to handle it.


This program is now 100% functional - thank you so much for your help, everyone! Stack overflow has been one of the best resources on the internet. Thank you all so much for taking the time to look over my code and give me your suggestions.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

一笔一画续写前缘 2024-09-22 11:50:52

我同意托拉克所说的一切,但要解决你的问题,你需要关闭管道。我认为你“悬而未决”,因为管道仍然打开。

因此,在父级中,在“等待”之前,我会关闭管道。

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 而不是 gets 来防止缓冲区溢出。

I agree with everything torak said, but to address your problem, you need to close your pipes. I think you are "hanging" because the pipe is still open.

So in the parent, right before the "waits", I would close the pipes.

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

Then, right before each execvp, I would close the ends of the pipe the child will not be using:

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


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

That should resolve your hanging. In addition to the suggestions made by torak, I would also recommend fgets instead of gets to prevent a buffer overflow.

一向肩并 2024-09-22 11:50:52

有几件事。不确定它们是否是造成您问题的原因,但在提交作业之前仍然需要考虑一些事情。

  1. 我认为您没有正确使用wait。根据http://linux.die.net/man/2/wait不将 pid 指针作为参数。

  2. 循环时,您都会为 tokentoken2 调用 malloc,但我没有看到相应的内存释放。

  3. 您已经编写了一个整体函数。良好的编码实践建议将其分解为子例程的集合

  4. 最后,并且它可能与第 3 点相关,以下两行代码在您的代码中出现了两次。再说一遍,这不是错误,而是不必要的重复,而且不优雅。

    printf("请输入第一条命令:\n");
    user_input = gets(line);

A couple of things. Not sure that they are the cause of your problems, but still things to consider before submitting your homework.

  1. I don't think you are using wait correctly. According to http://linux.die.net/man/2/wait it doesn't take pid pointer as an argument.

  2. Each time around the loop you call malloc for token and token2, but I don't see a corresponding release of the memory.

  3. You've writen a single monolithic function. Good coding practice would suggest breaking it out into a collection of subroutines

  4. Finally, and its possibly related to point 3, the following two lines of code appear twice in your code. Again it's not an error, but unnecessary duplication, and inelegant.

    printf("Please enter the first command: \n");
    user_input = gets(line);

却一份温柔 2024-09-22 11:50:52

首先,您也在子进程中调用 wait [编辑:不,您没有,因为每个子进程都调用 execvp]。

另外,wait 并不采用指向子进程 pid 的指针,而是指向将写入进程状态的变量(这意味着您将丢弃子进程的 pid)。

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

First of all, you're calling wait from the child processes as well [edit: no, you're not, since each child calls execvp].

Also, wait doesn't take a pointer to the child's pid, but to a variable where the process's status will be written to (which means you're throwing away your child's pid).

Finally, try using waitpid with the "WNOHANG" option. It won't hang, you can put both on a loop while you do other stuff, and you can check to see if the child processes have exited by inspecting the status variables. man waitpid.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文