■ はじめに
Magic SysRq Key (以降、SysRqKey と省略) とはシステムの障害時やデバッグ等で利用できる Linux カーネルの機能 です。キーボード上から Alt + SysRq + b を実行することでシステムを再起動させたり、Alt + SysRq + c で意図的にカーネルパニックを発生させたり等、その他にも様々な機能を備えています。
前々から SysRqKey に興味があった (勉強したかった) ので、使い方と機能と実装について調査しました。調査は主にv4.19 時点の公式ドキュメント (Linux Magic System Request Key Hacks) とソースコードから行いました。
■ 使い方と機能を知る
まずは、使い方と機能について調査しました。
1. 使い方と機能の要約
- SysRqKey はコマンドラインとキーボードの両方から使用可能
- コマンドラインの場合は
$ echo [コマンド] > /proc/sysrq-trigger
で実行 - キーボードの場合は Alt + SysRq +
[コマンド]
で実行 -
SysRqKey で有効な機能は
$ echo [制御値] > /proc/sys/kernel/sysrq
で制限可能 - システムフリーズ時になるべく安全に再起動するには 「r、e、i、s、u、b」を順次実行
2. 基本的な使い方
SysRqKey はコマンドラインとキーボードからそれぞれ利用できます。
-
コマンドラインから利用する場合
$ echo [コマンド] > /proc/sysrq-trigger
-
キーボードから利用する場合
Alt + SysRq +[コマンド]
[コマンド]
には後述の 「1-3. 機能(コマンド)一覧」 に記載のコマンド(半角英数字)が入ります。この部分を変更することで様々な機能が利用できます。
ただし、SysRqKey を使うには、前提として CONFIG_MAGIC_SYSRQ
が有効 (y
) である必要があります。この設定は /boot/config-<カーネルバージョン>
から確認できます。私の環境ではデフォルトで有効になっていました。
3. 機能(コマンド)一覧
SysRqKey で使用可能な機能(コマンド)は次の表に記載のとおりです。
なお SysRqKey の実行結果は dmesg
コマンドなどから確認できます。
コマンド | 挙動 |
---|---|
b | 即座にシステムを再起動します。その際ディスクの同期(sync)もアンマウントもしません。 |
c | NULL ポインタデリファレンスによりシステムをクラッシュさせます。 |
d | 現在保持している全てのロックを表示します (CONFIG_LOCKDEP 有効時)。 |
e | init プロセス(PID 1)を除く全プロセスへ SIGTERM シグナルを送信し、プロセスを終了させます。 |
f | OOM Killer を呼びメモリを多く消費しているプロセスを強制終了させます。 |
g | kgdb (kernel debugger) によって使用されます。 |
h | ヘルプを表示します。 |
i | init プロセス(PID 1)を除く全プロセスへ SIGKILL シグナルを送信し、プロセスを強制終了させます。 |
j | FIFREEZE ioctlによって凍結されたファイルシステムを解凍します (CONFIG_BLOCK 有効時)。 |
k | 仮想端末上の全プロセスに対して Secure Access Key (SAK) を実行し、終了させます (CONFIG_VT 有効時)。 |
l | 全てのアクティブな CPU のスタックバックトレースを表示します (CONFIG_SMP 有効時)。 |
m | 現在のメモリ情報をコンソールに表示します。 |
n | 優先度の高いリアルタイムタスクの nice レベルをリセットします。 |
o | システムをシャットダウンさせます。 |
p | 現在のレジスタおよびフラグをコンソールに表示します。 |
q | 全ての高精度タイマおよびクロックソースを表示します。 |
r | キーボードの raw モードを無効にして XLATE モードに設定します (CONFIG_VT 有効時)。 |
s | マウントされている全てのファイルシステムを同期(sync)しようとします。 |
t | 現在のタスクとそれらの情報の一覧を表示します。 |
u | 全てのマウントされたファイルシステムを読み取り専用で再マウントしようとします。 |
v | フレームバッファコンソールを強制的に復元します。 |
v [ARM 固有] | ETM バッファ表示します。 |
w | 割り込み不可能なスリープ状態 (D ステート) のタスクを表示します。 |
x | ppc/powerpc プラットフォームの xmon インタフェースによって使用されます。sparc 64 でグローバル PMU レジスタを表示します。MIPS の全ての TLB エントリを表示します。 |
y [SPARC-64 固有] | グローバル CPU レジスタを表示します。 |
z | ftrace バッファを表示します (CONFIG_TRACING 有効時)。 |
0 - 9 | どのログレベルでカーネルのメッセージを表示するかを設定します (例えば、0 なら PANIC や OOPS のような緊急度の高いメッセージのみが表示されます)。 |
4. 有効なコマンドの制御
システムで有効なコマンドは次のように制御できます1。
$ echo [制御値] > /proc/sys/kernel/sysrq
[制御値]
には次の表に記載の制御値が入ります。たとえば、4
を指定すると k、r が有効になります。複数のコマンドを有効にしたい場合は、それらに対応する制御値の合計値を指定します。4
と 16
なら 20
です。この場合は k、r に加え s が有効になります。
制御値 | 挙動 | 対象のコマンド |
---|---|---|
0 | sysrq の全機能を無効にします。 | 全てのコマンド |
1 | sysrq の全機能を有効にします。 | 全てのコマンド |
2 | コンソールのログレベルの制御を有効にします。 | 0 - 9 |
4 | キーボードの制御を有効にします。 | k、r |
8 | プロセスなどのデバッグダンプを有効にします。 | c、l、p、t、w、z、m |
16 | 同期 (sync) コマンドを有効にします。 | s |
32 | 読み取り専用で再マウントするコマンドを有効にします。 | u |
64 | プロセスのシグナル送信を有効にします。 | e、f、j、i |
128 | 再起動/電源オフを許可します。 | b |
256 | 全ての RT タスクの命名を許可します。 | n |
5. 動作確認
使い方と機能については以上です。ここからは、動作確認です。
5.1. 有効なコマンドの確認
まずは有効なコマンドを確認します。
私の環境では /proc/sys/kernel/sysrq
に 16
がデフォルトで設定されていました。16
で有効なコマンドは s なので、これを実行します。
[root@fdr28 ~]# echo s > /proc/sysrq-trigger
[root@fdr28 ~]# dmesg
...(snip)...
[394887.857272] sysrq: SysRq : Emergency Sync
[394887.860045] Emergency Sync complete
sysrq: SysRq : Emergency Sync
と Emergency Sync complete
が出力されました。前者はコマンド成功時に出力されるコマンド毎のメッセージで、後者はコマンドの実行結果です。今回は s なので出力されるログが少ないのですが、コマンドによっては、大量にログが出力されます。
5.2. 無効なコマンドの確認
16
で無効なコマンドである m を確認します(無効なコマンドは他にもあります)。
[root@fdr28 ~]# echo m > /proc/sysrq-trigger
[root@fdr28 ~]# dmesg
...(snip)...
[395027.990391] sysrq: SysRq : This sysrq operation is sdisabled.
This sysrq operation is disabled.
と出力され無効であることが確認できました。有効なコマンドのときにあったコマンド毎のメッセージも出力されていません。
5.3. 複数コマンドの有効化の確認
ここでは例として、既存で有効なコマンド s (制御値は 16
)に加え、前述の「1-5-2. 無効なコマンドの確認」で無効だったコマンド m (制御値は 8
)を有効にします。両方とも有効にするには 8 + 16 24
を /proc/sys/kernel/sysrq
に書き込みます。再起動などの特別な操作は不要です。
[root@fdr28 ~]# echo 24 > /proc/sys/kernel/sysrq
m が有効になっているか確認します。
[root@fdr28 ~]# echo m > /proc/sysrq-trigger
[root@fdr28 ~]# dmesg
...(snip)...
[200590.430782] sysrq: SysRq : Show Memory
[200590.430798] Mem-Info:
[200590.430815] active_anon:581575 inactive_anon:210237 isolated_anon:0
active_file:728564 inactive_file:304530 isolated_file:0
unevictable:0 dirty:178 writeback:0 unstable:0
slab_reclaimable:61448 slab_unreclaimable:33847
mapped:103241 shmem:95282 pagetables:16075 bounce:0
free:56782 free_pcp:1557 free_cma:0
...(snip)...
[200590.431115] 0 pages hwpoisoned
先ほどとは違い sysrq: SysRq : Show Memory
というコマンド毎のメッセージが表示された後、実行結果(メモリの情報)が表示されました。意図したとおりにコマンドが有効であることが確認できました。
6. 使いどころ
一応、SysRqKey の使いどころについても書いておきます。公式ドキュメントによると次のようなケースが使いどころのようです。障害時に役立ちそうな印象があります。
- X サーバや svgalib プログラムがクラッシュしたとき (r)
- トロイの木馬プログラムがコンソールで実行されていないことを確認したいとき (k)
- 何かしらの理由でシャットダウンできないとき (s -> y -> b)
- システムハング時にカーネルダンプを取得したいとき (c)
- システムフリーズ時になるべく安全に再起動したいとき (r -> e -> i -> s -> u -> b)2
- カーネルメッセージのログレベル絞りたい (0 - 9)
- 暴走しているプロセスを殺したいとき (e、i)
- FIFREEZE ioctl を介してフリーズしたファイルシステムが原因でシステムが応答しなくなったとき (j)
■ カーネル実装を読む
ここからはソースコードを元に SysRqKey の実装を読んでいきます。
なお、ここでは次のコマンドを実行した際に処理されるコードを元に読み進めていきます。
$ echo s > /proc/sysrq-trigger
1. 処理の全体像
まず $ echo s > /proc/sysrq-trigger
を実行した際の関数呼び出しを記載しておきます。今回は SysRqKey への理解を深める目的があるため、あまり関係のないコード (do_syscall_64
から _vfs_write
) は読み飛ばし、proc_reg_write
関数から読んでいくことにします。
do_syscall_64
ksys_write
vfs_write
__vfs_write
proc_reg_write <--- ここから一番下までが読む範囲です。
write_sysrq_trigger
__handle_sysrq
sysrq_handle_sync
emergency_sync
do_sync_work
2. proc_reg_write
この関数は procfs への書き込みをトリガーに呼び出されます。今回は /proc/sysrq-trigger
ファイルへ s
を書き込んでいるので呼び出されます。
237 static ssize_t proc_reg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
238 {
239 ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);
240 struct proc_dir_entry *pde = PDE(file_inode(file));
241 ssize_t rv = -EIO;
242 if (use_pde(pde)) {
243 write = pde->proc_fops->write;
244 if (write)
245 rv = write(file, buf, count, ppos);
246 unuse_pde(pde);
247 }
248 return rv;
249 }
読み進める上で重要な箇所は 243 行目です。ここで sysrq-trigger
の write
オペレーションのアドレスを格納しています。sysrq-trigger
のオペレーションでは .write = write_sysrq_trigger
と定義 (Memo_A) されているため、245 行目では write_sysrq_trigger
関数が呼び出されます。
**Memo_A**: `sysrq-trigger` のオペレーションの定義
1118 static const struct file_operations proc_sysrq_trigger_operations = {
1119 .write = write_sysrq_trigger,
1120 .llseek = noop_llseek,
1121 };
3. write_sysrq_trigger
proc_reg_write
関数の 245 行目から呼び出されます。
1100 #ifdef CONFIG_PROC_FS
1101 /*
1102 * writing 'C' to /proc/sysrq-trigger is like sysrq-C
1103 */
1104 static ssize_t write_sysrq_trigger(struct file *file, const char __user *buf,
1105 size_t count, loff_t *ppos)
1106 {
1107 if (count) {
1108 char c;
1109
1110 if (get_user(c, buf))
1111 return -EFAULT;
1112 __handle_sysrq(c, SYSRQ_FROM_PROC);
1113 }
1114
1115 return count;
1116 }
1110 行目では get_user
関数に変数 c
と buf
を渡して呼び出しています。調べたところ get_user
関数はユーザ空間からカーネル空間へ変数をコピーするためのもので、第一引数にコピー元の変数、第二引数にコピー元ユーザ空間のアドレスが渡されるようです。
また、1111 行目で返る EFAULT
は「アドレスが不正」という意味のエラーのようです。EFAULT
の定義元 (Memo_B) に「Bad adress」とコメントされていました。そのため、この関数では変数 buf
のアドレスが何かしらの理由で不正な場合は EFAULT
エラーが返り、そうでない場合は 1112 行目で __handle_sysrq
関数が呼び出されるのだと思います。
**Memo_B**: `EFAULT` の定義 (18 行目)
1 /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2 #ifndef _ASM_GENERIC_ERRNO_BASE_H
3 #define _ASM_GENERIC_ERRNO_BASE_H
4
5 #define EPERM 1 /* Operation not permitted */
6 #define ENOENT 2 /* No such file or directory */
7 #define ESRCH 3 /* No such process */
8 #define EINTR 4 /* Interrupted system call */
9 #define EIO 5 /* I/O error */
10 #define ENXIO 6 /* No such device or address */
11 #define E2BIG 7 /* Argument list too long */
12 #define ENOEXEC 8 /* Exec format error */
13 #define EBADF 9 /* Bad file number */
14 #define ECHILD 10 /* No child processes */
15 #define EAGAIN 11 /* Try again */
16 #define ENOMEM 12 /* Out of memory */
17 #define EACCES 13 /* Permission denied */
18 #define EFAULT 14 /* Bad address */
19 #define ENOTBLK 15 /* Block device required */
20 #define EBUSY 16 /* Device or resource busy */
21 #define EEXIST 17 /* File exists */
22 #define EXDEV 18 /* Cross-device link */
23 #define ENODEV 19 /* No such device */
24 #define ENOTDIR 20 /* Not a directory */
25 #define EISDIR 21 /* Is a directory */
26 #define EINVAL 22 /* Invalid argument */
27 #define ENFILE 23 /* File table overflow */
28 #define EMFILE 24 /* Too many open files */
29 #define ENOTTY 25 /* Not a typewriter */
30 #define ETXTBSY 26 /* Text file busy */
31 #define EFBIG 27 /* File too large */
32 #define ENOSPC 28 /* No space left on device */
33 #define ESPIPE 29 /* Illegal seek */
34 #define EROFS 30 /* Read-only file system */
35 #define EMLINK 31 /* Too many links */
36 #define EPIPE 32 /* Broken pipe */
37 #define EDOM 33 /* Math argument out of domain of func */
38 #define ERANGE 34 /* Math result not representable */
39
40 #endif
4. __handle_sysrq
write_sysrq_trigger
の 1112行目から呼び出されます。SysRqKey
においてメインな関数だと思います。
534 void __handle_sysrq(int key, unsigned int from)
535 {
536 struct sysrq_key_op *op_p;
537 int orig_log_level;
538 int i;
539
540 rcu_sysrq_start();
541 rcu_read_lock();
542 /*
543 * Raise the apparent loglevel to maximum so that the sysrq header
544 * is shown to provide the user with positive feedback. We do not
545 * simply emit this at KERN_EMERG as that would change message
546 * routing in the consumers of /proc/kmsg.
547 */
548 orig_log_level = console_loglevel;
549 console_loglevel = CONSOLE_LOGLEVEL_DEFAULT;
550 pr_info("SysRq : ");
551
552 op_p = __sysrq_get_key_op(key);
553 if (op_p) {
554 /* Ban synthetic events from some sysrq functionality */
555 if ((from == SYSRQ_FROM_PROC || from == SYSRQ_FROM_SYNTHETIC) &&
556 op_p->enable_mask & SYSRQ_DISABLE_USERSPACE)
557 printk("This sysrq operation is disabled from userspace.\n");
552 行目で指定のコマンド (s) とリンクするオペレーションを取得しているようです。コマンド毎にリンクするオペレーションは次の「Memo_C」のように定義されています。取得したオペレーションが NULL
でないコマンドは 554 行目から処理され、h など NULL
であるコマンドは後述の 570 行目で処理されます。
**Memo_C**: コマンド毎のオペレーション定義(sysrq_key_table)
437 static struct sysrq_key_op *sysrq_key_table[36] = {
438 &sysrq_loglevel_op, /* 0 */
439 &sysrq_loglevel_op, /* 1 */
440 &sysrq_loglevel_op, /* 2 */
441 &sysrq_loglevel_op, /* 3 */
442 &sysrq_loglevel_op, /* 4 */
443 &sysrq_loglevel_op, /* 5 */
444 &sysrq_loglevel_op, /* 6 */
445 &sysrq_loglevel_op, /* 7 */
446 &sysrq_loglevel_op, /* 8 */
447 &sysrq_loglevel_op, /* 9 */
448
449 /*
450 * a: Don't use for system provided sysrqs, it is handled specially on
451 * sparc and will never arrive.
452 */
453 NULL, /* a */
454 &sysrq_reboot_op, /* b */
455 &sysrq_crash_op, /* c */
456 &sysrq_showlocks_op, /* d */
457 &sysrq_term_op, /* e */
458 &sysrq_moom_op, /* f */
459 /* g: May be registered for the kernel debugger */
460 NULL, /* g */
461 NULL, /* h - reserved for help */
462 &sysrq_kill_op, /* i */
463 #ifdef CONFIG_BLOCK
464 &sysrq_thaw_op, /* j */
465 #else
466 NULL, /* j */
467 #endif
468 &sysrq_SAK_op, /* k */
469 #ifdef CONFIG_SMP
470 &sysrq_showallcpus_op, /* l */
471 #else
472 NULL, /* l */
473 #endif
474 &sysrq_showmem_op, /* m */
475 &sysrq_unrt_op, /* n */
476 /* o: This will often be registered as 'Off' at init time */
477 NULL, /* o */
477 NULL, /* o */
478 &sysrq_showregs_op, /* p */
479 &sysrq_show_timers_op, /* q */
480 &sysrq_unraw_op, /* r */
481 &sysrq_sync_op, /* s */
482 &sysrq_showstate_op, /* t */
483 &sysrq_mountro_op, /* u */
484 /* v: May be registered for frame buffer console restore */
485 NULL, /* v */
486 &sysrq_showstate_blocked_op, /* w */
487 /* x: May be registered on mips for TLB dump */
488 /* x: May be registered on ppc/powerpc for xmon */
489 /* x: May be registered on sparc64 for global PMU dump */
490 /* x: May be registered on x86_64 for disabling secure boot */
491 NULL, /* x */
492 /* y: May be registered on sparc64 for global register dump */
493 NULL, /* y */
494 &sysrq_ftrace_dump_op, /* z */
495 };
558 /*
559 * Should we check for enabled operations (/proc/sysrq-trigger
560 * should not) and is the invoked operation enabled?
561 */
562 if (from == SYSRQ_FROM_KERNEL || sysrq_on_mask(op_p->enable_mask)) {
562 行目では実行コマンドが有効であるかをチェックしています。sysrq_on_mask
関数では「1-4. 有効なコマンドの制御」で登場した /proc/sys/kernel/sysrq
の制御値 (sysrq_enabled
) とコマンド毎のマスク値 (enable_mask
) で 論理積をとっています (Memo_D)。
**Memo_D**: `sysrq_on_mask` の定義(sysrq_key_table)
66 /*
67 * A value of 1 means 'all', other nonzero values are an op mask:
68 */
69 static bool sysrq_on_mask(int mask)
70 {
71 return sysrq_always_enabled ||
72 sysrq_enabled == 1 ||
73 (sysrq_enabled & mask);
74 }
なお「Memo_D」で使用している sysrq_enabled
は /proc/sys/kernel/sysrq
の値なわけですが、その値は「Memo_E」の関数で設定されているようです (sysrq_enabled
は drivers/tty/sysrq.c
でグローバルな変数です)。
**Memo_E**: `sysrq_toggle_support` の定義(sysrq_key_table)
1048 int sysrq_toggle_support(int enable_mask)
1049 {
1050 bool was_enabled = sysrq_on();
1051
1052 sysrq_enabled = enable_mask;
1053
1054 if (was_enabled != sysrq_on()) {
1055 if (sysrq_on())
1056 sysrq_register_handler();
1057 else
1058 sysrq_unregister_handler();
1059 }
1060
1061 return 0;
1062 }
563 pr_cont("%s\n", op_p->action_msg);
564 console_loglevel = orig_log_level;
565 op_p->handler(key);
コマンドコマンドが有効な場合は、563行目で実行コマンドとリンクする action_msg
オペレーション (Emergency Sync
) がリングバッファに出力され、565 行目で handler
オペレーション (sysrq_handle_sync
) が呼び出されます。
566 } else {
567 pr_cont("This sysrq operation is disabled.\n");
568 }
567 行目は実行コマンドが無効である場合にとおるルートです。その場合、リングバッファに This sysrq operation is disabled.
が出力されます。
569 } else {
570 pr_cont("HELP : ");
571 /* Only print the help msg once per handler */
572 for (i = 0; i < ARRAY_SIZE(sysrq_key_table); i++) {
573 if (sysrq_key_table[i]) {
574 int j;
575
576 for (j = 0; sysrq_key_table[i] !=
577 sysrq_key_table[j]; j++)
578 ;
579 if (j != i)
580 continue;
581 pr_cont("%s ", sysrq_key_table[i]->help_msg);
582 }
583 }
584 pr_cont("\n");
585 console_loglevel = orig_log_level;
586 }
587 rcu_read_unlock();
588 rcu_sysrq_end();
589 }
570 行目からは 552 行目の op_p = __sysrq_get_key_op(key);
で取得したオペレーションが NULL
である場合にとおるルートで、ヘルプとして機能します。たとえば、オペレーションが NULL
の h コマンドを実行 (echo h > /proc/sysrq-trigger
) すると 570 行目で HELP :
と表示した後、581 行目で全てのコマンドに対応する help_msg
オペレーション (ヘルプメッセージ) をリングバッファに出力します (Memo_F)。
**Memo_F**: `echo h > /proc/sysrq-trigger` 実行例
[root@fdr28 ~]# dmesg
...(snip)...
[ 175.628520] sysrq: SysRq : HELP : loglevel(0-9) reboot(b) crash(c) terminate-all-tasks(e) memory-full-oom-kill(f) kill-all-tasks(i) thaw-filesystems(j) sak(k) show-backtrace-all-active-cpus(l) show-memory-usage(m) nice-all-RT-tasks(n) poweroff(o) show-registers(p) show-all-timers(q) unraw(r) sync(s) show-task-states(t) unmount(u) force-fb(V) show-blocked-tasks(w) dump-ftrace-buffer(z)
5. sysrq_handle_sync
すこし話が戻りますが「2-4. __handle_sysrq」の 565 行目で呼び出される実行コマンドの handler
オペレーションの先を読んでいきます。565 行目付近を再掲します。
562 if (from == SYSRQ_FROM_KERNEL || sysrq_on_mask(op_p->enable_mask)) {
563 pr_cont("%s\n", op_p->action_msg);
564 console_loglevel = orig_log_level;
565 op_p->handler(key);
今回の調査対象である実行コマンドである s のオペレーションの定義は次のとおりです。
173 static struct sysrq_key_op sysrq_sync_op = {
174 .handler = sysrq_handle_sync,
175 .help_msg = "sync(s)",
176 .action_msg = "Emergency Sync",
177 .enable_mask = SYSRQ_ENABLE_SYNC,
178 };
そのため、565 行目で実行される handler
オペレーションは sysrq_handle_sync
です。
169 static void sysrq_handle_sync(int key)
170 {
171 emergency_sync();
172 }
sysrq_handle_sync
は emergency_sync
関数を呼び出しているだけです。
6. emergency_sync
emergency_sync
関数の定義は次のとおりです。
146 void emergency_sync(void)
147 {
148 struct work_struct *work;
149
150 work = kmalloc(sizeof(*work), GFP_ATOMIC);
151 if (work) {
152 INIT_WORK(work, do_sync_work);
153 schedule_work(work);
154 }
155 }
150 行目の work
には 「高優先度、スリープ不可」を意味する GFP_ATOMIC
フラグを立ててメモリを割り当てています。
152 行目の INIT_WORK
は第二引数に渡された関数である do_sync_work
を 153 行目のワークキューに追加する前に必要な初期化や設定を行うマクロで、153 行目の schedule_work
関数でワークキューに追加します。
ワークキューに追加された関数はワーカスレッドは、遅延させて呼び出すことができますが、今回は 150 行目で「高優先度、スリープ不可」の GFP_ATOMIC
フラグを立てているため、このスレッドは即座に実行されます。
(と、書きましたがワークキューやらワーカースレッド周りの知識は自信ありません)
7. do_sync_work
ワーカ・スレッドにより実行する do_sync_work
関数の定義は次のとおりです。
128 static void do_sync_work(struct work_struct *work)
129 {
130 int nowait = 0;
131
132 /*
133 * Sync twice to reduce the possibility we skipped some inodes / pages
134 * because they were temporarily locked
135 */
136 iterate_supers(sync_inodes_one_sb, &nowait);
137 iterate_supers(sync_fs_one_sb, &nowait);
138 iterate_bdevs(fdatawrite_one_bdev, NULL);
139 iterate_supers(sync_inodes_one_sb, &nowait);
140 iterate_supers(sync_fs_one_sb, &nowait);
141 iterate_bdevs(fdatawrite_one_bdev, NULL);
136 ~ 137、139 ~ 140 行目の iterate_supers
は fs/super.c
にある定義されている関数でコメントによると全てのアクティブなスーパーブロックに対して第一引数の関数を実行するための関数のようです (???)。138、140 行目の iterate_bdevs
関数は fs/bloc_dev.c
にある関数で特にコメントありませんでしたが、bdevs とあるので、全てのブロックデバイスに対して第一引数の関数を実行するめの関数とか、そうゆう感じだと思います (この辺はちゃんと読んでないです)。
sync_inodes_one_sb
、sync_fs_one_sb
、fdatawrite_one_bdev
関数を 2 回ずつ実行しています。これは、133 ~ 134 行目のコメントによると、一時的なロックにより、同期できなかった inode やページを減らすための処置のようです。
142 printk("Emergency Sync complete\n");
143 kfree(work);
144 }
142 行目では s コマンドの処理が完了したことを示す Emergency Sync complete
をリングバッファに出力し、143 行目で emergency_sync
関数の 150 行目で確保したメモリを解放しています。
■ 調査環境
調査環境および関連ツールのバージョンは次のとおりです。
- ディストリビューション (Fedora 28)
- カーネル (4.19.16-200.fc28.x86_64)
- echo コマンド (coreutils-8.29-7.fc28.x86_64)
- glibc (glibc-2.27-37.fc28.x86_64)
調査に使用したソースコードも上記カーネルと同じ Fedora のカーネル(4.19.16-200.fc28
)です。
なお、Fedora カーネのソースコードは次の手順で取得しました。
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/jwboyer/fedora.git
$ git checkout -b <branch_name> kernel-4.19.16-200.fc28
■ おわりに
調査の結果、SysRqKey の使い方や機能については把握できましたが、今の私のレベルだと使いどころがよく分からない機能が多い印象でした。m で表示されるメモリの情報にはどのような意味があるのかなど、一つ一つの機能を掘り下げて理解することで、使いどころが見えてくるような気もしますが、今はそこまでのモチベがないので、また今度にします。
また、コードリーディングの記録を残すという試みは今回が初めてでしたが非常に勉強になりました。アウトプットすることで考えが整理され、実装を理解する助けになりましたし、普段なら見過すようなコードにも目を向けることができ、知見が広がったような感覚があります (普段はなんとなく理解できれば良いやという感じでナナメヨミすることが多いです)。
なお、私が Linux カーネルを学び始めたのは最近であるため、理解が浅く、おかしな解釈や言葉遣いをしている個所もあるかと思います。そういった箇所はご指摘いただけると幸いです。