编写自己的shell代码在处理某些管道时挂起 - 在C中

Byr*_*sco 2 c linux redirect fork pipe

我现在正在编写自己的shell作为一个类的项目,并且一切都在工作.我的问题是我的管道,有时他们工作,有时,他们只是挂起,直到我打断他们.我已经对此进行了研究,似乎写入stdin的函数没有从第一个进程接收到EOF; 通常我已经知道问题是管道没有被关闭,但我的代码并不是这种情况(据我所知).

所有重定向工作及其任何变化:

  • ls -l > file1
  • wc < file1 > file2

以下管道命令有效:

  • w | head -n 4
  • w | head -n 4 > file1

这不起作用:ls | grep file1它显示正确的输出并且永远不会结束,除非用户向其发送中断信号.ls | grep file1 > file2也行不通.它挂起而不显示输出,创建file2,但从不写入它.

无论如何,我希望有一些我错过了别人可以注意到的东西; 我已经有一段时间了.如果我能提供更多代码,请告诉我.我在下面发布的代码是主文件,没有删除.

/*
 * This code implemenFts a simple shell program
 * At this time it supports just simple commands with 
 * any number of args.
 */

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>

#include "input.h"
#include "myShell.h"
#include "BackgroundStack.h"

/*
 * The main shell function
 */ 
main() {
    char *buff[20];
    char *inputString;

    BackgroundStack *bgStack = malloc(sizeof(BackgroundStack));
    initBgStack(bgStack);

    struct sigaction new_act;
    new_act.sa_handler = sigIntHandler;
    sigemptyset ( &new_act.sa_mask );
    new_act.sa_flags = SA_RESTART;
    sigaction(SIGINT, &new_act, NULL);

    // Loop forever
    while(1) {
        const char *chPath;

        doneBgProcesses(bgStack);

        // Print out the prompt and get the input
        printPrompt();

        inputString = get_my_args(buff);
        if (buff[0] == NULL) continue;

        if (buff[0][0] == '#') continue;

        switch (getBuiltInCommand(buff[0])) {
            case EXIT:
                exit(0);
                break;
            case CD:
                chPath = (buff[1]==NULL) ? getenv("HOME") : buff[1];
                if (chdir(chPath) < 0) {
                    perror(": cd");
                }
                break;
            default:
                do_command(buff, bgStack);
        }

        //free up the malloced memory
        free(inputString);
    }// end of while(1)
}

static void sigIntHandler (int signum) {}

/* 
 * Do the command
 */
int do_command(char **args, BackgroundStack *bgStack) {
    int status, statusb;  
    pid_t child_id, childb_id;
    char **argsb;
    int pipes[2];

    int isBgd = isBackgrounded(args);
    int hasPipe = hasAPipe(args);

    if (isBgd) removeBackgroundCommand(args);
    if (hasPipe) {
        int cmdBi = getSecondCommandIndex(args);
        args[cmdBi-1] = NULL;
        argsb = &args[cmdBi];
        pipe(pipes);
    }

    // Fork the child and check for errors in fork()
    if((child_id = fork()) == -1) {
        switch(errno) {
            case EAGAIN:
                perror("Error EAGAIN: ");
                return;
            case ENOMEM:
                perror("Error ENOMEM: ");
                return;
        }
    }

    if (hasPipe && child_id != 0) {
        childb_id = fork();
        if(childb_id == -1) {
            switch(errno) {
                case EAGAIN:
                    perror("Error EAGAIN: ");
                    return;
                case ENOMEM:
                    perror("Error ENOMEM: ");
                    return;
            }
        }
    }

    if(child_id == 0 || (childb_id == 0 && hasPipe)) {
        if (child_id != 0 && hasPipe) args = argsb;
        if (child_id == 0 && isBgd) {
            struct sigaction new_act;
            new_act.sa_handler = SIG_IGN;
            sigaction(SIGINT, &new_act, 0);
        }

        if (child_id == 0 && hasPipe) {
            if (dup2(pipes[1], 1) != 1) fatalPerror(": Pipe Redirection Output Error");
            close(pipes[0]);
            close(pipes[1]);
        }
        if (child_id != 0 && hasPipe) {
            if (dup2(pipes[0], 0) != 0) fatalPerror(": Pipe Redirection Input Error");
            close(pipes[0]);
            close(pipes[1]);
            waitpid(child_id, NULL, 0);
        }

        if ((child_id != 0 && hasPipe) || !hasPipe) {
            if (hasAReOut(args)) {
                char outFile[100];
                getOutFile(args, outFile);

                int reOutFile = open(outFile, O_RDWR|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE);
                if (reOutFile<0) fatalPerror(": Redirection Output Error");

                if (dup2(reOutFile,1) != 1) fatalPerror(": Redirection Output Error");
                close(reOutFile);
            }
        }

        if ( (child_id == 0 && hasPipe) || !hasPipe) {
            if (hasAReIn(args)) {
                char inFle[100];
                getInFile(args, inFle);

                int reInFile = open(inFle, O_RDWR);
                if (reInFile<0) fatalPerror(": Redirection Input Error");

                if (dup2(reInFile,0) != 0) fatalPerror(": Redirection Input Error");
                close(reInFile);
            } else if (isBgd && !hasPipe) {
                int bgReInFile = open("/dev/null", O_RDONLY);
                if (bgReInFile<0) fatalPerror(": /dev/null Redirection Input Error");

                if (dup2(bgReInFile,0) != 0) fatalPerror(": /dev/null Redirection Input Error");
                close(bgReInFile);
            }
        }

        // Execute the command
        execvp(args[0], args);
        perror(args[0]);

        exit(-1);
    }

    // Wait for the child process to complete, if necessary
    if (!isBgd) waitpid(child_id, &status, 0);
    else if (!hasPipe) {
        printf("Child %ld started\n", (long)child_id);
        BackgroundProcess *bgPrs = malloc(sizeof(BackgroundProcess)); 
        bgPrs->pid = child_id;
        bgPrs->exitStatus = -1;

        addProcessToBgStack(bgStack, bgPrs);
    }
    if (hasPipe) waitpid(childb_id, &statusb, 0);
    if ( WIFSIGNALED(status) && !isBgd )    printf("Child %ld terminated due to signal %d\n", (long)child_id, WTERMSIG(status) );
    if ( hasPipe && WIFSIGNALED(statusb) ) printf("Child %ld terminated due to signal %d\n", (long)childb_id, WTERMSIG(status) );

} // end of do_command
Run Code Online (Sandbox Code Playgroud)

caf*_*caf 7

第二个孩子应该不是等待第一个孩子退出-它应该只是开始运行直线距离(它会阻塞,直到有输出由第一个孩子在管道生产),因此删除waitpid()的执行childb.相反,父进程应该等待两个子进程(或者可能只是第二个进程).(事实上​​,正如JeremyP所指出的那样,这个waitpid()电话无论如何都是失败的,因为childb它不是父母的child).

但是,您的问题是父进程主要向管道提供打开的文件描述符.在注释之前// Wait for the child process to complete, if necessary,父进程应该关闭其管道文件描述符:

close(pipes[0]);
close(pipes[1]);
Run Code Online (Sandbox Code Playgroud)

父级中的打开文件描述符意味着子grep进程永远不会看到EOF,因此它不会退出.