プロセスグループ
パイプで複数のプロセスを連結して動作させると、プロセスグループというものが作られます。ジョブと言いますが、カーネルではどのように管理されているのでしょうか?と以前スレッドグループをプロセスグループと勘違いしていました。そこで改めて調べてみました。
しかし、tasku_structにプロセスグループにかかるメンバーがないのです。bashそのものを調べた分けでないのですが、以下の理由で、たぶんプロセスグループはbashでの処理じゃないかと思うに至りました。
理由1:tasku_structにプロセスグループにかかるメンバーがない。
理由2:jobsコマンドはbash内部コマンド。
理由3:あるシェルでジョブとして起動したものは、他のシェルでjobsとしても表示しない。
bashではプロセス起動毎にそのプロセスIDとともにbash内の認識番号(ジョブID)を割り当てているようで、これはパイプ等で連結しない1プロセス起動の通常の立ち上げでも同じようです。そうすることで、ジョブ単位で処理したほうがいいもの(セッションの割り当て。画面、キー入力をどのプロセスに送ればいいか。)なんかは、シェルがそのジョブIDのプロセス群にかかる処理を、逐一しているんじゃないかと思われます。従ってパイプで連結されたプロセス群の入力は最初のプロセスで、出力は最後のプロセスとすればいいわけで、セッションの割付はジョブ単位としたほうが言い訳で、シェルとしてプロセスグループというものを実装していると思います。
・以下スレッドグループについて
httpdはpreforkで前もって複数のプロセス(スレッド)が立ち上がります。親のhttpdを削除すると、すべてのhttpdが削除されます。これは、これらのhttpdがスレッドグループを形成しているからです。
tasku_structとしてのプロセス識別子としては、pidとtgidだけのようです。pidはプロセス/スレッドに割り当てられたユニークな識別子です。カーネル内のスケジューリングではプロセスであろうとスレッドであろうと関係ありません。同じものとして処理されます。ただし終了処理においてシグナルをそのスレッドグループの全プロセスに送る場合があります。そのような場合はカーネルの処理というわけです。
プロセス/スレッドが作成されると、p->pid = pid_nr(pid)でユニークなプロセスIDが割り当てら、p->tgid = current->tgidでスレッドグループIDにはそのプロセスIDが割り当てられます。もしCLONE_THREAD(スレッドとして作成)なら、p->tgid = current->tgidでスレッドを作成したプロセスのIDが割り当てられています。
そうすることで、シグナル発生時、send_signal経由してよばれるcomplete_signal関数内で、スレッドグループに発生した致命的は、シグナルを、struct list_head thread_groupをたどることにより、全プロセス(スレッド)に配送することが可能となっているようです。
しかし、tasku_structにプロセスグループにかかるメンバーがないのです。bashそのものを調べた分けでないのですが、以下の理由で、たぶんプロセスグループはbashでの処理じゃないかと思うに至りました。
理由1:tasku_structにプロセスグループにかかるメンバーがない。
理由2:jobsコマンドはbash内部コマンド。
理由3:あるシェルでジョブとして起動したものは、他のシェルでjobsとしても表示しない。
bashではプロセス起動毎にそのプロセスIDとともにbash内の認識番号(ジョブID)を割り当てているようで、これはパイプ等で連結しない1プロセス起動の通常の立ち上げでも同じようです。そうすることで、ジョブ単位で処理したほうがいいもの(セッションの割り当て。画面、キー入力をどのプロセスに送ればいいか。)なんかは、シェルがそのジョブIDのプロセス群にかかる処理を、逐一しているんじゃないかと思われます。従ってパイプで連結されたプロセス群の入力は最初のプロセスで、出力は最後のプロセスとすればいいわけで、セッションの割付はジョブ単位としたほうが言い訳で、シェルとしてプロセスグループというものを実装していると思います。
・以下スレッドグループについて
httpdはpreforkで前もって複数のプロセス(スレッド)が立ち上がります。親のhttpdを削除すると、すべてのhttpdが削除されます。これは、これらのhttpdがスレッドグループを形成しているからです。
[root@localhost ~]# ps -fje | more UID PID PPID PGID SID C STIME TTY TIME CMD root 1 0 1 1 0 19:29 ? 00:00:04 init [3] root 72 7 1 1 0 19:29 ? 00:00:00 [kseriod] root 129 7 1 1 0 19:29 ? 00:00:00 [pdflush] root 130 7 1 1 0 19:29 ? 00:00:01 [pdflush] root 131 7 1 1 0 19:29 ? 00:00:00 [kswapd0] root 132 7 1 1 0 19:29 ? 00:00:00 [aio/0] root 290 7 1 1 0 19:29 ? 00:00:00 [kpsmoused] root 1511 1 1511 1511 0 19:31 ? 00:00:00 /usr/sbin/sshd root 1520 1 1520 1520 0 19:31 ? 00:00:00 /usr/sbin/httpd apache 1557 1520 1520 1520 0 19:31 ? 00:00:00 /usr/sbin/httpd apache 1558 1520 1520 1520 0 19:31 ? 00:00:00 /usr/sbin/httpd apache 1559 1520 1520 1520 0 19:31 ? 00:00:00 /usr/sbin/httpd apache 1560 1520 1520 1520 0 19:31 ? 00:00:00 /usr/sbin/httpdPPIDは親のプロセスIDをPGIDはスレッドグループ番号です。通常の場合スレッドグループ番号はプロセスIDと同じです。下から5つ目のhttpdのPID=1520,PGID=1520で、これがプロセスリーダだと分かります。下の4つのPGIDは1520,また親プロセスも1520ですから、プロセスグループを形成していて、しかもスレッドとして起動されたな。というのが分かります。
tasku_structとしてのプロセス識別子としては、pidとtgidだけのようです。pidはプロセス/スレッドに割り当てられたユニークな識別子です。カーネル内のスケジューリングではプロセスであろうとスレッドであろうと関係ありません。同じものとして処理されます。ただし終了処理においてシグナルをそのスレッドグループの全プロセスに送る場合があります。そのような場合はカーネルの処理というわけです。
プロセス/スレッドが作成されると、p->pid = pid_nr(pid)でユニークなプロセスIDが割り当てら、p->tgid = current->tgidでスレッドグループIDにはそのプロセスIDが割り当てられます。もしCLONE_THREAD(スレッドとして作成)なら、p->tgid = current->tgidでスレッドを作成したプロセスのIDが割り当てられています。
static struct task_struct *copy_process(unsigned long clone_flags, ・・・・・ p->pid = pid_nr(pid); p->tgid = p->pid; if (clone_flags & CLONE_THREAD) p->tgid = current->tgid; ・・・・・ if (clone_flags & CLONE_THREAD) { atomic_inc(¤t->signal->count); atomic_inc(¤t->signal->live); p->group_leader = current->group_leader; list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group); ・・・・・p->group_leader = current->group_leaderスレッドグループリーダをスレッドのtasku_structの>group_leaderにセットし、list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group)で、スレッドグループのtasku_struct→thread_groupをヘッドとするスレッドリストしていまう。
そうすることで、シグナル発生時、send_signal経由してよばれるcomplete_signal関数内で、スレッドグループに発生した致命的は、シグナルを、struct list_head thread_groupをたどることにより、全プロセス(スレッド)に配送することが可能となっているようです。
static void complete_signal(int sig, struct task_struct *p, int group) { ・・・・・ if (!sig_kernel_coredump(sig)) { signal->flags = SIGNAL_GROUP_EXIT; signal->group_exit_code = sig; signal->group_stop_count = 0; t = p; do { sigaddset(&t->pending.signal, SIGKILL); signal_wake_up(t, 1); } while_each_thread(p, t); return; ・・・・・ }