read()とfread()が呼ばれてからの流れ

ひょんなことからread()とfread()が呼ばれてからシステムコールにたどり着くまでを追ってみたので、メモがてらここに書いておきます。


FreeBSD 6.3-STABLE のソースをざっと追ってみたところ、
fread() からの関数コールトレース(下記参照)では__sys_read() を呼び出していました。


lib/libc/stdio/fread.c

fread() {
   :
 ret = __fread(buf, size, count, fp);
   :
}

__fread() {
   :
  __srefill(fp)
   :
}


lib/libc/stdio/refill.c

int __srefill() {
   :
 fp->_r = _sread(fp, (char *)fp->_p, fp->_bf._size);
   :
}


lib/libc/stdio/stdio.c

_sread(fp, buf, n) {
   :
 ret = (*fp->_read)(fp->_cookie, buf, n);
   :
}


lib/libc_r/uthread/uthread_read.c

_read(int fd, void *buf, size_t nbytes) {
   :
 while ((ret = __sys_read(fd, buf, nbytes)) < 0) {
   :
}


一方、read()の入り口が見つからなかったのですが、read() が入っているであろう libc.a を
objdump -t してシンボルを見たところ、read というシンボルは read.S というファイルに存在しているようです。


/usr/obj/usr/src/lib/libc/read.S
コンパイル時に動的に作成されるようで探し難い…)

#include "SYS.h"
RSYSCALL(read)


/usr/src/lib/libc/i386/SYS.h を見ると、

#define SYSCALL(x)  2: PIC_PROLOGUE; jmp PIC_PLT(HIDENAME(cerror)); \
     ENTRY(__CONCAT(__sys_,x));      \
     .weak CNAME(x);         \
     .set CNAME(x),CNAME(__CONCAT(__sys_,x));  \
     .weak CNAME(__CONCAT(_,x));     \
     .set CNAME(__CONCAT(_,x)),CNAME(__CONCAT(__sys_,x)); \
     mov __CONCAT($SYS_,x),%eax; KERNCALL; jb 2b

#define RSYSCALL(x) SYSCALL(x); ret

#define KERNCALL  int $0x80

とマクロの嵐ですが、何となく __sys_read &read というシンボルが呼ばれると、int 0x80 ソフトウェア割り込みでシステムコールが呼び出されているような気がします。


そうすると、以下のようになっているのかなぁ。と。


fread() の場合: _read() -> __sys_read() 経由でシステムコール呼び出し

read() の場合: 直接システムコール呼び出し