まずはじめに割り込みはハードウェアで提供する機能でシグナルはソフトウェアで提供する機能だということを理解してください。
ソフトウェア割り込みはソフトウェアで実装した割り込み相当の機能※という意味ではなくてCPU例外の一種です。
CPUを特権モードに移行するためにソフトウェア割り込みを発生させる必要があります。要はユーザー空間からカーネル空間に処理を移す際、すなわちシステムコールの実装でソフトウェア割り込みを使うということです。
(※ややこしいことに文脈によってはそう使うこともあります)
■シグナルと割り込みの区別
区別をしているわけではなくて、シグナルの機能実装に部分的に割り込みが使われているというだけです。
非同期な処理の通知を実現したいので割り込みを使うというのは正解の一つではありますが大変危険です。
OSはハードウェアの機能だとかリソースを隠蔽して(抽象化して)ユーザーに提供するべきです。
他のユーザーが割り込み内で無限ループさせたら他のユーザーが全く使えないということになってしまうのでOSが管理、監視をした上で
ハードウェアに依存することなく通知を実現する方法をユーザーに提供してるわけです。
■signalの動作
試しに以下のプログラムをstraceしながらSIGHUPを送ってやります。
C
1 # include <signal.h>
2 # include <stdio.h>
3
4 void sigcatch ( int ) ;
5
6 int main ( ) {
7 if ( SIG_ERR == signal ( SIGHUP , sigcatch ) ) {
8 printf ( "failed to set signal handler.n" ) ;
9 exit ( 1 ) ;
10 }
11
12 while ( 1 ) {
13 sleep ( 1 ) ;
14 }
15
16 return 0 ;
17 }
18
19 void sigcatch ( int sig ) {
20 printf ( "catch signal %dn" , sig ) ;
21 }
22
kill -s 1 プロセスID
実行結果
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({1, 0}, 0x7fffcedc5ae0) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({1, 0}, 0x7fffcedc5ae0) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({1, 0}, {0, 581633616}) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=31701, si_uid=1000} ---
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 5), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc64cf53000
rt_sigreturn() = -1 EINTR (Interrupted system call)
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({1, 0}, 0x7fffcedc5ae0) = ?
+++ exited with 1 +++
nanosleep(sleep)で戻り値がERESTART_RESTARTBLOCKとなり、sigcatchが呼ばれています。
注目すべきことはシステムコールから復帰した後にシグナルハンドラが呼ばれている点です。
■実装
調べながら書いてますが、古い情報が混ざったり私の解釈が怪しい部分が多いので注意してください。
linux/kernel/signal.cにシグナルの実装が書かれています。
ソース内で頻繁にtask_structという構造体が出てきますが、プロセス、スレッドを記述したもので、この中にpending(個別保留中シグナルのキュー)、shared_pending(共有保留中シグナルのキュー)、sighand(シグナルハンドラ)、state(プロセスの状態)があります。
__send_signalでsignalをキューに入れた後、complete_signalを呼び出して、そこから最終的にwake_up_stateという関数呼び出してTASK_INTERRUPTIBLE(割り込み可能)のプロセスを起床させようとしています。このあとコンテキストが一旦切れますが、プロセスが起床してシステムコールからユーザーモードに戻る際に、do_notify_resumeでdo_signalが呼び出されキューに入っているシグナルを確認しています。シグナルがあった場合にはhandle_signalでシステムコールの戻り先をシグナルハンドラに書き換えて、システムコールから戻った際にシグナルハンドラが呼ばれます。その後再びシステムコールへ戻り通常の流れに戻るという仕組みのようです。
参考:
http://elixir.free-electrons.com/linux/latest/source/kernel/signal.c
http://elixir.free-electrons.com/linux/latest/source/arch/arm64/kernel/signal.c#L402
http://elixir.free-electrons.com/linux/latest/source/arch/arm64/kernel/signal.c#L331
http://elixir.free-electrons.com/linux/latest/source/arch/arm64/kernel/signal.c#L288
http://wiki.bit-hive.com/north/pg/%A5%B7%A5%B0%A5%CA%A5%EB
http://wiki.bit-hive.com/north/pg/%A5%B7%A5%B0%A5%CA%A5%EB%28%A4%BD%A4%CE2%29
http://wiki.bit-hive.com/north/pg/%A5%B7%A5%B0%A5%CA%A5%EB%28%A4%BD%A4%CE3%29
http://wiki.bit-hive.com/north/pg/%A5%B7%A5%B0%A5%CA%A5%EB(%A4%BD%A4%CE4)-%A5%B7%A5%B9%A5%C6%A5%E0%A5%B3%A1%BC%A5%EB%BA%C6%BC%C2%B9%D4(sleep)
https://github.com/hiboma/hiboma/blob/master/Linux%E3%82%AB%E3%83%BC%E3%83%8D%E3%83%AB%E8%A7%A3%E8%AA%AD%E5%AE%A4/Linux%E3%82%AB%E3%83%BC%E3%83%8D%E3%83%AB%E8%A7%A3%E8%AA%AD%E5%AE%A4-5-2.md#do_signal
http://d.hatena.ne.jp/cupnes/20150504/1430712820
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/07/25 05:59 編集
2017/07/25 06:20
2017/07/25 07:17
2017/07/25 07:17
2017/07/25 07:20
2017/07/25 07:25
2017/07/26 01:23