セッションとプロセスグループ

プロセスグループ

http://linuxjm.sourceforge.jp/html/LDP_man-pages/man7/credentials.7.html より引用:

各プロセスはセッション ID とプロセスグループ ID を持つ。 これらの ID はどちらも pid_t 型で表現される。 プロセスは、それぞれ getsid(2), getpgrp(2) を使って自分のセッション ID、プロセスグループ ID を取得できる。

fork(2) で生成された子プロセスは親プロセスのセッション ID とプロセスグループ ID を継承する。プロセスのセッション ID とプロセスグループ ID は execve(2) の前後で不変である。

セッションとプロセスグループの概念は、シェルのジョブ制御を行うために 考案されたものである。 プロセスグループ (時には「ジョブ」と呼ばれることもある) は、 同じプロセスグループ ID を共有するプロセスの集まりである。 シェルは、一つのコマンドもしくはパイプラインの実行に使われるプロセス群に 対して一つのプロセスグループを生成する (例えば、コマンド "ls | wc" を実行するために生成される二つのプロセスは 同じプロセスグループに置かれる)。 所属するプロセスグループは setpgid(2) を使って設定できる。 自身のプロセス ID がプロセスグループ ID と同じプロセスは、 そのグループの「プロセスグループ・リーダー」である。

プロセスグループの例)
適当なコマンドを実行。パイプも使ってみる。


[user1@vm03 shtest]$ ./hoge.sh | less
別端末からプロセスの状況を確認。(ps の j オプションは BSD job control format.)

[user1@vm03 ~]$ ps j
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
4410 4411 4411 1966 pts/0 4978 S 501 0:00 -bash
4437 4438 4438 2029 pts/1 4982 S 501 0:00 -bash
4411 4978 4978 1966 pts/0 4978 S+ 501 0:00 /bin/sh ./hoge.sh
4411 4979 4978 1966 pts/0 4978 S+ 501 0:00 less
4978 4980 4978 1966 pts/0 4978 S+ 501 0:00 sleep 180
4438 4982 4982 2029 pts/1 4982 R+ 501 0:00 ps j
hoge.sh, less, sleep 180 はいずれもPGID=4978 で同一プロセスグループ。プロセスグループ・リーダーはPIDがPGIDと等しいhoge.sh。

セッション

http://linuxjm.sourceforge.jp/html/LDP_man-pages/man7/credentials.7.html より引用:

セッションは、同じセッション ID を共有するプロセスの集まりである。 ある一つのプロセスグループの全メンバーは同じセッション ID を持つ (つまり、一つのプロセスグループのメンバーは全て同じセッションに所属し、 これにより、セッションとプロセスグループで二階層のプロセス階層が形成できる)。 新たなセッションの生成はプロセスが setsid(2) を呼び出すことで行う。 setsid(2) は、 setsid(2) を呼び出したプロセスの PID と同じ値のセッション ID を持つ 新たなセッションを生成する。 セッションの生成者は「セッション・リーダー」と呼ばれる。

あるセッションの全プロセスは一つの 制御端末 を共有する。 セッションリーダーが最初に端末をオープンした際に制御端末は設定される (open(2) の呼び出しで O_NOCTTY フラグが指定された場合を除く)。 一つの端末は、最大でも一つのセッションの制御端末にしかなれない。

一つのセッションのジョブの中で、フォアグラウンドジョブになれるのは最大でも一つで、そのセッションの他のジョブはバックグラウンドジョブである。 フォアグラウンドジョブだけが端末からの読み込みを行える。 バックグラウンドのプロセスが端末から読み込みを行おうとした場合、 フォアグラウンドジョブを停止させるシグナルである SIGTTIN が所属するプロセスグループに対して送信される。 端末に TOSTOP フラグがセットされていた場合 (termios(3) 参照)、 フォアグラウンドジョブだけが端末への書き込みを行える。 バックグラウンドのプロセスが端末への書き込みを行おうとした場合、 フォアグラウンドジョブを停止させるシグナルである SIGTTOU が生成される。 シグナルを生成する端末キー (例えば 中断キー、通常は control-C) が押された場合、 そのシグナルはフォアグラウンドジョブのプロセスに送信される。

セッションの例)
適当なコマンドを3回実行。3回目は&をつけず、フォアグラウンドで実行。


[user1@vm03 shtest]$ ./hoge.sh &
[1] 5012
[user1@vm03 shtest]$ ./hoge.sh &
[2] 5014
[user1@vm03 shtest]$ ./hoge.sh
別端末からプロセスの状況を確認

[user1@vm03 ~]$ ps ajx
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
:
1964 1966 1966 1966 pts/0 5019 Ss 0 0:00 -bash
:
1966 4410 4410 1966 pts/0 5019 S 0 0:00 su - user1
4410 4411 4411 1966 pts/0 5019 S 501 0:00 -bash
:
4411 5012 5012 1966 pts/0 5019 S 501 0:00 /bin/sh ./hoge.sh
5012 5013 5012 1966 pts/0 5019 S 501 0:00 sleep 180
4411 5014 5014 1966 pts/0 5019 S 501 0:00 /bin/sh ./hoge.sh
5014 5015 5014 1966 pts/0 5019 S 501 0:00 sleep 180
4411 5019 5019 1966 pts/0 5019 S+ 501 0:00 /bin/sh ./hoge.sh
5019 5020 5019 1966 pts/0 5019 S+ 501 0:00 sleep 180
:

  • SID=1966となっている各プロセスは、同一セッション。
  • SID=1966のセッションのセッション・リーダーはbash(PID:1966)。なお、上記psコマンドのSTATの"Ss"の小文字のsはセッション・リーダーであることを示す。
  • SID=1966のセッションのフォアグラウンドジョブはPGID=5019のジョブ。なお、上記psコマンドのSTATの"S+"の"+"はフォアグラウンドジョブであることを示す。
  • SID=1966のセッションの端末はpts/0である。pts/0は他のどのセッションの端末でもない。

端末 (terminal)

端末とはLinuxに対する標準入出力をLinuxと仲介するもの。
例えば、キーボードで入力した文字列を標準入力としてLinux/Unixに渡したり、標準出力をコンソール上に表示する。
psコマンドなどで表示されるttyやptsも端末のことを指している。
ptsは擬似端末と呼ばれ、telnetsshなどでログインするとこの端末に接続される。
ttyは仮想コンソールと呼ばれ、Linuxに直接接続している場合に使われる端末である。通常、Linuxの場合、最初の6つの仮想コンソール (/dev/tty1〜/dev/tty6) があり、Alt + ファンクションキー(F1など)で切り替えることができる。

ttyコマンド

以下のように、ttyコマンドで、現在利用している端末デバイスを表示することができる。


[user1@vm03 shtest]$ tty
/dev/pts/0

[参考]
http://linuxjm.sourceforge.jp/html/LDP_man-pages/man7/credentials.7.html
http://equj65.net/tech/linuxprocessgroup/
http://ja.wikipedia.org/wiki/Tty

Ctrl+C と killコマンド の違い

kill : 指定されたプロセスにのみシグナルを送信(プロセスの親子は関係ない)
Ctrl+C : フォアグラウンドのジョブの全プロセスに対してSIGINTを送信

[実験]

以下のようなスクリプトを実行させ、killした場合とCtrl+Cした場合とで挙動の違いを見る。

[user1@vm03 shtest]$ cat hoge.sh
#!/bin/sh

sleep 180
killコマンドの場合

hoge.sh を実行


[user1@vm03 shtest]$ ./hoge.sh

別端末からプロセスを確認


[user1@vm03 ~]$ ps jx
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
4410 4411 4411 1966 pts/0 4583 S 501 0:00 -bash
4437 4438 4438 2029 pts/1 4585 S 501 0:00 -bash
4411 4583 4583 1966 pts/0 4583 S+ 501 0:00 /bin/sh ./hoge.sh
4583 4584 4583 1966 pts/0 4583 S+ 501 0:00 sleep 180
4438 4585 4585 2029 pts/1 4585 R+ 501 0:00 ps jx
hoge.sh のプロセスとsleep のプロセスが両方ある。PGIDは同じ。

hoge.sh のプロセスをkillで殺す。


[user1@vm03 ~]$ kill 4583

もう一度プロセスを確認


[user1@vm03 ~]$ ps jx
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
4410 4411 4411 1966 pts/0 4411 S+ 501 0:00 -bash
4437 4438 4438 2029 pts/1 4587 S 501 0:00 -bash
1 4584 4583 1966 pts/0 4411 S 501 0:00 sleep 180
4438 4587 4587 2029 pts/1 4587 R+ 501 0:00 ps jx
→ sleep プロセスが残った。sleep プロセスの親プロセスのPID は1。つまり init プロセス。

なお、killコマンドはシグナル番号が指定されなかった場合、デフォルトでSIGTERM (シグナル番号:15)を送る。

Ctrl+C の場合

hoge.sh を実行


[user1@vm03 shtest]$ ./hoge.sh

別端末からプロセスを確認


[user1@vm03 ~]$ ps jx
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
4410 4411 4411 1966 pts/0 4591 S 501 0:00 -bash
4437 4438 4438 2029 pts/1 4593 S 501 0:00 -bash
4411 4591 4591 1966 pts/0 4591 S+ 501 0:00 /bin/sh ./hoge.sh
4591 4592 4591 1966 pts/0 4591 S+ 501 0:00 sleep 180
4438 4593 4593 2029 pts/1 4593 R+ 501 0:00 ps jx
hoge.sh のプロセスとsleep のプロセスが両方ある。PGIDは同じ。

元の端末でCtrl+C でhoge.sh を中断する。


[user1@vm03 shtest]$ ./hoge.sh
^C

もう一度hoge.sh のプロセスを確認


[user1@vm03 ~]$ ps jx
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
4410 4411 4411 1966 pts/0 4411 S+ 501 0:00 -bash
4437 4438 4438 2029 pts/1 4595 S 501 0:00 -bash
4438 4595 4595 2029 pts/1 4595 R+ 501 0:00 ps jx
hoge.sh も sleep プロセスもなくなっている。hoge.sh と同じPGID (PGID:4591) のプロセスに対してすべてSIGINT (シグナル番号:2)が送られたため。

PGIDが同じプロセス群は「プロセスグループ」という。これは時には「ジョブ」とも呼ばれる。

どうして送られたシグナルがSIGINTであるとわかるか


[user1@vm03 shtest]$ stty -a
speed 38400 baud; rows 35; columns 90; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = ; eol2 = ;
:
→ intr = ^C; が、Ctrl+C でSIGINTを送信するということを意味する。

[まとめ]

kill : 指定されたプロセスにのみシグナルを送信(プロセスの親子は関係ない)
Ctrl+C : フォアグラウンドのジョブの全プロセスに対してSIGINTを送信

参考:http://equj65.net/tech/linuxprocessgroup/

OS自作入門 29日目分、30日目分 (読了)

29日目分 圧縮と簡単なアプリケーション

OSのサイズを小さくするため、圧縮ファイルを解凍する機能をOSにつける話など。
他、インベーダーゲームのアプリなど。

30日目分 高度なアプリケーション

この章はOS本体とは直接関係ないので流し読み。
IPLのところだけ詳しく読んだ。BIOSでディスクを読み込む処理で、複数セクタ同時に読み込む機能を使ってディスク読み込みの高速化をしようという話。

全体の振り返り

途中間が空いたが、12/26〜1/12 の18日間で読了となった。30日に収まった。
結局私は本文に沿ってサンプルプログラムを動かしながらOS作りの過程を追っただけで、OSを本当に自作したことにはならなかった。
それでもOS作りの過程を追いながら、これまでOSでブラックボックスだった部分の中身が、少しだけ分かったような気になった。
とくに勉強になったのは、割り込み、タイマー、マルチタスクAPIのところか。
最初はOS作りというといかにも難しそうであったが、思ったよりは難しくなかった。
今後Linuxなど他のOSに触れる際も、「はりぼてOS」との違いを意識しながら見ることで、より深い理解が得られるようになるのではないかと思う。

OS自作入門 27日目分、28日目分

27日目分 LDTとライブラリ

キーワード:LDT(local descriptor table)、ライブラリ

ポイント:

  • GDTが全部のタスクから共通に使えるセグメント設定であるのに対し、LDTはあるタスクでしか有効でないセグメント設定
  • アプリ用のセグメントをLDTの中で設定すれば、他のタスクからセグメントを破壊されることはない
  • ライブラリとは、将来他のプログラムにも使えそうな部品を集めてまとめたもの

28日目分 ファイルと日本語表示

キーワード:__alloca, ファイルAPI, 日本語表示

ポイント:

  • Cコンパイラではスタックに4KB以上の変数を確保しようとすると、__allocaという関数を呼び出すようになる。この関数はOSの仕様にあわせてスタック上に領域を確保する。
  • 全角文字の文字コードは「点」「区」「面」という単位で定義される。

感想:

  • ファイルAPI のところ、これまでブラックボックスだったLinux などの open(2) や read(2) も中身はだいたいこんな感じのことをやっているのかなというイメージが沸いた

OS自作入門 25日目分、26日目分

OS自作入門、平日は結局できず、前回から4日、間が空いてしまった。そのせいもあり、モチベーションが若干下がり気味。しかし何とか最後まで完走したい。

25日目、26日目分、これまでの知識を応用してプログラムをOSらしく改良しているだけで、新出事項は特になし。

25日目分 コンソールを増やそう

BEEPサウンド、色を増やす、コンソールを増やす
→256色でもきれいに見せるテクニック のところで深みにハマってしまった が面白い

26日目分 ウィンドウ移動の高速化

ウィンドウ移動の高速化、コンソールを閉じる、startコマンド、ncstコマンド など

OS自作入門 24日目分 ウィンドウ操作

掲題のとおり、ウィンドウ操作。切り替え、移動、マウスで閉じる、アプリケーションウィンドウも入力切り替え、入力ウィンドウをマウスで切り替える、タイマAPI、タイマAPIのキャンセル。
プログラムの論理を追うのはそれなりに大変だが、これまでの知識を応用しただけで、新出事項は特になし。

OS自作入門 23日目分 グラフィックいろいろ

mallocAPI、ウィンドウに点や線を書くAPI など。

mallocでは、アプリからはOS用のセグメントは利用できないので、OSのメモリ割り当て関数は利用できない。よって、はじめにOSがアプリ用に確保するデータセグメントを、指定されたサイズ分だけ多めにとっておき、これをmallocされたときに割り当てることにする。という点が目新しかった。

ほかはこれといって目新しい点なし。

ただ、線を引くAPIのプログラムで、プログラムの意味が直感的に理解できず、ハマってしまった。