简体   繁体   English

Unix C Shell-作业控制问题!

[英]Unix C Shell - Job Control Issue!

I've been working on creating my own Unix Shell in C to get practice with its interworkings...I'm having some issues getting my process to run in the background while allowing my shell to continue taking user input . 我一直在努力用C创建自己的Unix Shell,以实现其互操作性…… 在让我的Shell继续接受用户输入的同时,我的程序在后台运行时遇到了一些问题 If you could take the time to dissect what I've got below it would be much appreciated! 如果您能花时间剖析一下我的不足,将不胜感激!

My variables are below, just incase that helps understand things more... 我的变量在下面,以防万一,有助于您进一步了解...

#define TRUE 1

static char user_input = '\0'; 

static char *cmd_argv[5]; // array of strings of command
static int cmd_argc = 0; // # words of command

static char buffer[50]; // input line buffer
static int buffer_characters = 0;
int jobs_list_size = 0;

/* int pid; */
int status;
int jobs_list[50];

Here is my main function. 这是我的主要功能。

int main(int argc, char **argv)
{           
    printf("[MYSHELL] $ ");

    while (TRUE) {
        user_input = getchar();
        switch (user_input) {

            case EOF:
                exit(-1);

            case '\n':
                printf("[MYSHELL] $ ");
                break;

            default:
                // parse input into cmd_argv - store # commands in cmd_argc
                parse_input();

                //check for zombie processes
                check_zombies();

                if(handle_commands() == 0)
                    create_process();
                    printf("\n[MYSHELL] $ ");

        }
    }
    printf("\n[MYSHELL] $ ");
    return 0;
}

Parse Input...I know, I can't get readline to work on this box :( If provided the & operator, create the job in the background... (see below) 解析输入...我知道,我无法在此框上使用readline :(如果提供了运算符,请在后台创建作业...(请参阅下文)

void parse_input()
{
    // clears command line
    while (cmd_argc != 0) {
        cmd_argv[cmd_argc] = NULL;
        cmd_argc--; 
    }

    buffer_characters = 0;

    // get command line input
    while ((user_input != '\n') && (buffer_characters < 50)) {
        buffer[buffer_characters++] = user_input;
        user_input = getchar();
    }

    // clear buffer
    buffer[buffer_characters] = 0x00;

    // populate cmd_argv - array of commands
    char *buffer_pointer;
    buffer_pointer = strtok(buffer, " ");

    while (buffer_pointer != NULL) { 
        cmd_argv[cmd_argc] = buffer_pointer;
        buffer_pointer = strtok(NULL, " ");

        //check for background process execution
        if(strcmp(cmd_argv[cmd_argc], "&")==0){
            printf("Started job %d\n", getpid());    
            make_background_job();
        }

        cmd_argc++;
    }
}

Make background job. 进行后台工作。 Closes child process STDIN, opens new STDIN, and executes. 关闭子进程STDIN,打开新的STDIN,然后执行。

void make_background_job()
{
    int pid;
    pid = fork();
    fclose(stdin); // close child's stdin
    fopen("/dev/null", "r"); // open a new stdin that is always empty

    fprintf(stderr, "Child pid = %d\n", getpid());

    //add pid to jobs list
    jobs_list[jobs_list_size] = getpid();
/*     printf("jobs list %d", *jobs_list[jobs_list_size]);         */
    jobs_list_size++;

    execvp(*cmd_argv,cmd_argv);

    // this should never be reached, unless there is an error
    fprintf (stderr, "unknown command: %s\n", cmd_argv[0]);     
}

The meat of my job control. 我的工作控制的肉。 Fork spawns child, returns 0 for child and PID for parent. Fork产生子代,子代返回0,父代返回PID。

void create_process()
{   
    pid_t pid;

    pid = fork();
    status = 0;

    switch(pid){
        case -1:
            perror("[MYSHELL ] $ (fork)");
            exit(EXIT_FAILURE);
        case 0:            
            make_background_job();
            printf("\n\n----Just made background job in case 0 of create_process----\n\n");        
            break;

        default:
            printf("\n\n----Default case of create_process----\n\n");
            // parent process, waiting on child...
            waitpid(pid, &status, 0);

            if (status != 0) 
                fprintf  (stderr, "error: %s exited with status code %d\n", cmd_argv[0], status);
            else
                break;
    }
}

My problem is when I execute a job in the background, its executing the command twice, and exiting out of the shell. 我的问题是,当我在后台执行作业时,它执行两次命令,然后退出外壳。 (It functions correctly otherwise if no background process is enabled). (否则,如果未启用任何后台进程,它将正常运行)。 Where am I getting confused? 我在哪里感到困惑? I think it may have to do with issues regarding my PID's, as I'm not populating the list correctly either in 'make_background_job' 我认为这可能与我的PID有关,因为我没有在'make_background_job'中正确填充列表

Here is my output, the example.sh just throws out helloWorld: 这是我的输出,example.sh只是抛出helloWorld:

[MYSHELL] $ ./example.sh &
Started job 15479
Child pid = 15479
Child pid = 15481
Hello World
Hello World

What seems to happen is 似乎发生的是

  • in main() the prompt is displayed, expecting a command main()中显示提示,要求输入命令
  • when a command is input, parse_input() is called 输入命令时,将调用parse_input()
  • it builds the commands array until it finds & where it calls make_background_jobs() 它会构建命令数组,直到找到&并调用make_background_jobs()
  • that function forks quickly, and executes in parallel, in two processes, execvp() 该函数快速分叉,并在两个进程execvp()中并行执行
  • execvp() replaces each of the two processes to execute the command execvp()替换两个进程中的每个进程以执行命令
  • thus two "Hello world" appear. 因此出现了两个“ Hello world”。

The problem is in make_background_jobs() where, I think, the expected behavior was that only one of the two processes should execute the command, and the other one (father) returns, to keep the program active. 问题出在make_background_jobs()中,我认为预期的行为是两个进程中只有一个应该执行该命令,而另一个(父亲)返回,以保持程序处于活动状态。

This can be solved by modifying that function, making the father process return: 这可以通过修改该函数来解决,使父进程返回:

    void make_background_job()
    {
      int pid;
      pid = fork();

      if (pid) return; // The father process returns to keep program active
      ...

edit 编辑

I gave it a try, removing the unnecessary 我尝试了一下,删除了不必要的


void make_background_job()
{
    int pid;
    pid = fork();

    if ( ! pid)
    {
      fclose(stdin); // close child's stdin
      fopen("/dev/null", "r"); // open a new stdin that is always empty

      fprintf(stderr, "Child Job pid = %d\n", getpid());

      //add pid to jobs list
      jobs_list[jobs_list_size] = getpid();
  /*     printf("jobs list %d", *jobs_list[jobs_list_size]);         */
      jobs_list_size++;

      execvp(*cmd_argv,cmd_argv);

    // this should never be reached, unless there is an error
      fprintf (stderr, "unknown command: %s\n", cmd_argv[0]);     
      exit(1);
    }

    waitpid(pid, &status, 0);
}

The background job is created in another process. 后台作业是在另一个过程中创建的。 The father waits for the job to complete. 父亲等待工作完成。


void parse_input()
{
    // clears command line
    while (cmd_argc != 0) {
        cmd_argv[cmd_argc] = NULL;
        cmd_argc--; 
    }

    buffer_characters = 0;

    // get command line input
    while ((user_input != '\n') && (buffer_characters < 50)) {
        buffer[buffer_characters++] = user_input;
        user_input = getchar();
    }

    // clear buffer
    buffer[buffer_characters] = 0x00;

    // populate cmd_argv - array of commands
    char *buffer_pointer;
    buffer_pointer = strtok(buffer, " ");

    int ok = 0;

    while (buffer_pointer != NULL) { 
        cmd_argv[cmd_argc] = buffer_pointer;
        buffer_pointer = strtok(NULL, " ");

        //check for background process execution
        if(strcmp(cmd_argv[cmd_argc], "&")==0){
          ok = 1;
          break;
        }

        cmd_argc++;
    }

    if (!ok) cmd_argv[cmd_argc = 0] = NULL; // If no & found, reset commands
}

Only parses input. 仅解析输入。

Below a new handle_commands() that return 0 if there is a command to play, and the main follows. 在新的handle_commands()下方,如果有要播放的命令,则返回0 ,随后是main


int handle_commands() { return cmd_argc > 0 ? 0:1; }

int main(int argc, char **argv)
{           
    printf("[MYSHELL] $ ");

    while (TRUE) {
        user_input = getchar();
        switch (user_input) {

            case EOF:
                exit(-1);

            case '\n':
                printf("[MYSHELL] $ ");
                break;

            default:
                // parse input into cmd_argv - store # commands in cmd_argc
                parse_input();

                //check for zombie processes
                check_zombies();

                if(handle_commands() == 0)
                    make_background_job();  // Call directly the bg job
                    printf("\n[MYSHELL] $ ");

        }
    }
    printf("\n[MYSHELL] $ ");
    return 0;
}

The main() calls directly make_background_job() . main()直接调用make_background_job()

There is only one fork() in make_background_job. make_background_job中只有一个fork() create_process() has been removed. create_process()已被删除。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM