コース目次 / 第2章
write()という、依頼の中身を読む
write(fd, buf, count)の3つの引数と返り値の意味を読み解きます。システムコールが引数と返り値を持つ明確な契約であることを理解し、strace -cで呼び出し回数の統計を見ます。
前章の出力を、もう一度見ます。
write(1, "hello, syscall\n", 15hello, syscall
) = 15
3つの引数と、1つの返り値
write(fd, buf, count)——
第1引数(fd=1) — ファイルディスクリプタ。1は標準出力(画面)を指す、決まった番号です。
第2引数(buf) — 書き込みたいデータの先頭アドレス。straceは中身を“hello, syscall\n”として表示してくれています。
第3引数(count=15) — 書き込みたいバイト数。sizeof(msg) - 1で、末尾のヌル文字を除いた15バイトを指定しました。
返り値(=15) — 実際に書き込めたバイト数です。
「頼んだ数」と「実際に書けた数」が別々にあるのがポイントです。ネットワーク越しの書き込みなどでは、頼んだバイト数より少ない数しか書き込めないことがあります。C言語でwrite()を直接使うコードは、本来この返り値を確認し、書き残しがあれば再度書き込むべきです(今回の最小限のコードは確認していません——教材としての単純化です)。
システムコールは、明確な契約
「画面に文字を出す」という一見あいまいな動作が、write(ファイルディスクリプタ, バッファのアドレス, バイト数)という厳密に定義された引数と返り値を持つ契約に分解されている——これがシステムコールの正体です。同じ番号のシステムコールは、Linuxが動くどんなCPU(x86_64・ARMなど)でも同じ意味を持ちます。sec-asmで見たsyscall命令は、まさにこの契約を呼び出すためのCPU命令でした。
呼び出し回数を、統計で見る
strace -c ./hello
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
0.00 0.000000 0 1 read
0.00 0.000000 0 1 write
0.00 0.000000 0 2 close
0.00 0.000000 0 2 fstat
0.00 0.000000 0 8 mmap
0.00 0.000000 0 3 mprotect
0.00 0.000000 0 1 munmap
0.00 0.000000 0 1 brk
0.00 0.000000 0 2 pread64
0.00 0.000000 0 1 1 access
0.00 0.000000 0 1 execve
0.00 0.000000 0 1 arch_prctl
0.00 0.000000 0 1 set_tid_address
0.00 0.000000 0 2 openat
0.00 0.000000 0 1 set_robust_list
0.00 0.000000 0 1 prlimit64
0.00 0.000000 0 1 rseq
------ ----------- ----------- --------- --------- ----------------
100.00 0.000000 0 30 1 total
-cは、個々の呼び出しの代わりに種類ごとの回数と所要時間の集計を見せます。実務で「このプログラムは何をたくさんやっているか」をざっくり掴みたいときに便利です。writeの行のcalls列が1——前の章で確認した「1回だけ呼ばれた」という事実が、ここでも数字として裏付けられています。mmapが8回と多いのも、動的リンカがlibcを読み込む過程で何度もメモリ領域を割り当てているためです。
持ち帰る一言
システムコールは、引数と返り値を持つ明確な契約。 write(fd, buf, count) = 実際に書けたバイト数——この形を知っていれば、他のシステムコール(read・openなど)も同じ枠組みで読めます。次でこのコースをまとめます。
こうなっていればOK
卒業まであと1章です。
この章はまだ完了していません。
保存できませんでした(プライベートブラウズ中かもしれません)。この端末に進捗は残りませんが、先へは進めます。