kill_fasyncの引数のまぎらわしさ
tl;dr
- kill_fasyncの第2引数は飛ばしたいシグナル番号を指定するわけではない
- つまみ食いで勉強すると落とし穴にはまる危険があるので気をつける
詳細
kill_fasyncという関数をご存知だろうか。kill_fasyncとは、Linuxカーネルが提供する関数の1つで、 カーネルモジュールからユーザプロセスへシグナルを飛ばすものだ。
Kernel 4.8での実装はこんな感じ。至ってシンプルで、 正直これで何がわかるか、というところであるが、少なくとも何をいくつ引数に取るかわかる。
Linux/fs/fcntl.c - Linux Cross Reference - Free Electrons
724 void kill_fasync(struct fasync_struct **fp, int sig, int band) 725 { 726 /* First a quick test without locking: usually 727 * the list is empty. 728 */ 729 if (*fp) { 730 rcu_read_lock(); 731 kill_fasync_rcu(rcu_dereference(*fp), sig, band); 732 rcu_read_unlock(); 733 } 734 } 735 EXPORT_SYMBOL(kill_fasync);
さて、これを見て何を思うか。素直な心と有り余る読解力で読むと、第2引数のsigは飛ばすシグナルの番号だと思うのではないだろうか。 ところがこれは飛ばすシグナルの番号とは全く関係がない。
さて、kill_fasync_rcuを見てみよう。
699 static void kill_fasync_rcu(struct fasync_struct *fa, int sig, int band) 700 { 701 while (fa) { 702 struct fown_struct *fown; 703 unsigned long flags; 704 705 if (fa->magic != FASYNC_MAGIC) { 706 printk(KERN_ERR "kill_fasync: bad magic number in " 707 "fasync_struct!\n"); 708 return; 709 } 710 spin_lock_irqsave(&fa->fa_lock, flags); 711 if (fa->fa_file) { 712 fown = &fa->fa_file->f_owner; 713 /* Don't send SIGURG to processes which have not set a 714 queued signum: SIGURG has its own default signalling 715 mechanism. */ 716 if (!(sig == SIGURG && fown->signum == 0)) 717 send_sigio(fown, fa->fa_fd, band); 718 } 719 spin_unlock_irqrestore(&fa->fa_lock, flags); 720 fa = rcu_dereference(fa->fa_next); 721 } 722 }
意外にもsigはSIGURGじゃなければOKで、実際にシグナルを飛ばしていると思しき send_sigioにはsigが全く入っていない。じゃあ一体sigって何なのか。 それに飛ばしたいシグナル番号ってどこに入ってるんだ。
順番が前後するが、飛ばしたいシグナル番号はsend_sigio関数を見ればわかるよね。 send_sigioはさらにsend_sigio_to_taskという関数を呼んでる。
491 void send_sigio(struct fown_struct *fown, int fd, int band) 492 { 493 struct task_struct *p; 494 enum pid_type type; 495 struct pid *pid; 496 int group = 1; 497 498 read_lock(&fown->lock); 499 500 type = fown->pid_type; 501 if (type == PIDTYPE_MAX) { 502 group = 0; 503 type = PIDTYPE_PID; 504 } 505 506 pid = fown->pid; 507 if (!pid) 508 goto out_unlock_fown; 509 510 read_lock(&tasklist_lock); 511 do_each_pid_task(pid, type, p) { 512 send_sigio_to_task(p, fown, fd, band, group); 513 } while_each_pid_task(pid, type, p); 514 read_unlock(&tasklist_lock); 515 out_unlock_fown: 516 read_unlock(&fown->lock); 517 }
あった。シグナル番号signumはfownに入ってる。
449 static void send_sigio_to_task(struct task_struct *p, 450 struct fown_struct *fown, 451 int fd, int reason, int group) 452 { 453 /* 454 * F_SETSIG can change ->signum lockless in parallel, make 455 * sure we read it once and use the same value throughout. 456 */ 457 int signum = ACCESS_ONCE(fown->signum); 458 459 if (!sigio_perm(p, fown, signum)) 460 return; 461 462 switch (signum) { 463 siginfo_t si; 464 default: 465 /* Queue a rt signal with the appropriate fd as its 466 value. We use SI_SIGIO as the source, not 467 SI_KERNEL, since kernel signals always get 468 delivered even if we can't queue. Failure to 469 queue in this case _should_ be reported; we fall 470 back to SIGIO in that case. --sct */ 471 si.si_signo = signum; 472 si.si_errno = 0; 473 si.si_code = reason; 474 /* Make sure we are called with one of the POLL_* 475 reasons, otherwise we could leak kernel stack into 476 userspace. */ 477 BUG_ON((reason & __SI_MASK) != __SI_POLL); 478 if (reason - POLL_IN >= NSIGPOLL) 479 si.si_band = ~0L; 480 else 481 si.si_band = band_table[reason - POLL_IN]; 482 si.si_fd = fd; 483 if (!do_send_sig_info(signum, &si, p, group)) 484 break; 485 /* fall-through: fall back on the old plain SIGIO signal */ 486 case 0: 487 do_send_sig_info(SIGIO, SEND_SIG_PRIV, p, group); 488 } 489 }
でもfown_structなんてどこで設定するんだ?と思ったあなたは鋭い。 実はkill_fasyncを使うには(当たり前だが)準備がいる。 具体的にはfcntlを使えばよい。以下のスライドを見てくれれば何となくわかると思う。 F_SETSIGとシグナル番号を与えてfcntlを撃ってやればOKだ。先に見るべきだった。
http://www.cs.usfca.edu/~cruse/cs635s05/lesson22.ppt
では、SIGIOは一体何なのか。 このへんを読むと、非同期のI/Oイベントを伝えるときにはSIGIOを使うらしいというのがわかる。 要はある種のお約束的なものですね。 まあ確かにカーネルモジュールというかドライバだから、ユーザプロセスに何か通知するのって、 外部デバイスの入出力イベント以外にないよなあ。
- 6.4. Asynchronous Notification
- Asynchronous I/O on linux
- The GNU C Library: Asynchronous I/O Signals
- Man page of SIGNAL
ところでSIGURGって何で特別扱いされてるの?と気になるけど、こちらもある種のお約束的なものらしい。 SIGIOよりも用途が限定されてるようなので特別扱いなのかも。
SIGURG は、OOB データをサポートしているソケットでアウト・オブ・バンド (OOB) データを受信するときに送信される信号です。たとえば、AF_INET アドレス・ファミリーで SOCK_STREAM タイプのソケットは、 SIGURG 信号を送信するように条件付けることができます。
SIGIO は、あらゆるタイプのソケットで通常のデータ、OOB データ、エラー条件、その他が発生したときに送信される信号です。
まとめ
kill_fasyncの第2引数はある種のおまじない的にSIGIOを指定すればよいっぽい。 使い方をちゃんと勉強しないと余計なことを調べなきゃいけなくなるので、 ショートカットせずに地道に調べた方がよい。