トップ «前の日記(2015-04-16) 最新 次の日記(2015-04-30)» 編集

はじめてのにき

ここの位置付け

2004|11|
2005|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|03|04|05|06|07|08|09|10|11|

ToDo:


2015-04-29

_ syscall と syscall wrapper

なんか安くなってるとのことで、遅らばせながらアセンブラ短歌本を読んでみた。なんか自分でも考えてみたくなる。

で、中の余談の一節のしょうもないことが気になった。

なお正式 には exit() は _exit() を呼び出すためのライブラリ関数であり,
_exit() が 本来 のシステムコールの API だ . このため 「exit システムコール」
ではなく 「_exit システムコール」 と表記すべきで , man のカテゴリは
exit(3) と_exit(2) になっている . まあでもあまり気にせずに , ここでは
「exit システムコール」と呼んでしまっている .

これはなんか全体的におかしいような気がする。

int 0x80 で呼ぶ文脈なんだから、 linux システムコールというレイヤで見るべきというのは良いと思う。間違ってると思うのは

  • _exit というシステムコールは linux に無い(と言って良いと思う)
  • exit(3) も _exit(2) も exit システムコールを呼ばない

あたり。いろいろ書いてみる。

まず、システムコールまわりには3つのレイヤがあると考えて良いと思う。

  • linux kernel のシステムコール
  • glibc 内の syscall wrapper
  • glibc 内の libc 関数

最初の linux kernel のシステムコール

これは歴史的な事情とかで名前とか引数が微妙にヘンだったりする。これらの正式な名前は、 linux kernel のコードを SYSCALL_DEFINE で git grep するか、 /usr/include/asm/unistd_{32,64}.h あたりを見るのが良いと思う。

unistd_32.h を見れば、まぁ __NR__exit なんてものは無いし、 kernel 内でも SYSCALL_DEFINE1(exit, int, error_code) などとなってる。まぁでも _llseek とかは kernel 内ではアンダースコア落ちてるな。。

このレイヤにあるのは exit と exit_group 。 exit はスレッド終了であり、 exit_group がプロセス終了をする。まあ短歌の世界ではどっちでも同じだろう。

次の glibc 内の syscall wrapper

基本的に、このレイヤが glibc の man の 2 番目のセクションに載っている、と考えるのが良いと思う。 gettid や futex なんかの、 glibc が提供してないシンボルも man 2 で出るんだけど、やつらはちょっとした親切で載ってるだけという感じで良いと思う。また、 syscall wrapper と syscall の ABI がだいぶ違う場合なんかは、親切で linux 側の方も紹介してくれる時もある。例えば clone(2)

がまぁ、基本的に man 2 が出すのは syscall wrapper 。 x86 には send なんて linux システムコールは無いし (c.f., man 2 socketcall) 、実際には pread64 が呼ばれるはずだけど pread しか書いてない。などなど。

man 2 にはエラー時には errno がセットされるとか書いてあるけど、これなんかも libc がやってること。 errno なんて kernel の世界には無くて、 -4095 から -1 までの数字が返ればそれを errno にセットする、てのを libc がやってる。

このレイヤの仕事は libc の ABI を kernel の ABI に変換する、ってこと。おかげさまで syscall(__NR_XXX, ...) とかしないでも割と簡単にシステムコールを叩くプログラムが書ける。

普通あまりたいした仕事しないけど、 sigaction とか fork とかは割と仕事ありますね。 mmap の第6引数がアラインされてない時 EINVAL 返すのが glibc の仕事とか (c.f., man 2 mmap2)、 x86 含めて、 kernel の sigsuspend で第1,2引数使ってないアーキテクチャがあるとか言い出すとだいぶ libc マニヤ。

最後の glibc 内の libc 関数

これが man 3 で出てくるやつ。普通、単なる syscall wrapper と違って、もうちょっと libc チックなイロイロをする。 fopen(3) と open(2) とかを考えると良いと思う。僕的には exit(3) と _exit(2) は fopen と open くらい違う。 atexit 呼ぶとかをはじめとして、結構いろいろやるんで。たぶん、歴史的には exit(3) が単なる syscall wrapper じゃない感満載なので、 syscall wrapper 足す時に名前かぶらないように _exit としたんじゃないかな。 C99 では _Exit 。

exit(3) も _exit(2) も _Exit(2) も exit システムコールは呼ばない。呼ぶのは exit_group 。 exit システムコールに対応する syscall wrapper は…たぶん無いと思う。一番近いのが pthread_exit(3) と言えなくはないけど…くらい。

んなことどうでもいい

と思う。

ただこのへんのレイヤをきちんと分別しないと大変な珍しい世界でしばらく仕事してたので、どうでもいい知識がたくさん身についた。まあ int 0x80 とかする人は上記3レイヤくらいは把握しといてもいいんではないかな。

(22:38)

お名前:
E-mail:
コメント:
人生、宇宙、すべての答え
本日のリンク元

2015年
4月
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30

search / home / index

全てリンクフリーです。 コード片は自由に使用していただいて構いません。 その他のものはGPL扱いであればあらゆる使用に関して文句は言いません。 なにかあれば下記メールアドレスへ。

shinichiro.hamaji _at_ gmail.com / shinichiro.h