select()のソース
http://shinh.skr.jp/m/?date=20081008#p01
http://d.hatena.ne.jp/shinichiro_h/20081010#1223567210
をきっかけにちょっとkernel(2.6.22)内部を見てみた。
sys_select()の内部ではSO_SNDLOWATは参照していないっぽいね。
asmlinkage long sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, struct timeval __user *tvp) { //省略 ret = core_sys_select(n, inp, outp, exp, &timeout); //省略 }
sys_select()からcore_sys_select()を呼んでいる。
static int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp, fd_set __user *exp, s64 *timeout) { //省略 if ((ret = get_fd_set(n, inp, fds.in)) || (ret = get_fd_set(n, outp, fds.out)) || (ret = get_fd_set(n, exp, fds.ex))) goto out; //省略 }
実際にoutpに関係あるのは、上記のget_fd_set()。
これを追うと
static inline int get_fd_set(unsigned long nr, void __user *ufdset, unsigned long *fdset) { nr = FDS_BYTES(nr); if (ufdset) return copy_from_user(fdset, ufdset, nr) ? -EFAULT : 0; memset(fdset, 0, nr); return 0; }
で、
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n) { if (access_ok(VERIFY_READ, from, n)) n = __copy_from_user(to, from, n); else memset(to, 0, n); return n; }
最終的に、__range_ok()というマクロに行き着く。
/* * Test whether a block of memory is a valid user space address. * Returns 0 if the range is valid, nonzero otherwise. * * This is equivalent to the following test: * (u33)addr + (u33)size >= (u33)current->addr_limit.seg * * This needs 33-bit arithmetic. We have a carry... */ #define __range_ok(addr,size) ({ \ unsigned long flag,roksum; \ __chk_user_ptr(addr); \ asm("addl %3,%1 ; sbbl %0,%0; cmpl %1,%4; sbbl $0,%0" \ :"=&r" (flag), "=r" (roksum) \ :"1" (addr),"g" ((int)(size)),"rm" (current_thread_info()->addr_limit.seg)); \ flag; }) #define access_ok(type,addr,size) (likely(__range_ok(addr,size) == 0))
どうもこのマクロがselect()のfd_setに空きが有るかどうかを調べてるっぽい。
なんかアセンブラが出てるから意味が分からないけど。
http://www.linux.or.jp/JM/html/LDP_man-pages/man7/socket.7.htmlには
SO_RCVLOWAT と SO_SNDLOWAT バッファ中に溜めることのできるデータの最小値を指定する。このサイズを越えると、ソケット層はそのデータをプロトコルに渡し (SO_SNDLOWAT)、 受信時にはユーザに渡す (SO_RCVLOWAT)。 これら二つの値は 1 に初期化される。 SO_SNDLOWAT は Linux では変更できない (setsockopt(2) は ENOPROTOOPT エラーで失敗する)。 SO_RCVLOWAT は Linux 2.4 以降でのみ変更可能である。現状、Linux ではシステムコール select(2) と poll(2) は SO_RCVLOWAT の設定を考慮に入れずに動作し、データが1バイト利用可能になっただけでも、ソケットは読み出し可能とのマークをつける。一方、それに続けて行うソケットからの read は SO_RCVLOWAT バイトのデータが利用可能になるまで停止してしまう。
とか書いてる。
やっぱりSO_RCVLOWATは見ていない様子。
SO_RCVLOWATってなんの為に有るんだか。
grepしてみるとこんな感じ。
$ grep -r SO_RCVLOWAT . ./arch/sparc64/solaris/socket.c:#define SOL_SO_RCVLOWAT 0x1004 ./arch/sparc64/solaris/socket.c: case SOL_SO_RCVLOWAT: optname = SO_RCVLOWAT; break; ./include/asm-alpha/socket.h:#define SO_RCVLOWAT 0x1010 ./include/asm-arm/socket.h:#define SO_RCVLOWAT 18 ./include/asm-arm26/socket.h:#define SO_RCVLOWAT 18 ./include/asm-avr32/socket.h:#define SO_RCVLOWAT 18 ./include/asm-blackfin/socket.h:#define SO_RCVLOWAT 18 ./include/asm-cris/socket.h:#define SO_RCVLOWAT 18 ./include/asm-frv/socket.h:#define SO_RCVLOWAT 18 ./include/asm-h8300/socket.h:#define SO_RCVLOWAT 18 ./include/asm-i386/socket.h:#define SO_RCVLOWAT 18 ./include/asm-ia64/socket.h:#define SO_RCVLOWAT 18 ./include/asm-m32r/socket.h:#define SO_RCVLOWAT 18 ./include/asm-m68k/socket.h:#define SO_RCVLOWAT 18 ./include/asm-mips/socket.h:#define SO_RCVLOWAT 0x1004 /* receive low-water mark */ ./include/asm-parisc/socket.h:#define SO_RCVLOWAT 0x1004 ./include/asm-powerpc/socket.h:#define SO_RCVLOWAT 16 ./include/asm-s390/socket.h:#define SO_RCVLOWAT 18 ./include/asm-sh/socket.h:#define SO_RCVLOWAT 18 ./include/asm-sparc/socket.h:#define SO_RCVLOWAT 0x0800 ./include/asm-sparc64/socket.h:#define SO_RCVLOWAT 0x0800 ./include/asm-v850/socket.h:#define SO_RCVLOWAT 18 ./include/asm-x86_64/socket.h:#define SO_RCVLOWAT 18 ./include/asm-xtensa/socket.h:#define SO_RCVLOWAT 18 ./include/net/sock.h: * @sk_rcvlowat: %SO_RCVLOWAT setting ./net/core/sock.c: case SO_RCVLOWAT: ./net/core/sock.c: case SO_RCVLOWAT: ./tags:SO_RCVLOWAT include/asm-i386/socket.h 28;" dk
アーキテクチャによってdefine値が違うね。
とそんなのはどうでも良くて、関係ありそうなのはここ。
./net/core/sock.c: case SO_RCVLOWAT: ./net/core/sock.c: case SO_RCVLOWAT:
みてみるとsock_getsockopt()、sock_setsockopt()と言う関数。
多分getsockopt()とsetsockopt()のカーネル側の関数だと思う。
ということで、SO_RCVLOWATは存在はするけど、カーネル内部では何の働きもしていないっぽい。
なんかこういう存在はしているけど何の意味もないのって、他にも有りそうだね。