第2章ユーザモードで実現する機能
基本的には、ユーザモードの処理からシステムコールを介してカーネルの処理を呼び出す。
システムコールの種類
プロセス生成・削除
メモリ確保・解放
プロセス間通信
ネットワーク
ファイルシステム操作
ファイル操作(デバイスアクセス)
CPUモードの遷移
プロセスは通常ユーザモードで実行しているが、カーネルに処理を依頼するためにシステムコールを発行すると、CPUで割り込みというイベントが発生する。
割り込みにより、CPUではユーザモードからカーネルモードに遷移して、依頼内容に応じたカーネルの処理が起動する。
カーネル内のシステムコール処理が終了すれば、再びユーザモードに戻ってプロセスの動作を継続する。
カーネルは処理の冒頭で、プロセスからの要求が正当なものかチェックし、不正な要求であればシステムコールを失敗させる。
当たり前だがユーザープロセスからシステムコールを介さず直接CPUのモードを変更する方法は無い。
システムコールの呼び出し
straceコマンドで確認できる
以下のファイルを作成
code:hello.c
int main(void)
{
puts("hello, world");
return 0;
}
コンパイルして実行すると
$ cc -o hello hello.c
$ /.hello
hello, world
と表示される
straceコマンドを使うと
$ strace -o hello.log ./hello
hello, world
同じように表示されている。logの中身を見ると、下から3行目のwrite()システムコールによって、hello, world\nという文字列を出力している
code: hello.log
brk(NULL) = 0x17f7000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=81719, ...}) = 0
mmap(NULL, 81719, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f83b1c6f000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\t\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1868984, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83b1c6e000
mmap(NULL, 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f83b1694000
mprotect(0x7f83b1854000, 2097152, PROT_NONE) = 0
mmap(0x7f83b1a54000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) = 0x7f83b1a54000
mmap(0x7f83b1a5a000, 14752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f83b1a5a000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83b1c6d000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83b1c6c000
arch_prctl(ARCH_SET_FS, 0x7f83b1c6d700) = 0
mprotect(0x7f83b1a54000, 16384, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ) = 0
mprotect(0x7f83b1c83000, 4096, PROT_READ) = 0
munmap(0x7f83b1c6f000, 81719) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 8), ...}) = 0
brk(NULL) = 0x17f7000
brk(0x1818000) = 0x1818000
write(1, "hello, world\n", 13) = 13
exit_group(0) = ?
+++ exited with 0 +++
上記31回のシステムコールのほとんどはhello.cのmain関数の前後に実行される、プログラムの開始処理と終了処理
プロセスがユーザモードとカーネルモードそれぞれで実行している割合
sarコマンドで取得できる
$ sar -P ALL 1
code:sar
06:56:55 AM CPU %user %nice %system %iowait %steal %idle
06:56:56 AM all 0.00 0.00 0.00 0.00 0.00 100.00
06:56:56 AM 0 0.00 0.00 0.00 0.00 0.00 100.00
06:56:56 AM 1 0.00 0.00 0.00 0.00 0.00 100.00
06:56:56 AM 2 0.00 0.00 0.00 0.00 0.00 100.00
06:56:56 AM 3 0.00 0.00 0.00 0.00 0.00 100.00
06:56:56 AM CPU %user %nice %system %iowait %steal %idle
06:56:57 AM all 0.00 0.00 0.00 0.00 0.00 100.00
06:56:57 AM 0 0.00 0.00 0.00 0.00 0.00 100.00
06:56:57 AM 1 0.99 0.00 0.00 0.00 0.00 99.01
06:56:57 AM 2 0.00 0.00 0.00 0.00 0.00 100.00
06:56:57 AM 3 0.00 0.00 0.00 0.00 0.00 100.00
06:56:57 AM CPU %user %nice %system %iowait %steal %idle
06:56:58 AM all 0.00 0.00 0.25 0.00 0.00 99.75
06:56:58 AM 0 0.00 0.00 0.00 0.00 0.00 100.00
06:56:58 AM 1 0.00 0.00 0.00 0.00 0.00 100.00
06:56:58 AM 2 0.00 0.00 0.00 0.00 0.00 100.00
06:56:58 AM 3 0.00 0.00 0.00 0.00 0.00 100.00
(中略)
Average: CPU %user %nice %system %iowait %steal %idle
Average: all 0.04 0.00 0.11 0.05 0.00 99.80
Average: 0 0.07 0.00 0.00 0.21 0.00 99.71
Average: 1 0.14 0.00 0.21 0.00 0.00 99.64
Average: 2 0.00 0.00 0.00 0.00 0.00 100.00
Average: 3 0.00 0.00 0.14 0.00 0.00 99.86
CPUコア場で実行していた処理の種類%userから%idleの各フィールドを足すと100%になる
ユーザモードで実行している時間の割合は%userと%niceの合計
カーネルモードで実行している時間の割合は%system
システムコールを発行せず、ひたすら無限ループをするプログラムを実行したらどうなるか
code: loop.c
int main(void)
{
for(;;)
;
}
cc -o loop loop.c
./loop &
sar -P ALL 1 1
code:sar
07:05:12 AM CPU %user %nice %system %iowait %steal %idle
07:05:13 AM all 25.06 0.00 0.00 0.00 0.00 74.94
07:05:13 AM 0 0.00 0.00 0.00 0.00 0.00 100.00
07:05:13 AM 1 0.00 0.00 0.00 0.00 0.00 100.00
07:05:13 AM 2 0.00 0.00 0.00 0.00 0.00 100.00
07:05:13 AM 3 100.00 0.00 0.00 0.00 0.00 0.00
Average: CPU %user %nice %system %iowait %steal %idle
Average: all 25.06 0.00 0.00 0.00 0.00 74.94
Average: 0 0.00 0.00 0.00 0.00 0.00 100.00
Average: 1 0.00 0.00 0.00 0.00 0.00 100.00
Average: 2 0.00 0.00 0.00 0.00 0.00 100.00
Average: 3 100.00 0.00 0.00 0.00 0.00 0.00
親プロセスのプロセスIDを取得するシステムコールgetppid()を無限ループさせるとどうなるか
code:ppidloop.c
int main(void)
{
for (;;)
getppid();
}
cc -o ppidloop ppidloop.c
./ppidloop &
sar -P ALL 1 1
code:sar
07:20:26 AM CPU %user %nice %system %iowait %steal %idle
07:20:27 AM all 10.00 0.00 15.25 0.00 0.00 74.75
07:20:27 AM 0 0.00 0.00 0.00 0.00 0.00 100.00
07:20:27 AM 1 0.00 0.00 0.00 0.00 0.00 100.00
07:20:27 AM 2 0.00 0.00 0.00 0.00 0.00 100.00
07:20:27 AM 3 39.00 0.00 61.00 0.00 0.00 0.00
Average: CPU %user %nice %system %iowait %steal %idle
Average: all 10.00 0.00 15.25 0.00 0.00 74.75
Average: 0 0.00 0.00 0.00 0.00 0.00 100.00
Average: 1 0.00 0.00 0.00 0.00 0.00 100.00
Average: 2 0.00 0.00 0.00 0.00 0.00 100.00
Average: 3 39.00 0.00 61.00 0.00 0.00 0.00
CPU上ではppidloopプログラムを39%の割合で処理している
ppidloopプログラムの依頼に対応して、このプログラムの親プロセスを取得するカーネルの処理を61%の割合で実行している。
システムコールの計測時間
strace -T -o hello.log ./hello
code:hello.log
execve("./hello", "./hello", 26 vars */) = 0 <0.000232> brk(NULL) = 0x225f000 <0.000014>
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) <0.000018>
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) <0.000016>
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 <0.000021>
fstat(3, {st_mode=S_IFREG|0644, st_size=81719, ...}) = 0 <0.000016>
mmap(NULL, 81719, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fb35cb75000 <0.000020>
close(3) = 0 <0.000014>
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) <0.000017>
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 <0.000020>
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\t\2\0\0\0\0\0"..., 832) = 832 <0.000016>
fstat(3, {st_mode=S_IFREG|0755, st_size=1868984, ...}) = 0 <0.000014>
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb35cb74000 <0.000016>
mmap(NULL, 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb35c59a000 <0.000017>
mprotect(0x7fb35c75a000, 2097152, PROT_NONE) = 0 <0.000020>
mmap(0x7fb35c95a000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) = 0x7fb35c95a000 <0.000021>
mmap(0x7fb35c960000, 14752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb35c960000 <0.000016>
close(3) = 0 <0.000014>
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb35cb73000 <0.000015>
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb35cb72000 <0.000014>
arch_prctl(ARCH_SET_FS, 0x7fb35cb73700) = 0 <0.000015>
mprotect(0x7fb35c95a000, 16384, PROT_READ) = 0 <0.000019>
mprotect(0x600000, 4096, PROT_READ) = 0 <0.000016>
mprotect(0x7fb35cb89000, 4096, PROT_READ) = 0 <0.000032>
munmap(0x7fb35cb75000, 81719) = 0 <0.000029>
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 8), ...}) = 0 <0.000016>
brk(NULL) = 0x225f000 <0.000014>
brk(0x2280000) = 0x2280000 <0.000015>
write(1, "hello, world\n", 13) = 13 <0.000030>
exit_group(0) = ?
+++ exited with 0 +++
hello, worldという文字列を出力するのに30マイクロ秒かかっている
OSが提供するライブラリ
システムの初期化
init
OSの挙動を変える
sysctl
nice
sync
ファイル操作
touch
mkdir
テキストデータの加工
grep
sort
uniq
性能測定
sar
iostat
コンパイラ
gcc
スクリプト言語実行環境
perl
python
ruby
シェル
bash
ウィンドウシステム
X