UNIX:使用fork()和dup2()在循环中运行进程会提前结束循环

时间:2018-03-12 22:42:33

标签: c unix

我开始在UNIX中编写shell来练习API调用,例如fork()dup2(),read()和wait()。目前,我的shell打开并运行正常。当我键入要运行的命令(例如ls -a)时,它会正确解析此命令并执行它。问题是,主循环提前终止,在单个命令后退出shell。我需要循环继续运行,直到从stdin读取'exit'。这是我目前的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <unistd.h>
static const char prompt[] = "myshell> ";
static const char sep[] = " \t\n\r";
int main()
{
    int ac; // arg count
    char *av[10]; //argument vector
    int tty = open("/dev/tty", O_RDWR); // open tty for read/write
    int pid; // process id
    int status; // child process exit status
    int w;
    void (*istat)(int), (*qstat)(int);

    if (tty == -1)
    {
        fprintf(stderr, "can't open /dev/tty\n");
        exit(EXIT_FAILURE);
    }
    while (1)
    {
        char *arg, line[256]; // buffer to hold line of input
        int i;
        // prompt and read
        write(tty, prompt, sizeof(prompt) - 1);
        i = read(tty, line, sizeof(line));
        if (i <= 0)
            break;
        line[i] = '\0';
        // tokenize the line into av[]
        ac = 0;
        for (arg = strtok(line, sep); arg && ac < 10; arg = strtok(NULL, sep))
            av[ac++] = arg;

        if (ac > 0 && strcmp(av[0], "exit") == 0)
            break;

        if ((pid = fork()) == 0)
        {
            // this is the forked child process that is a copy of the running program
            dup2(tty, 0); // stdin from tty
            dup2(tty, 1); // stdout to tty
            dup2(tty, 2); // stderr to tty
            close(tty);
            // last argument must be NULL for execvp()
            av[ac] = NULL;
            // execute program av[0] with arguments av[0]... replacing this program
            execvp(av[0], av);
            fprintf(stderr, "can't execute %s\n", av[0]);
            exit(EXIT_FAILURE);
        }
        close(tty);
        // disable interrupt (^C and kill -TERM) and kill -QUIT
        istat = signal(SIGINT, SIG_IGN);
        qstat = signal(SIGQUIT, SIG_IGN);
        // wait until forked child process terminated, get its exit status
        while ((w = wait(&status)) != pid && w != -1)
            continue;
        if (w == -1)
            status = -1;

    }

    // restore interrupt and quit signals
    signal(SIGINT, istat);
    signal(SIGQUIT, qstat);
    exit(EXIT_SUCCESS);
}

我尝试在退出成功之上移动这些线(因此它们在循环之外和主要内部)

    close(tty);
    // disable interrupt (^C and kill -TERM) and kill -QUIT
    istat = signal(SIGINT, SIG_IGN);
    qstat = signal(SIGQUIT, SIG_IGN);
    // wait until forked child process terminated, get its exit status
    while ((w = wait(&status)) != pid && w != -1)
        continue;
    if (w == -1)
        status = -1;

}

// restore interrupt and quit signals
    signal(SIGINT, istat);
    signal(SIGQUIT, qstat);
    exit(EXIT_SUCCESS);
}

这样做是正确循环shell,但是在执行命令的输出的最后一行上方打印提示,因为程序在打印提示并等待输入之前没有等待子进程结束。我尝试移动一些线路,但每次执行一个命令后它最终都会终止shell

2 个答案:

答案 0 :(得分:2)

您不想在主循环中调用close(tty)。这样做会导致下一个read(tty,...失败,退出shell。

此外,如果你真的想要禁用SIGQUIT / SIGQUIT,你应该在循环中对称地恢复它们。

答案 1 :(得分:0)

以下发布的代码:

  1. 跳过信号的处理,你可能想要重新加入。
  2. 干净地编译
  3. 正确检查并记录任何错误
  4. 执行所需功能,但禁用某些信号
  5. 除外
  6. 继续执行循环,直到用户输入&#39;退出&#39;
  7. 现在,建议的代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <fcntl.h>
    
    // added next statement for 'wait()' and 'waitpid()'
    #include <sys/types.h>
    
    #include <sys/wait.h>
    #include <unistd.h>
    //#include <signal.h>
    #include <unistd.h>
    
    static const char prompt[] = "myshell> ";
    static const char sep[] = " \t\n\r";
    
    
    int main( void )
    {
        int ac; // arg count
        char *av[10]; //argument vector
    
        pid_t pid; // process id
        int status; // child process exit status
    
    
        int tty = open("/dev/tty", O_RDWR); // open tty for read/write
        if (tty == -1)
        {
            perror( "open for /dev/tty failed");
            exit(EXIT_FAILURE);
        }
    
        while (1)
        {
            char *arg;
            char line[256]; // buffer to hold line of input
    
            // prompt and read
            write(tty, prompt, sizeof(prompt) - 1);
            ssize_t i = read(tty, line, sizeof(line));
    
            if (i == 0)
                break;
    
            if (i < 0)
            {
                perror( "read failed" );
                exit( EXIT_FAILURE );
            }
    
            line[i] = '\0';
    
            // tokenize the line into av[]
            ac = 0;
            for (arg = strtok(line, sep); arg && ac < 10; arg = strtok(NULL, sep))
                av[ac++] = arg;
    
            // last argument must be NULL for execvp()
            av[ac] = NULL
    
            if (ac > 0 && strcmp( av[0], "exit" ) == 0)
                break;
    
            pid = fork();
            switch( pid )
            {
            case -1:
                perror( "fork failed" );
                exit( EXIT_FAILURE );
                break;
    
            case 0:  // child process
                // this is the forked child process that is a copy of the running program
                dup2(tty, 0); // stdin from tty
                dup2(tty, 1); // stdout to tty
                dup2(tty, 2); // stderr to tty
                close(tty);
                ;
    
                // execute program av[0] with arguments av[0]... replacing this program
                execvp(av[0], av);
                perror( "execvp failed" );
                exit(EXIT_FAILURE);
                break;
    
            default: // parent process
                // wait until forked child process terminated, get its exit status
                waitpid( pid, &status, 0 );
                break;
            }
        }
    
        return 0;
    }