シグナルの設定
前回の復習
環境変数の受け渡し
バックグラウンド実行機能の追加
本日の内容
シグナルについて
特定のシグナルを受け取った時の挙動の変更
シグナルについて
シグナルはプロセス間の非同期通信の際に利用される文字通り「信号」です.
あるプロセスが他のプロセスにシグナルを送信するとOSは宛先のプロセスに割り込んで特定の動作を実行します.
シグナルにはいくつか種類がありますがそれぞれにデフォルトで異なる動作が登録されています.
例えば,SIGINT(^Cのキーボード入力で送信されるシグナル)はプロセスの終了という動作が登録されているため,プログラムの実行中に^Cを入力すると強制終了することができます.
特定のシグナルを受け取った時の挙動の変更
SIGCHLDの挙動の変更
前回バックグラウンド実行機能を実装しましたが,一切wait処理を行わない場合子プロセスは終了後にリソースが解放されずゾンビになってしまいます.そのため親プロセス側で終了した子プロセスのリソース解放処理を行う必要がありますが,子プロセスは終了時にSIGCHLDというシグナルを親プロセスに送るため,これを受け取った際にwait処理を行う関数を実行することで対応します.
シグナルを受け取った時の挙動を設定するにはsignal関数を用います.
したがって,新たにsigchld_wait関数を書き,以下のようなコードをmain関数の最初に足せば良いでしょう.
signal
第1引数にシグナル名,第2引数に受け取り時に実行する関数名を指定する.
第2引数の関数の型はvoid(*)(int)である必要がある(?)
調べてはないので誰かお願いします
実は移植性の低いsignalよりsigaction関数の利用が推奨されている
wait_pid
PIDを第1引数に取れ,第3引数に細かいオプションを取れる.
pidで-1を指定すると任意のPIDを受け取れる.(つまりwaitと同じ)
WNOHANGは呼び出し元を中断しないことを示す.
code:rcsh.c
void sigchld_wait(int sig) {
int status;
waitpid(-1, &status, WNOHANG);
printf("process exited with status %d\n", WEXITSTATUS(status));
}
int main(void) {
if (signal(SIGCHLD, sigchld_wait) == SIG_ERR) {
fprintf(stderr, "rcsh: signal(SIGCHLD) failed\n");
exit(1);
}
// 省略
SIGINTの挙動の変更
C-cで終了しないように変更しましょう.
signal関数でSIGINTの挙動を何もしないように変更するだけですね.
シグナルを無視する関数SIG_IGNは標準で用意されています.
やることとしては,最初にSIGINTを無視するように設定しておいて,子プロセス側で分岐した際にその設定を削除するというだけです.
code:rcsh.c
// ^Cを無視
if (signal(SIGINT, SIG_IGN) == SIG_ERR) {
fprintf(stderr, "rcsh: signal(SIGINT) failed\n");
exit(1);
}
while(1) {
//省略
if(pid == 0) {
signal(SIGINT, SIG_DFL);
// 省略