今回はこちらの書籍「[試して理解]Linuxのしくみ〜実験と図解で学ぶOSとハードウェアの基礎知識」 の2章 ユーザモードで実現する機能について、自己理解のために内容をかいつまんで要約します。素晴らしい書籍で理解が進むと思いますのでぜひご購入を検討ください。
プロセスとOSの関わりについて
Linuxではハードウェアリソースをプロセスやアプリが勝手にさわれないように、カーネルを経由した操作しか認めていません。ハードウェアを触りたい場合はカーネルに対してシステムコールを発行することで依頼を行うことになります。
CPUのモードについて
CPUにはユーザモードとカーネルモードと呼ばれる2つのモードがあります。CPUがプロセスの処理を実施している場合はユーザモードとなりますが、プロセスがシステムコールを発行するとカーネルモードに遷移してカーネルが処理を行います。
カーネルモードとユーザモードの割合を計測する
timeコマンド
[root@master vagrant]# time kubectl get all > /dev/null real 0m0.142s user 0m0.068s sys 0m0.041s
- real...実際にかかった時間
- user...ユーザモードでの実行時間
- sys...カーネルモードでの実行時間
user
とsys
は実際にCPUを使用した時間のためtime sleep 10 | sleep 10 | sleep 10
のような子プロセスが動く場合はトータルで使用されたCPU時間が表示されます。
直感に反するケースが並行・並列にプロセスが動作した場合で3プロセスが並列に走り10秒で全ての処理が完了した場合、CPUコアをそれぞれが100%使っていたとすると10 × 3で30秒のCPU実行時間となります、
sarコマンド
[root@master vagrant]# sar -P ALL 1 Linux 3.10.0-957.12.2.el7.x86_64 (master) 2020年04月21日 _x86_64_ (2 CPU) 19時52分42秒 CPU %user %nice %system %iowait %steal %idle 19時52分43秒 all 7.57 0.00 5.95 0.00 0.00 86.49 19時52分43秒 0 6.52 0.00 5.43 0.00 0.00 88.04 19時52分43秒 1 7.61 0.00 5.43 0.00 0.00 86.96 19時52分43秒 CPU %user %nice %system %iowait %steal %idle 19時52分44秒 all 4.79 0.00 4.79 0.00 0.00 90.43 19時52分44秒 0 2.15 0.00 4.30 0.00 0.00 93.55 19時52分44秒 1 8.33 0.00 6.25 0.00 0.00 85.42
%user
と%nice
の合算値がユーザモードでの実行割合、%system
がカーネルモードでの実行割合です。
straceコマンド
[root@master vagrant]# strace -T cat /etc/hosts execve("/bin/cat", ["cat", "/etc/hosts"], [/* 21 vars */]) = 0 <0.004788> brk(NULL) = 0x229c000 <0.000038> mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9da2558000 <0.000077> access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) <0.000035> open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 <0.000029> fstat(3, {st_mode=S_IFREG|0644, st_size=31575, ...}) = 0 <0.000024> mmap(NULL, 31575, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f9da2550000 <0.000026> close(3) = 0 <0.000015> open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 <0.000039> read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20&\2\0\0\0\0\0"..., 832) = 832 <0.000023> fstat(3, {st_mode=S_IFREG|0755, st_size=2156160, ...}) = 0 <0.000020> mmap(NULL, 3985888, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f9da1f6a000 <0.000103> mprotect(0x7f9da212d000, 2097152, PROT_NONE) = 0 <0.000141> mmap(0x7f9da232d000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c3000) = 0x7f9da232d000 <0.000040> mmap(0x7f9da2333000, 16864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f9da2333000 <0.000023> close(3) = 0 <0.000077> mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9da254f000 <0.000019> mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9da254d000 <0.000018> arch_prctl(ARCH_SET_FS, 0x7f9da254d740) = 0 <0.000016> mprotect(0x7f9da232d000, 16384, PROT_READ) = 0 <0.000024> mprotect(0x60b000, 4096, PROT_READ) = 0 <0.000031> mprotect(0x7f9da2559000, 4096, PROT_READ) = 0 <0.000025> munmap(0x7f9da2550000, 31575) = 0 <0.000031> brk(NULL) = 0x229c000 <0.000018> brk(0x22bd000) = 0x22bd000 <0.000018> brk(NULL) = 0x22bd000 <0.000015> open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 <0.000530> fstat(3, {st_mode=S_IFREG|0644, st_size=106075056, ...}) = 0 <0.000019> mmap(NULL, 106075056, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f9d9ba40000 <0.000026> close(3) = 0 <0.000018> fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 <0.000017> open("/etc/hosts", O_RDONLY) = 3 <0.000088> fstat(3, {st_mode=S_IFREG|0644, st_size=221, ...}) = 0 <0.000019> fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0 <0.000021> read(3, "127.0.0.1\tmaster\tmaster\n127.0.0."..., 65536) = 221 <0.000026> write(1, "127.0.0.1\tmaster\tmaster\n127.0.0."..., 221127.0.0.1 master master 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 192.168.10.2 master 192.168.10.3 node1 ) = 221 <0.000119> read(3, "", 65536) = 0 <0.000121> close(3) = 0 <0.000023> close(1) = 0 <0.000025> close(2) = 0 <0.000020> exit_group(0) = ? +++ exited with 0 +++ [root@master vagrant]#
read(3, "127.0.0.1\tmaster\tmaster\n127.0.0."..., 65536) = 221 <0.000026>
とある通りこの行のread
に0.000026秒かかっています。
システムコールのラッパー関数
システムコールを呼び出せるのはアセンブラと呼ばれる低級言語のみです。しかしそれではアセンブラを使えないユーザに対してハードルが高すぎるため、システムコールをラップした関数がOSによって提供されます。C言語の場合はglibcライブラリが提供されています。
まとめ
この章ではユーザモードとカーネルモードについて整理しました。書籍にはより詳しい情報や、基礎的なため今回は端折った内容も豊富に掲載されていますので勉強の際はぜひお買い求めください。