フラミナル

考え方や調べたことを書き殴ります。IT技術系記事多め

【Linuxのしくみ】2章 ユーザモードで実現する機能 を読んで自分なりにまとめ

今回はこちらの書籍「[試して理解]Linuxのしくみ〜実験と図解で学ぶOSとハードウェアの基礎知識」 の2章 ユーザモードで実現する機能について、自己理解のために内容をかいつまんで要約します。素晴らしい書籍で理解が進むと思いますのでぜひご購入を検討ください。

プロセスとOSの関わりについて

f:id:lirlia:20200422043928p:plain

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...カーネルモードでの実行時間

usersysは実際に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)       20200421日  _x86_64_        (2 CPU)

195242秒     CPU     %user     %nice   %system   %iowait    %steal     %idle
195243秒     all      7.57      0.00      5.95      0.00      0.00     86.49
1952430      6.52      0.00      5.43      0.00      0.00     88.04
1952431      7.61      0.00      5.43      0.00      0.00     86.96

195243秒     CPU     %user     %nice   %system   %iowait    %steal     %idle
195244秒     all      4.79      0.00      4.79      0.00      0.00     90.43
1952440      2.15      0.00      4.30      0.00      0.00     93.55
1952441      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>とある通りこの行のread0.000026秒かかっています。

システムコールのラッパー関数

システムコールを呼び出せるのはアセンブラと呼ばれる低級言語のみです。しかしそれではアセンブラを使えないユーザに対してハードルが高すぎるため、システムコールをラップした関数がOSによって提供されます。C言語の場合はglibcライブラリが提供されています。

まとめ

この章ではユーザモードとカーネルモードについて整理しました。書籍にはより詳しい情報や、基礎的なため今回は端折った内容も豊富に掲載されていますので勉強の際はぜひお買い求めください。