我有一个程序,期望由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;
}


例程

#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的stdin中留下了一些内容,因此fgets从流中获取它并保留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 file descriptor行。也就是说,executeCommand3()函数中的某些内容关闭了父进程的标准输入。这可能不是您想要的。可能您应该在调用executeCommand3()之前先进行分叉,然后从子级调用该函数,而让父级等待。或者,您可能在executeCommand3()中更注意标准输入的关闭位置。

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

10-11 18:41