特定条件になったらpid書いてるファイル読んでkillして再起動するやつがpid一周したのか全く別のプロセスkillしたっぽくてあれ。
— RyuichiWatanabe@gurasan (@ryuichi_1208) 2024年8月18日
今時はどうするんだろうと思ったりした。そもそもコンテナみたいにネームスペースが切られているみたいなケースだとpidが一周すること自体がそこまで起こらないとかだろうか?コンテナ内ではそうでもホストOSはむしろpidが一周しやすくなっているだろうしなんかしらあるだろうなと思って調べた。
pidfd_send_signal
ファイルディスクリプタで指定されたプロセスにシグナルを送る というシステムコールがLinux 5.1あたりで入った模様。pidfd_open(2)で開いておいてシグナルを送りたい時にpidfd_send_signal(2)を実行することで一周してPIDが同じ意図しないプロセスが存在する場合でもシグナルが送られずにエラーを返すようになる。以下はサンプルコード。(実際の意味合い的には/proc
のファイルをopenするのと同じものになるはず)
#define _GNU_SOURCE #include <unistd.h> #include <sys/syscall.h> #include <sys/types.h> #include <sys/wait.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> // pidfd_open システムコールをラップする関数 int pidfd_open(pid_t pid, unsigned int flags) { return syscall(SYS_pidfd_open, pid, flags); } // pidfd_send_signal システムコールをラップする関数 int pidfd_send_signal(int pidfd, int sig, siginfo_t *info, unsigned int flags) { return syscall(SYS_pidfd_send_signal, pidfd, sig, info, flags); } int main() { pid_t pid; int status; // 子プロセスを生成 pid = fork(); if (pid == -1) { perror("fork"); exit(EXIT_FAILURE); } if (pid == 0) { // 子プロセス: 無限ループで生き続ける printf("Child process PID: %d\n", getpid()); while (1) { sleep(1); } } else { // 親プロセス: 少し待ってから子プロセスを終了させる sleep(2); // pidfd_open を使って子プロセスのファイルディスクリプタを取得 int pidfd = pidfd_open(pid, 0); if (pidfd == -1) { perror("pidfd_open"); exit(EXIT_FAILURE); } // pidfd_send_signal を使って子プロセスに SIGTERM を送信 if (pidfd_send_signal(pidfd, SIGTERM, NULL, 0) == -1) { perror("pidfd_send_signal"); close(pidfd); exit(EXIT_FAILURE); } // 子プロセスの終了を待機 waitpid(pid, &status, 0); if (WIFEXITED(status)) { printf("Child process exited with status %d\n", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { printf("Child process killed by signal %d\n", WTERMSIG(status)); } close(pidfd); } return 0; }
実装
権限があるか、pidのfdかどうかや同一nsかどうかを見てシグナルを送っている。シンプル。スレッドやプロセスグループでのシグナル送信があったりとコンテナやスレッドベースのアプリケーションでのプロセス管理において役立つんだろうかという感想。