"ulimit -a"の値をNetBSDのカーネル側から調べてみる

[NetBSD Advent Calendar 2016](http://qiita.com/advent-calendar/2016/netbsd)、25日目の記事は[11日目の記事](http://qiita.com/furandon_pig/items/98b5cf24e73376056974)で構築した、NetBSDカーネルのビルド・テスト環境を利用してカーネル内部の処理を追いかけてみようと思います。
本当はkgdbでカーネルデバッグ環境を作るネタを紹介したかったのですが、いまいちVirtualBox環境でのシリアルポート設定がうまく行えなかったため、今回はコードリーディングの形で処理の流れを把握してみます。

## カーネルのソースコードはどこから追いかける？

カーネルのソースコードを読む場合、ついつい最新機能の処理部分に目が向いてしまいがちで、新機能を追いかけようとする→プロセス構造体等の基本的な構成の把握に時間がかかる、という落とし穴にハマることが良くあります。

[カーネルのビルド・テスト環境が手軽に構築](http://qiita.com/furandon_pig/items/98b5cf24e73376056974)できるようになったので、まずはユーザランドでの動作を把握できるような基本的な機能はカーネル内でどういう処理・振る舞いになっているのか調べてみます。
(これを積み重ねて行けば新機能の処理とかも読み解きやすくなるかと)

## 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`がリソース制限まわりの情報を保持している構造体のようです。

```c
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`というメンバ変数を持っています。

```c
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 };
```
 
```c
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`コマンドで表示される値のようですね。

```c
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[]`の値が設定されている箇所を調べれば良さそうです。

```c
sys/resource.h:
 82 #define RLIMIT_FSIZE    1               /* maximum file size */
```

ソースコードを追いかけてみると、`kern/kern_acct.c:acct_process()`で`RLIM_INFINITY`という値が設定されていました。いかにも「無制限」を意味するようなマクロ定数な感じがします。

```c
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の場合は「無制限」を意味することがわかります。

```c
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](http://qiita.com/advent-calendar/2016/netbsd)を完走することができました。
これもひとえに皆様のおかげです。私の投稿がだいぶ滞ってしまい、参加者の皆様にはご迷惑をおかけしてしまいました。

[ONODERA Ryo](http://qiita.com/ryoon)様、[Enomoto Yuuki](http://qiita.com/user340)様、[Ebihara Jun](http://qiita.com/ebijun)様、[Makoto Fujiwara](http://www.ki.nu/~makoto/)様、[oshimaya](https://twitter.com/oshimyja)様、[ISIHARA Takanori](https://twitter.com/tisihara)様(投稿日順です)ありがとうございました。
参加表明されていた方もありがとうございます(代理投稿したらどなたが参加表明されていたのかわからなくなってしまいました、すみません...)。

特に[ONODERA Ryo](http://qiita.com/ryoon)様にはご多忙にも関わらず数多くの投稿をいだたき、本当に助かりました。ありがとうございます。

今年は個人的にバタバタしてしまい、記事の投稿が遅くなってしまったのが反省点です。来年は11月頭くらいから準備しておくようにしようと思います。

ともかく、これで無事にNetBSD Advent Calendar 2016完走ですね。どうか来年もご参加いただければと思います。

(ちょっと早いですが)それではよいお年を！