(´-`).。oO(killコマンドを提供するプロジェクト多いなって
killコマンドを提供するプロジェクト
とりあえず気付いた範囲で4つ。
上のリストはmanページへのリンクになっているのでオプションの違いとかはそちらを参照してください。busyboxのkillは最小構成って感じなのでオプションは1つしかないですね。-sオプションはbusybox以外は実装してますね。で、-lオプションは全てに存在。まあ、これはシグナル名を表示するので無いと困りますね。
ディストリビューションのkillコマンド
じゃあ、ディストリビューションはどのkillコマンドを採用してるのか?ということで手元にあるディストリビューションを確認したところこんなふうになってました。
ディストリビューション | killコマンドを提供するパッケージ |
---|---|
fedora 31 | util-linux |
centos 8 | util-linux |
arch linux(docker image) | util-linux |
ubuntu 18.04 | procps-ng |
alpine linux(docker image) | busybox |
killコマンドの実装
ソースを見てみます。調べるときに実際に動作させてなくてコードを読んだだけなのでもしかしたら間違ってる可能性もありますが。
util-linux
kill_verbose()がkill(2)でsignalをプロセスに投げているところのようです。
static int kill_verbose(const struct kill_control *ctl) { int rc = 0; if (ctl->verbose) printf(_("sending signal %d to pid %d\n"), ctl->numsig, ctl->pid); if (ctl->do_pid) { printf("%ld\n", (long) ctl->pid); return 0; } #ifdef HAVE_SIGQUEUE if (ctl->use_sigval) rc = sigqueue(ctl->pid, ctl->numsig, ctl->sigdata); else #endif rc = kill(ctl->pid, ctl->numsig); if (rc < 0) warn(_("sending signal to %s failed"), ctl->arg); return rc; }
if文でctl->do_pidをチェックしているところは-pオプションの処理です。util-linuxのkillはsigqueue(3)が利用できる場合はkill(2)ではなくてsigqueue(3)を使うんですね。sigqueue(3)の場合はデータを付加できるのでできるならこっちを使おうってことですね。これはmanにも書かれていますが-qオプションを使うとプロセスに送るデータを設定できます。
procps-ng
procps-ngの場合はkill_main()がkillを実現する関数っぽいです。 この関数の主要なところはここです。
for (i = 0; i < argc; i++) { pid = strtol_or_err(argv[i], _("failed to parse argument")); if (!kill((pid_t) pid, signo)) continue; error(0, errno, "(%ld)", pid); exitvalue = EXIT_FAILURE; continue; }
こちらはこれといって特殊なことはしてないですね。
coreutils
coreutisの場合はsend_signals()という関数がkill(2)を呼んでいます。
static int send_signals (int signum, char *const *argv) { int status = EXIT_SUCCESS; char const *arg = *argv; do { char *endp; intmax_t n = (errno = 0, strtoimax (arg, &endp, 10)); pid_t pid = n; if (errno == ERANGE || pid != n || arg == endp || *endp) { error (0, 0, _("%s: invalid process id"), quote (arg)); status = EXIT_FAILURE; } else if (kill (pid, signum) != 0) { error (0, errno, "%s", quote (arg)); status = EXIT_FAILURE; } } while ((arg = *++argv)); return status; }
これも短い関数ですね。こちらもprocps-ng同様にkill(2)を呼ぶだけです。
busybox
busyboxの場合はkill_main()という関数でkillの処理を行っています。余談ですがbusyboxのkill.cはprocps/にあるし、procps-ngと同じ感じにしてるんですかね?
#if ENABLE_KILL /* Looks like they want to do a kill. Do that */ while (arg) { # if SH_KILL /* * We need to support shell's "hack formats" of * " -PRGP_ID" (yes, with a leading space) * and " PID1 PID2 PID3" (with degenerate case "") */ while (*arg != '\0') { char *end; if (*arg == ' ') arg++; pid = bb_strtoi(arg, &end, 10); if (errno && (errno != EINVAL || *end != ' ')) { bb_error_msg("invalid number '%s'", arg); errors++; break; } if (kill(pid, signo) != 0) { bb_perror_msg("can't kill pid %d", (int)pid); errors++; } arg = end; /* can only point to ' ' or '\0' now */ } # else /* ENABLE_KILL but !SH_KILL */ pid = bb_strtoi(arg, NULL, 10); if (errno) { bb_error_msg("invalid number '%s'", arg); errors++; } else if (kill(pid, signo) != 0) { bb_perror_msg("can't kill pid %d", (int)pid); errors++; } # endif arg = *++argv; } return errors; #endif
コードにはSH_KILLは通常定義されてるのかどうかわかりませんが、定義の有無にかかわらずkill(2)を使うだけですね。
実装の差異
util-linuxはsigqueue(3)が利用可能ならそれを使い、利用できなければkill(2)を呼ぶという実装になっていますが、それ以外はkill(2)を呼ぶ形でした。
まとめ
Linuxでは基本的とも言えるkillコマンドですが、このコマンドを提供するパッケージは複数あり、ディストリビューションによってどのパッケージのkillコマンドを使うか選択していました。 また、実装でも微妙な違いがあるということがわかりました( ´ー`)フゥー...
Raspberry Piで学ぶコンピュータアーキテクチャ (Make:PROJECTS)
- 作者:Eben Upton,Jeff Duntemann,Ralph Roberts,Tim Mamtora,Ben Everard
- 出版社/メーカー: オライリージャパン
- 発売日: 2019/09/17
- メディア: 単行本(ソフトカバー)