c - multi-pipe() C 程序中的无限循环

标签 c pipe fork exec

我有一个程序,需要一个由 executeCommand3 执行的命令列表。目标是执行当前存储在该列表中的命令列表,但将来将在类似 shell 的环境中键入要执行的命令,代码如下:

MAIN.c

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>    //for getpid()
#include <sys/types.h> // for pid_t

#include "routines.h" //for executeCommand3()

#define SUB_PROCESSES_NUMBER 2

char *command0[] = {"ls", "tos", NULL};
char *command1[] = {"wc", NULL}; 
char *command2[] = {"wc", NULL};
char **commands[SUB_PROCESSES_NUMBER + 1] = {command0, command1, command2};

int main()
{   

    char input[1024];
    printf("scan: ");
    fgets(input,1024,stdin);
    int value=0;
    while((strcmp(input,"q\n")!=0))
    {
        value=executeCommand3(commands, SUB_PROCESSES_NUMBER+1);

        char buffer[1024];
        printf("value %i\nscan: ",value);
        fgets(input,1024,stdin);
        fgets(buffer,1024,stdin);
        printf("%s\n",input);
    }
    return 0;
}

例程.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h> // for open, close,read,write on FD
#include <error.h>     // for error handling
#include <sys/types.h> // structs like time_t
#include <sys/wait.h>  // wait, waitpid, waitid - wait for process to change state

#include "routines.h"

#define READ 0  // for file descriptor index
#define WRITE 1 // for file descriptor index

#define STDIN 0
#define STDOUT 1

int executeCommand3(char ***args, int processNumb)
{
    pid_t pidList[processNumb];    //list of id of cmd processes (one for each child)
    int fdBackLog[processNumb][2]; // list of file desrciptors relative for each cmd (a pair for each child)

    int lastProcessFlag = 0;
    int i;
    for (i = 0; i < processNumb; i++) //cycle through the list of commands
    {
        if ((i + 1) == processNumb){
            lastProcessFlag = 1;
        }

        if (lastProcessFlag != 1)
        {
            int retPipe = pipe(fdBackLog[i]);
            if (retPipe < 0) // generating pipe for comunication between cmd(i) and cmd(i+1)
            {
                perror("pipe error");

                if (i > 0)
                {
                    close(fdBackLog[i - 1][READ]); 
                }
                exit(EXIT_FAILURE); //exit with failure code
            }
        }

        pidList[i] = fork();

        if (pidList[i] < 0) // error fork
        {
            perror("error fork()");

            if (i > 0)
            {                                  
                close(fdBackLog[i - 1][READ]); 
            }
            close(fdBackLog[i][READ]);  // close the read pipe end of cmd(i)
            close(fdBackLog[i][WRITE]); //close the write pipe end of cmd(i)
            exit(EXIT_FAILURE);
        }

        if (pidList[i] == 0) // CHILD PROCESS
        {
            printf("children %i process parent %i for: %s \n", getpid(), getppid(), args[i][0]);
            if (lastProcessFlag != 1)
            {


                close(fdBackLog[i][READ]); 

                dup2(fdBackLog[i][WRITE], STDOUT);
                close(fdBackLog[i][WRITE]); // duplicated pipes are not useful any more

            }
            else
            {
                close(fdBackLog[i][WRITE]); //last process has nothing to write on pipe
            }

            if (i > 0)
            {
                // Also need to redirect stdin if this is not first process
                dup2(fdBackLog[i - 1][READ], STDIN);
                close(fdBackLog[i - 1][READ]);
            }

            int exitValue = execvp(args[i][0], args[i]); 
            perror(args[i][0]);
            exit(EXIT_FAILURE); // Should not be reached;
        }

        close(fdBackLog[i][WRITE]); 
        if (i > 0)
        {
            close(fdBackLog[i - 1][READ]);
        }
    }

    int wPid, status;

    while((wPid = wait(&status))>0)
    {
        printf("Child #%i (%i)\n", wPid, status);
    }
}

我的问题是错误处理,实际上第一个命令列表的第二项是一些随机单词。

当我执行命令时,输出很好(就像在普通 shell 中一样),但问题是 MAIN.c 中的 while 循环继续调用函数 executeCommand3()。我认为这可能会发生,因为 executeCommand3() 中的某些进程在 MAIN.c 的标准输入中留下了一些内容,因此 fgets 从流中获取它并保留 cicle while 循环的。所以我添加了另一个 fget 来中断循环,但问题仍然存在......

如何修复程序,以便即使 executeCommand3() 出现错误,while 循环也会停止并且我可以正常重做循环? 问题出在 executeCommand3() 函数中(可能在某些 dup2()pipe()...)或 MAIN.c

最佳答案

一个问题是您没有对 fgets() 调用进行错误检查。如果这样做,您会发现 stdin 已关闭。以下是一些以 fcntl() 调用形式进行诊断的代码,该调用检查标准输入文件描述符是否仍然有效:

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

#define READ 0
#define WRITE 1

#define STDIN 0
#define STDOUT 1

#define SUB_PROCESSES_NUMBER 2

void executeCommand3(char ***args, int processNumb);

char *command0[] = {"ls", "tos", NULL};
char *command1[] = {"wc", NULL};
char *command2[] = {"wc", NULL};
char **commands[SUB_PROCESSES_NUMBER + 1] = {command0, command1, command2};

int main(void)
{
    char input[1024];
    printf("scan: ");
    fgets(input, 1024, stdin);
    if (fcntl(0, F_GETFD, 0) < 0)
    {
        perror("fcntl() - 1");
        exit(1);
    }
    while ((strcmp(input, "q\n") != 0))
    {
        executeCommand3(commands, SUB_PROCESSES_NUMBER + 1);
        char buffer[1024];
        printf("\nscan: ");
        if (fcntl(0, F_GETFD, 0) < 0)
        {
            perror("fcntl() - 2");
            exit(1);
        }
        fgets(input, 1024, stdin);
        fgets(buffer, 1024, stdin);
        printf("%s\n", input);
    }
    return 0;
}

void executeCommand3(char ***args, int processNumb)
{
    pid_t pidList[processNumb];
    int fdBackLog[processNumb][2];

    int lastProcessFlag = 0;
    int i;
    for (i = 0; i < processNumb; i++)
    {
        if ((i + 1) == processNumb)
        {
            lastProcessFlag = 1;
        }

        if (lastProcessFlag != 1)
        {
            int retPipe = pipe(fdBackLog[i]);
            if (retPipe < 0)
            {
                perror("pipe error");
                if (i > 0)
                {
                    close(fdBackLog[i - 1][READ]);
                }
                exit(EXIT_FAILURE);
            }
        }

        pidList[i] = fork();

        if (pidList[i] < 0)
        {
            perror("error fork()");
            if (i > 0)
            {
                close(fdBackLog[i - 1][READ]);
            }
            close(fdBackLog[i][READ]);
            close(fdBackLog[i][WRITE]);
            exit(EXIT_FAILURE);
        }

        if (pidList[i] == 0)
        {
            printf("children %i process parent %i for: %s \n", getpid(), getppid(), args[i][0]);
            if (lastProcessFlag != 1)
            {
                close(fdBackLog[i][READ]);
                dup2(fdBackLog[i][WRITE], STDOUT);
                close(fdBackLog[i][WRITE]);
            }
            else
            {
                close(fdBackLog[i][WRITE]);
            }
            if (i > 0)
            {
                dup2(fdBackLog[i - 1][READ], STDIN);
                close(fdBackLog[i - 1][READ]);
            }

            execvp(args[i][0], args[i]);
            perror(args[i][0]);
            exit(EXIT_FAILURE);
        }

        close(fdBackLog[i][WRITE]);
        if (i > 0)
        {
            close(fdBackLog[i - 1][READ]);
        }
    }

    int wPid, status;

    while ((wPid = wait(&status)) > 0)
    {
        printf("Child #%i (%i)\n", wPid, status);
    }
}

当我在没有子目录 tos 的目录中运行该程序时,我得到如下输出(我的程序版本称为 pipe71):

$ ./pipe71
scan: pifflebunk
children 35712 process parent 35711 for: ls 
children 35713 process parent 35711 for: wc 
children 35714 process parent 35711 for: wc 
ls: tos: No such file or directory
Child #35712 (256)
Child #35713 (0)
       1       3      25
Child #35714 (0)

fcntl() - 2: Bad file descriptor
scan: $
$

请注意 fcntl() - 2: Bad 文件描述符 行。这表示executeCommand3() 函数中的某些内容关闭了父进程的标准输入。这可能不是您想要的。您可能应该在调用 executeCommand3() 之前进行 fork,然后从子级调用该函数,让父级等待。或者您可以在 executeCommand3() 中更加小心地了解关闭标准输入的位置。

关于c - multi-pipe() C 程序中的无限循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50533598/

相关文章:

C程序在被告知之前执行命令

c - #define 为无符号长整型

c - 兄弟管道比父子管道安全吗?

c - 向家长发送信号不起作用

c - fork 没有在母亲身上显示 child 的 pid

c - 了解 fork() 语句及其进程树

c - wcscpy 不接受目标变量中的 TCHAR

c++ - OOP 中的 GLFWwindowsizefun 可访问性

java - 如何使用命令提示符在两个 jar 文件之间创建管道?

c - 第三个进程 "wc"将无法​​工作