0

自分の手作りシェルを実装するのに少し問題があります。プロセスをフォークして、waitpidを使用してフォアグラウンドで実行することはできましたが、「sleep 5&」などの単純なプロセスをバックグラウンドで実行しようとすると、プロセスが永久に実行されるように見えます。checkListJobsは、プロセスの実行が完了したかどうかを判断しますが、停止することはありません。どんな助けでも大歓迎です。エラーは私の「foo」関数にあると思います。

void insertJob(int pid) {
    printf("beginning job %d.\n", pid);
    struct job *node = malloc(sizeof(struct job));
    node->pid = pid;
    node->next = NULL;

    if(root == NULL) {
        root = node;
    } else {
        node->next = root;
        root = node;
    }
}    

void checkListJobs(int z) {
    curr = root;
    while(curr!=NULL) {
        if(kill(curr->pid,0) != 0)   {
            if(prev==NULL) {
                prev = curr;
                root = curr;
            } else {
                prev->next = curr->next;
            }
        } else {
            if(!z) printf("%d is still running.\n", curr->pid);
        }
        prev = curr;
        curr = curr->next;
    }
}   


//code for child forking
void foo(char *cmd, char *argv[], int args) {
    int bgFlag;

    if(!strcmp(argv[args], "&")){
        argv[args] = '\0';
        bgFlag = 1;
    }

    int pid = fork();
    int status = 0;

    if(pid==0){
        if(bgFlag) {
            fclose(stdin); // close child's stdin
            fopen("/dev/null", "r"); // open a new stdin that is always empty
        }
        execvp(cmd, argv);
        // this should never be reached, unless there is an error
            fprintf (stderr, "unknown command: %s\n", cmd);
            exit(0);
    } else {
        if(!bgFlag) {
            waitpid(pid, &status, 0);
        } else {
            insertJob(pid);
        }
        if (status != 0) {
            fprintf  (stderr, "error: %s exited with status code %d\n", cmd,     status);
        } else {
            // cmd exec'd successfully
        }
    }

    // this is the parent still, since the child always terminates from exec or exit

    // continue being a shell...
}
4

1 に答える 1

1

子プロセスが終了したときにプログラムに通知するため、SIGCHLDのシグナルハンドラーをインストールする必要があります。SIGCHLDを受け取ったら、wait()(または、PID値が-1のwaitpid()を呼び出す必要があります。これは、どの子が終了したかわからないため子が終了したことだけです)。

ハンドラーを作成する最も安全な方法は次のとおりです。

volatile sig_atomic_t sigchld;
int handle_child(int sig)
{
  if (sig == SIGCHLD)
    sigchld = 1;
}

そして、メインループで、が1であるかどうかを確認しますsigchld。そうである場合、子プロセスが終了し、ループで呼び出すことができますwaidpid()(どの子が終了したかわからないため、-1のPIDを使用します)(を参照)。以下)複数の子が同時に終了する可能性があるため。また、システムコールがエラーを返し、errnoそれEINTRがシグナルによって中断された場合は、メインループの先頭に戻るか、それに応じてチェックして処理します(できるだけ早く0sigchldにリセットすることを忘れないでください) 。 sigchld)。

for(;;)
{
  int status;
  pid_t child;

  child = waitpid(-1,&status,WNOHANG);
  if (child == -1) 
  {
    if (errno == ECHILD) break; /* no more children */
    /* error, handle how you wish */
  }
  /* handle the return status of the child */
}
sigchld = 0;

シグナルハンドラー内から呼び出すことができますが(POSIXはそうしても安全だと言っています)、非常に微妙なバグにつながる可能性があるため、シグナルハンドラーで他​​に何もwaitpid()しないでください(たとえば、 -への呼び出し中にSIGCHLDが発生します) -を呼び出す結果となるシグナルハンドラーのコードは、非常に厄介な問題につながります。そのため、シグナルハンドラーにフラグを設定することをお勧めします---シグナルハンドラーで行う回数が少ないほど良いです)。malloc()malloc()

于 2013-02-26T05:27:51.153 に答える