NetBSD Advent Calendar 2016、25日目の記事は11日目の記事で構築した、NetBSDカーネルのビルド・テスト環境を利用してカーネル内部の処理を追いかけてみようと思います。
本当はkgdbでカーネルデバッグ環境を作るネタを紹介したかったのですが、いまいちVirtualBox環境でのシリアルポート設定がうまく行えなかったため、今回はコードリーディングの形で処理の流れを把握してみます。
カーネルのソースコードはどこから追いかける?
カーネルのソースコードを読む場合、ついつい最新機能の処理部分に目が向いてしまいがちで、新機能を追いかけようとする→プロセス構造体等の基本的な構成の把握に時間がかかる、という落とし穴にハマることが良くあります。
カーネルのビルド・テスト環境が手軽に構築できるようになったので、まずはユーザランドでの動作を把握できるような基本的な機能はカーネル内でどういう処理・振る舞いになっているのか調べてみます。
(これを積み重ねて行けば新機能の処理とかも読み解きやすくなるかと)
ulimitの機能を調べてみる
というワケで、とりあえず今回はulimit
によるプロセスのリソース制限まわりについて、カーネル内でどう扱われているかを追いかけてみます。
$ ulimit -a
time(cpu-seconds) unlimited
file(blocks) unlimited
coredump(blocks) unlimited
data(kbytes) 262144
stack(kbytes) 4096
lockedmem(kbytes) 163233
memory(kbytes) 489700
nofiles(descriptors) 1024
processes 1024
threads 1024
vmemory(kbytes) unlimited
sbsize(bytes) unlimited
カーネルのリソース構造体
さっそくカーネルのソースコードを追いかけてみます。struct proc
のサブ構造体struct plimit *p_limit
がリソース制限まわりの情報を保持している構造体のようです。
sys/proc.h:
187 /*
188 * Description of a process.
...
210 struct proc {
211 LIST_ENTRY(proc) p_list; /* l: List of all processes */
...
220 /* Substructures: */
...
225 struct plimit *p_limit; /* :: Process limits */
struct plimit
はstruct rlimit[]
という配列のメンバ変数を保持しており、struct rlimit
はrlimit_cur
とrlimit_max
というメンバ変数を持っています。
sys/resourcevar.h:
69 /*
70 * Process resource limits. Since this structure is moderately large,
71 * but changes infrequently, it is shared copy-on-write after forks.
72 *
73 * When a separate copy is created, then 'pl_writeable' is set to true,
74 * and 'pl_sv_limit' is pointed to the old proc_t::p_limit structure.
75 */
76 struct plimit {
77 struct rlimit pl_rlimit[RLIM_NLIMITS];
78 char * pl_corename;
79 size_t pl_cnlen;
80 u_int pl_refcnt;
81 bool pl_writeable;
82 kmutex_t pl_lock;
83 struct plimit * pl_sv_limit;
84 };
sys/resource.h:
111 struct rlimit {
112 rlim_t rlim_cur; /* current (soft) limit */
113 rlim_t rlim_max; /* maximum value for rlim_cur */
114 };
115
struct rlimit[]
の配列インデックス値はマクロ定数で定義されており、どうやらこのインデックスで参照される値がumit -a
コマンドで表示される値のようですね。
sys/resource.h:
78 /*
79 * Resource limits
80 */
81 #define RLIMIT_CPU 0 /* cpu time in milliseconds */
82 #define RLIMIT_FSIZE 1 /* maximum file size */
83 #define RLIMIT_DATA 2 /* data size */
84 #define RLIMIT_STACK 3 /* stack size */
85 #define RLIMIT_CORE 4 /* core file size */
86 #define RLIMIT_RSS 5 /* resident set size */
87 #define RLIMIT_MEMLOCK 6 /* locked-in-memory address space */
88 #define RLIMIT_NPROC 7 /* number of processes */
89 #define RLIMIT_NOFILE 8 /* number of open files */
90 #define RLIMIT_SBSIZE 9 /* maximum size of all socket buffers */
91 #define RLIMIT_AS 10 /* virtual process size (inclusive of mmap) */
92 #define RLIMIT_VMEM RLIMIT_AS /* common alias */
93 #define RLIMIT_NTHR 11 /* number of threads */
94
95 #if defined(_NETBSD_SOURCE)
96 #define RLIM_NLIMITS 12 /* number of resource limits */
97 #endif
$ ulimit -a
number of threads (-T) 1024
socket buffer size (bytes, -b) unlimited
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) 262144
file size (blocks, -f) unlimited
max locked memory (kbytes, -l) 163233
max memory size (kbytes, -m) 489700
open files (-n) 1024
pipe size (512 bytes, -p) 1
stack size (kbytes, -s) 4096
cpu time (seconds, -t) unlimited
max user processes (-u) 1024
virtual memory (kbytes, -v) unlimited
例として、ulimit -a
コマンドのfile size
はどう設定されているか見てみます。値として「無制限」を意味する値が設定されており、RLIMIT_FSIZE
のインデックス値で参照できるstruct rlimit[]
の値が設定されている箇所を調べれば良さそうです。
sys/resource.h:
82 #define RLIMIT_FSIZE 1 /* maximum file size */
ソースコードを追いかけてみると、kern/kern_acct.c:acct_process()
でRLIM_INFINITY
という値が設定されていました。いかにも「無制限」を意味するようなマクロ定数な感じがします。
kern/kern_acct.c:
399 /*
400 * Write out process accounting information, on process exit.
401 * Data to be written out is specified in Leffler, et al.
402 * and are enumerated below. (They're also noted in the system
403 * "acct.h" header file.)
404 */
405 int
406 acct_process(struct lwp *l)
407 {
413 struct proc *p = l->l_proc;
...
434 /* Set current and max to avoid illegal values */
435 p->p_rlimit[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
436 p->p_rlimit[RLIMIT_FSIZE].rlim_max = RLIM_INFINITY;
RLIM_INFINITY
のマクロ手数が定義されている箇所を見てみます。struct rlimit->rlim_cur
にはu_quad_t
なサイズ(ビット長)の値が設定できること、msb(最上位ビット)以外がすべて1の場合は「無制限」を意味することがわかります。
sys/resource.h:
99 #define RLIM_INFINITY (~((u_quad_t)1 << 63)) /* no limit */
100 #define RLIM_SAVED_MAX RLIM_INFINITY /* unrepresentable hard limit */
101 #define RLIM_SAVED_CUR RLIM_INFINITY /* unrepresentable soft limit */
という感じで、いつも使う機能についてNetBSDカーネルのソースコードではどうデータが表現されているかを追いかけてみました。
(せっかくカーネルのビルド・テスト環境を用意したけどカーネルを動作させてみるとかしていないですね...)
まとめ
日常使うような機能について、NetBSDカーネル内部ではどのように値が保持・管理されているかをullimit -a
コマンドを例にして調べてみました。
カーネルの新しい機能の調査も面白いですが、日々使う機能についてカーネル側での振る舞いを把握しておくとOSの理解やトラブルシューティングに役立つのではないでしょうか。
おわりに
今年もなんとか無事にNetBSD Advent Calendar 2016を完走することができました。
これもひとえに皆様のおかげです。私の投稿がだいぶ滞ってしまい、参加者の皆様にはご迷惑をおかけしてしまいました。
ONODERA Ryo様、Enomoto Yuuki様、Ebihara Jun様、Makoto Fujiwara様、oshimaya様、ISIHARA Takanori様(投稿日順です)ありがとうございました。
参加表明されていた方もありがとうございます(代理投稿したらどなたが参加表明されていたのかわからなくなってしまいました、すみません...)。
特にONODERA Ryo様にはご多忙にも関わらず数多くの投稿をいだたき、本当に助かりました。ありがとうございます。
今年は個人的にバタバタしてしまい、記事の投稿が遅くなってしまったのが反省点です。来年は11月頭くらいから準備しておくようにしようと思います。
ともかく、これで無事にNetBSD Advent Calendar 2016完走ですね。どうか来年もご参加いただければと思います。
(ちょっと早いですが)それではよいお年を!