2011-02-18 23 views
0

我一直在C語言中創建自己的Unix Shell以獲得其交互練習......我有一些問題讓我的進程在後臺運行,同時允許我的shell繼續使用用戶輸入。如果你可以花時間解剖下面的東西,我將非常感激!Unix C Shell - 作業控制問題!

我的變量如下面,只是櫃面,可以幫助瞭解的東西更多...

#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]; 

這是我的主要功能。

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; 
} 

解析輸入...我知道,我不能讓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++; 
    } 
} 

製作後臺作業。關閉子進程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]);  
} 

我的工作控制的肉。叉產生孩子,爲孩子返回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; 
    } 
} 

我的問題是,當我在後臺執行作業,其執行命令兩次,退出出殼。 (如果沒有啓用後臺進程,它會正常工作,否則)。我在哪裏感到困惑?我認爲它可能有問題,就做我的PID的,因爲我也不在「make_background_job」

這裏是我的輸出正確填充列表中,example.sh剛剛拋出的helloWorld:

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

回答

3

有什麼事發生的是

  • main()顯示提示,期待一個命令
  • 當輸入命令時,parse_input()
  • 它建立的命令陣列,直到它找到&其中它調用make_background_jobs()
  • 迅速起作用叉,並執行並行地,在兩道工序,execvp()
  • execvp()替換每個兩個過程來執行命令
  • 因此出現兩個「Hello World」。

問題出在哪裏make_background_jobs(),我認爲,預期的行爲是,只有兩個進程中的一個應該執行命令,而另一個(父親)的回報,以保持節目活躍。

這可以通過修改功能,使父進程返回來解決:

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

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

編輯

我給它一個嘗試,在去除不需要


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); 
} 

的後臺作業是在另一個進程中創建的。父親等待工作完成。


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 
} 

只解析輸入。

如果有要播放的命令並且main跟在後面,則返回0的新的​​的下方。


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; 
} 

main()直接調用make_background_job()

make_background_job中只有一個fork()create_process()已被刪除。

+0

我覺得我的任務分離不正確;我應該在我的shell中有兩個fork調用嗎? IE,是否有理由在make_background_job中具有該功能,還是應該在create_process中處理該邏輯?我迷路了! :( – user546459 2011-02-18 21:54:41