web-dev-qa-db-ja.com

私のダミーシェルでfork()と子プロセスを使用するバックグラウンドプロセスについて

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

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

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

しかし、これを行おうとすると、プログラムのフローで野生の出来事が発生し、「バックグラウンド」プロセスが画面に入力を表示し続け、ここからどこに行くべきか本当にわかりません。

これは私のコードの実装ですが、これは間違いです。この孫のすべてが私が取る必要のあるルートであるかどうか疑問に思っています。そうであれば、私のコードの何が問題になっていますか?

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 }

P.S。明確にするために、バックグラウンドで実行しているプロセスで、printステートメントまたは何かの無限ループがあったとしても、二度と音が出ないようにする必要があります。私はそれがps -aまたは何かを介してバックグラウンドでまだ実行されていることを確認したいだけです。

紛らわしい説明でごめんなさい私はそれをもっとよく説明する方法がわかりません。

前もって感謝します

P.P.S後続の各コマンドが 'background'のブール値を決定するように実装しますが、混乱を招きます

20
tlw11591

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

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

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

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

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

22
caf