はじめに
せっかくゴールデンウィークに入ったので、1 年間で学んできたことを復習する意味でも、最近更新が滞っているこのブログに残しておこうと思いました。
基本的には Linux 周りのことを書くと思います。アドカレみたいなのりです。
システムコール
システムコールというのは、各種プロセスが、別のプロセスを生成したり、ハードウェアを操作したりする際に、カーネル側に処理を依頼する方法となる。
具体的には以下のような操作を実施する場合にはシステムコール経由で行う必要がある。
- プロセス生成/削除
- メモリ確保/解放
- プロセス間通信
- ネットワーク周辺操作
- ファイルシステム操作
- ファイル操作 (デバイスアクセス)
システムコールが発布される際は CPU がユーザーモードからカーネルモード (Ring 3 -> Ring 0) に遷移し、要求の正当性を判断した上で、システムコールを受諾する。
実験
Hello World
まずは、簡単な Hello World コードを書き、strace で結果を見てみる。
#include <stdio.h>
int main(void) {
puts("Hello World!");
return 0;
}
$ gcc -o hello hello.c
$ strace -o hello.log ./hello
Hello World!
$ cat hello.log
execve("./hello", ["./hello"], [/* 33 vars */]) = 0
brk(0) = 0x2360000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9313b2b000
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=20510, ...}) = 0
mmap(NULL, 20510, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f9313b25000
close(3) = 0
open("/lib64/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\0\20\35\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2122040, ...}) = 0
mmap(NULL, 3944896, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f9313547000
mprotect(0x7f9313701000, 2093056, PROT_NONE) = 0
mmap(0x7f9313900000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b9000) = 0x7f9313900000
mmap(0x7f9313906000, 16832, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f9313906000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9313b24000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9313b22000
arch_prctl(ARCH_SET_FS, 0x7f9313b22740) = 0
mprotect(0x7f9313900000, 16384, PROT_READ) = 0
mprotect(0x7f9313b2c000, 4096, PROT_READ) = 0
munmap(0x7f9313b25000, 20510) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9313b2a000
write(1, "Hello World!\n", 13) = 13
exit_group(0) = ?
+++ exited with 0 +++
色々システムコールが呼ばれているが、重要なのは write() システムコールがちゃんと呼ばれているところ。
他にも mmap() でメモリを確保したり、一番最初に execve() で bash から fork したこのプロセスを exec したりしてる。
システムコールなし
システムコールを発行しないような、ただただ無限にループするプロセスを作成する。
#include <stdio.h>
int main(void) {
for(;;)
;
}
$ gcc -o loop loop.c
$ ./loop &
[1] 3384
$ sar -P ALL 1 1
Linux 4.14.26-46.32.amzn1.x86_64 (ip-10-1-11-92) 05/02/2018 _x86_64_ (8 CPU)
10:53:56 PM CPU %user %nice %system %iowait %steal %idle
10:53:57 PM all 12.50 0.00 0.12 0.00 0.00 87.38
10:53:57 PM 0 0.00 0.00 0.00 0.00 0.00 100.00
10:53:57 PM 1 0.00 0.00 0.00 0.00 0.00 100.00
10:53:57 PM 2 0.00 0.00 0.00 0.00 0.00 100.00
10:53:57 PM 3 0.00 0.00 0.00 0.00 0.00 100.00
10:53:57 PM 4 100.00 0.00 0.00 0.00 0.00 0.00
10:53:57 PM 5 0.00 0.00 0.00 0.00 0.00 100.00
10:53:57 PM 6 0.00 0.00 0.00 0.00 0.00 100.00
10:53:57 PM 7 0.00 0.00 0.00 0.00 0.00 100.00
CPU4 の user 値だけが 100% になっていることを確認できる。