25

C で簡単なシェル プログラムを作成しようとしています。必要なのは、他のローカル プログラムを実行できるプロンプトをユーザーに提供することです。親プロセスが子でwaits()し、子execvp()のプログラムであるfork()を使用して、その部分をうまく行うことができます。

ただし、「&」文字がユーザーのコマンドの末尾に追加されている場合、プログラムをバックグラウンドで実行する必要があります。つまり、親が子プロセスを待機せず、代わりにプロンプ​​トをすぐにユーザーに返す必要があります。バックグラウンド プロセスの実行を継続できますが、画面に何も表示することはできません。ps コマンドでまだ存在することを確認できるようにしたいだけです。

fork() を使用して子を作成し、子 fork() を再度使用してある種の孫を作成し、すぐに子プロセスを exit() するという考え方を理解しようとしました。つまり、孫を孤児にします。おそらくこれにより、親は引き続き子を待つことができますが、子は事実上ほぼすぐに終了するため、まったく待機しないように見えますか? ゾンビの狂気について何か?知らない。私が遭遇したいくつかのサイトは、プロセスをバックグラウンドで実行する方法としてこれを推奨しているようです。

ただし、これを実行しようとすると、プログラムの流れに異常が発生し、「バックグラウンド」プロセスが画面に入力を表示し続け、ここからどこへ行くべきか本当にわかりません。

これはコードの私の実装ですが、かなり間違っていると確信しています。この孫のこと全体が私がたどる必要のあるルートでさえあるのかどうか疑問に思っています。もしそうなら、私のコードの何が問題なのですか?

36 int main(int argc, char *argv[])
37 {
38     char buffer[512];
39     char *args[16];
40     int background;
41     int *status;
42     size_t arg_ct;
43     pid_t pid;
44 
45     while(1)
46     {
47         printf("> ");
48         fgets(buffer, 512, stdin);
49         parse_args(buffer, args, 16, &arg_ct);
50 
51         if (arg_ct == 0) continue;
52 
53         if (!strcmp(args[0], "exit"))
54         {
55             exit(0);
56         }
57 
58         pid = fork();  //here I fork and create a child process
61 
62         if (pid && !background)  //i.e. if it's the parent process and don't need to run in the background (this is the part that seems to work)
63         {
64             printf("Waiting on child (%d).\n", pid);
65             pid = wait(status);
66             printf("Child (%d) finished.\n", pid);
67         }
68         else
69         {
70             if (background && pid == 0) { //if it's a child process and i need to run the command in background
71                
72                 pid = fork(); //fork child and create a grandchild
73                 if (pid) exit(0); //exit child and orphan grandchild
74                 
75                 execvp(args[0], args); //orphan execs the command then exits
76                 exit(1);
77 
78             } else exit(0);
79         }
80     }
81     return 0;
82 }

PS明確にするために、印刷ステートメントなどの無限ループが発生した場合でも、バックグラウンドで実行しているプロセスが二度と音を出さないようにする必要があります。ps -a などを使用して、バックグラウンドでまだ実行されていることを確認したいだけです。

わかりにくい説明で申し訳ありませんが、これ以上説明する方法がわかりません。

前もって感謝します

PPS後続の各コマンドが「背景」のブール値を決定するように実装します。混乱して申し訳ありません

4

1 に答える 1

31

シェルでdouble- メソッドを使用する必要はありません。fork()これは、デーモンを実行したシェルによる監視から特に逃れたいデーモンを作成するためのものです。

&代わりに、既存のシェルでの動作を複製する方法は、 を呼び出してからfork()、子で呼び出しsetpgid(0, 0);て、子を新しいプロセス グループに入れることです。親はそのまま続行します (おそらくPID子の を出力した後) - は呼び出されませんwait()

各端末は、フォアグラウンド プロセス グループを 1 つだけ持つことができます。これは、現在端末に出力を送信できるプロセス グループです。これは、シェルがメンバーであるプロセス グループのままになるため、シェルはフォアグラウンドのままになります。

バックグラウンド プロセス グループがターミナルから読み取りを試みると、それを停止するシグナルが送信されます。端末への出力は引き続き許可されます - これは正常です (ls &通常のシェルで試してください)。コマンドをシェルに実装するには、フォアグラウンド プロセス グループを変更する関数fgが必要です。tcsetpgrp()

また、シェル プロンプトを表示する直前など、定期的waitpid()にオプションを指定して呼び出すこともできます。WNOHANGこれにより、バックグラウンド プロセスがいつ終了または停止したかを検出できます (または、SIGCHLDシグナルを処理できます)。

于 2011-11-30T02:05:28.970 に答える