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は存在はするけど、カーネル内部では何の働きもしていないっぽい。
なんかこういう存在はしているけど何の意味もないのって、他にも有りそうだね。