KDOC 109: 発行システムコールを調べる

この文書のステータス

  • 作成
    • 2024-02-26 貴島
  • レビュー
    • 2024-03-24 貴島

概要

Linuxカーネルを理解するうえで重要なのは、システムコールを理解することだという。調べ方を書く。

調べ方

straceで発行しているシステムコールを調べられる。

strace ls 2>&1 | head -n 20
execve("/usr/bin/ls", ["ls"], 0x7fffd6410850 /* 80 vars */) = 0
brk(NULL)                               = 0x55c9d95e7000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffcbdf07c90) = -1 EINVAL (Invalid argument)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f39db13e000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=86381, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 86381, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f39db128000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=166280, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 177672, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f39db0fc000
mprotect(0x7f39db102000, 139264, PROT_NONE) = 0
mmap(0x7f39db102000, 106496, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6000) = 0x7f39db102000
mmap(0x7f39db11c000, 28672, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x20000) = 0x7f39db11c000
mmap(0x7f39db124000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x27000) = 0x7f39db124000
mmap(0x7f39db126000, 5640, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f39db126000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3

lsコマンドをエラーにしてみよう。statxシステムコールでファイルがないのを検知して、そのエラーを書き出しているのかな。-1が正常時より多く出ているように見えるが、どれなのかはわからない。

strace ls INVALID 2>&1 | tail -n 50
mprotect(0x7f39d1d5e000, 4096, PROT_READ) = 0
mprotect(0x7f39d1fb0000, 4096, PROT_READ) = 0
mprotect(0x56391fc1c000, 4096, PROT_READ) = 0
mprotect(0x7f39d2004000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=9788*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x7f39d1fb4000, 86381)           = 0
statfs("/sys/fs/selinux", 0x7ffd42c5d4a0) = -1 ENOENT (No such file or directory)
statfs("/selinux", 0x7ffd42c5d4a0)      = -1 ENOENT (No such file or directory)
getrandom("\xc2\x38\x2d\xe3\x83\xfd\x85\x73", 8, GRND_NONBLOCK) = 8
brk(NULL)                               = 0x563920bb5000
brk(0x563920bd6000)                     = 0x563920bd6000
openat(AT_FDCWD, "/proc/filesystems", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0444, st_size=0, ...}, AT_EMPTY_PATH) = 0
read(3, "nodev\tsysfs\nnodev\ttmpfs\nnodev\tbd"..., 1024) = 424
read(3, "", 1024)                       = 0
close(3)                                = 0
access("/etc/selinux/config", F_OK)     = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=6784496, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 6784496, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f39d164d000
close(3)                                = 0
ioctl(1, TCGETS, 0x7ffd42c5d3d0)        = -1 ENOTTY (Inappropriate ioctl for device)
ioctl(1, TIOCGWINSZ, 0x7ffd42c5d4c0)    = -1 ENOTTY (Inappropriate ioctl for device)
statx(AT_FDCWD, "INVALID", AT_STATX_SYNC_AS_STAT, STATX_MODE, 0x7ffd42c5d030) = -1 ENOENT (No such file or directory)
statx(AT_FDCWD, "INVALID", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_MODE, 0x7ffd42c5d030) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=2996, ...}, AT_EMPTY_PATH) = 0
read(3, "# Locale name alias data base.\n#"..., 4096) = 2996
read(3, "", 4096)                       = 0
close(3)                                = 0
openat(AT_FDCWD, "/usr/share/locale/en_US/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale-langpack/en_US/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale-langpack/en/LC_MESSAGES/coreutils.mo", O_RDONLY) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=613, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 613, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f39d2003000
close(3)                                = 0
write(2, "ls: ", 4ls: )                     = 4
write(2, "cannot access 'INVALID'", 23cannot access 'INVALID') = 23
openat(AT_FDCWD, "/usr/share/locale/en_US/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale-langpack/en_US/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/locale-langpack/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
write(2, ": No such file or directory", 27: No such file or directory) = 27
write(2, "\n", 1
)                       = 1
close(1)                                = 0
close(2)                                = 0
exit_group(2)                           = ?
+++ exited with 2 +++

↓dateを見てみる。 /etc/localtime でタイムゾーンを確認して計算しているように見える。時間を取得するシステムコール、などは使ってないようだ。TZifはTimeZone informationの略のようだ。

strace date 2>&1 | tail -n 30
arch_prctl(ARCH_SET_FS, 0x7fd050a58740) = 0
set_tid_address(0x7fd050a58a10)         = 1395333
set_robust_list(0x7fd050a58a20, 24)     = 0
rseq(0x7fd050a590e0, 0x20, 0, 0x53053053) = 0
mprotect(0x7fd050c70000, 16384, PROT_READ) = 0
mprotect(0x555785cf4000, 4096, PROT_READ) = 0
mprotect(0x7fd050cd3000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=9788*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x7fd050c83000, 86381)           = 0
getrandom("\xc9\x4e\x10\xfc\x21\x5d\x36\xed", 8, GRND_NONBLOCK) = 8
brk(NULL)                               = 0x55578778a000
brk(0x5557877ab000)                     = 0x5557877ab000
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=6784496, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 6784496, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fd0503df000
close(3)                                = 0
openat(AT_FDCWD, "/etc/localtime", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=309, ...}, AT_EMPTY_PATH) = 0
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=309, ...}, AT_EMPTY_PATH) = 0
read(3, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\4\0\0\0\0"..., 4096) = 309
lseek(3, -176, SEEK_CUR)                = 133
read(3, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\4\0\0\0\0"..., 4096) = 176
close(3)                                = 0
newfstatat(1, "", {st_mode=S_IFIFO|0600, st_size=0, ...}, AT_EMPTY_PATH) = 0
write(1, "Mon Feb 26 08:52:26 PM JST 2024\n", 32Mon Feb 26 08:52:26 PM JST 2024
) = 32
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

↓unameで使ってみる。システムコールunameに構造体らしきものをわたしているのが見える。最終的にファイルディスクリプタ1にwriteしているのを確認できる。

strace uname -i 2>&1 | tail -n 30
mmap(0x7f572fd1a000, 1658880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x7f572fd1a000
mmap(0x7f572feaf000, 360448, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7f572feaf000
mmap(0x7f572ff07000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x214000) = 0x7f572ff07000
mmap(0x7f572ff0d000, 52816, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f572ff0d000
close(3)                                = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f572fcef000
arch_prctl(ARCH_SET_FS, 0x7f572fcef740) = 0
set_tid_address(0x7f572fcefa10)         = 1379097
set_robust_list(0x7f572fcefa20, 24)     = 0
rseq(0x7f572fcf00e0, 0x20, 0, 0x53053053) = 0
mprotect(0x7f572ff07000, 16384, PROT_READ) = 0
mprotect(0x5616b783b000, 4096, PROT_READ) = 0
mprotect(0x7f572ff6a000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=9788*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x7f572ff1a000, 86381)           = 0
getrandom("\xee\x05\xb9\xe2\x1b\x9e\xa4\x02", 8, GRND_NONBLOCK) = 8
brk(NULL)                               = 0x5616b7fef000
brk(0x5616b8010000)                     = 0x5616b8010000
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=6784496, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 6784496, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f572f676000
close(3)                                = 0
uname({sysname="Linux", nodename="orange-ThinkPad-X1-Carbon-Gen-10", ...}) = 0
newfstatat(1, "", {st_mode=S_IFIFO|0600, st_size=0, ...}, AT_EMPTY_PATH) = 0
write(1, "x86_64\n", 7x86_64
)                 = 7
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

↓manで使ってみる。単にヘルプドキュメントを表示するだけでも、裏側ではたくさんやっていることがわかる。

strace man 2>&1 | head -n 20
execve("/usr/bin/man", ["man"], 0x7ffd32ad3890 /* 80 vars */) = 0
brk(NULL)                               = 0x562d715d0000
arch_prctl(0x3001 /* ARCH_??? */, 0x7fff0d6e1530) = -1 EINVAL (Invalid argument)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f70b679a000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/man-db/glibc-hwcaps/x86-64-v3/libmandb-2.10.2.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/man-db/glibc-hwcaps/x86-64-v3", 0x7fff0d6e0750, 0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/man-db/glibc-hwcaps/x86-64-v2/libmandb-2.10.2.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/man-db/glibc-hwcaps/x86-64-v2", 0x7fff0d6e0750, 0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/man-db/tls/haswell/x86_64/libmandb-2.10.2.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/man-db/tls/haswell/x86_64", 0x7fff0d6e0750, 0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/man-db/tls/haswell/libmandb-2.10.2.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/man-db/tls/haswell", 0x7fff0d6e0750, 0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/man-db/tls/x86_64/libmandb-2.10.2.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/man-db/tls/x86_64", 0x7fff0d6e0750, 0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/man-db/tls/libmandb-2.10.2.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/man-db/tls", 0x7fff0d6e0750, 0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/man-db/haswell/x86_64/libmandb-2.10.2.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/usr/lib/man-db/haswell/x86_64", 0x7fff0d6e0750, 0) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/man-db/haswell/libmandb-2.10.2.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)

関連