- 必要とする知識など:
- UNIX (含む Linux) 上での C 言語と SOCKET によるプログラミング経験および TCP/IP に関する知識。
socket() システムコール、十回目です。sys_socket() -> socreate() -> udp_attach() -> in_pcballoc() と呼び出されていて、in_pcballc() から udp_attach() に戻ったところです。
inp = sotoinpcb(so);
sotoinpcb() は、/usr/src/sys/netinet/in_pcb.h (http://svnweb.freebsd.org/base/release/9.1.0/sys/netinet/in_pcb.h?view=markup) で以下のように定義されています。
#define sotoinpcb(so) ((struct inpcb *)(so)->so_pcb)
so_pcb は、/usr/src/sys/sys/socketvar.h (http://svnweb.freebsd.org/base/release/9.1.0/sys/sys/socketvar.h?view=markup) で以下のように定義されています。
struct socket {
...
void *so_pcb; /* protocol control block */
struct socket のメンバ変数 so_pcb は void * と定義されているので、以下の処理は、
inp = sotoinpcb(so);
以下のように so->so_pcb を struct inpcb * にキャストしています。
inp = ((struct inpcb *)(so)->so_pcb)
次の処理に進みます。
inp->inp_vflag |= INP_IPV4;
inp->inp_ip_ttl = V_ip_defttl;
INP_IPV4 は、/usr/src/sys/netinet/in_pcb.h (http://svnweb.freebsd.org/base/release/9.1.0/sys/netinet/in_pcb.h?view=markup) で以下のように定義されています。
/*
* Flags for inp_vflags -- historically version flags only
*/
#define INP_IPV4 0x1
#define INP_IPV6 0x2
#define INP_IPV6PROTO 0x4 /* opened under IPv6 protocol */
V_ip_defttl は、/usr/src/sys/netinet/ip_var.h (http://svnweb.freebsd.org/base/release/9.1.0/sys/netinet/ip_var.h?view=markup) で以下のように定義されています。
#define V_ip_defttl VNET(ip_defttl)
VNET() は、/usr/src/sys/net/vnet.h (http://svnweb.freebsd.org/base/release/9.1.0/sys/net/vnet.h?view=markup) で定義されています。VNET() の詳細は割愛しますが、
inp->inp_ip_ttl = V_ip_defttl;
は以下のように展開されます。
inp->inp_ip_ttl = (ip_defttl);
ip_defttl は、/usr/src/sys/netinet/raw_ip.c (http://svnweb.freebsd.org/base/release/9.1.0/sys/netinet/raw_ip.c?view=markup) で以下のように定義されています。
VNET_DEFINE(int, ip_defttl) = IPDEFTTL;
VNET_DEFINE() は、/usr/src/sys/net/vnet.h (http://svnweb.freebsd.org/base/release/9.1.0/sys/net/vnet.h?view=markup) で定義されていますが、詳細は割愛します。
IPDEFTTL は、/usr/src/sys/netinet/ip.h (http://svnweb.freebsd.org/base/release/9.1.0/sys/netinet/ip.h?view=markup) で以下のように定義されています。
#define IPDEFTTL 64 /* default ttl, from RFC 1340 */
結局、以下の定義は、
VNET_DEFINE(int, ip_defttl) = IPDEFTTL;
以下のように展開されます。
int ip_defttl = 64;
次の処理です。
error = udp_newudpcb(inp);
if (error) {
in_pcbdetach(inp);
in_pcbfree(inp);
INP_INFO_WUNLOCK(&V_udbinfo);
return (error);
}
udp_newudpcb() は、udp_attach と同じ /usr/src/sys/netinet/udp_usrreq.c (http://svnweb.freebsd.org/base/release/9.1.0/sys/netinet/udp_usrreq.c?view=markup) で以下のように定義されています。
int
udp_newudpcb(struct inpcb *inp)
{
struct udpcb *up;
up = uma_zalloc(V_udpcb_zone, M_NOWAIT | M_ZERO);
if (up == NULL)
return (ENOBUFS);
inp->inp_ppcb = up;
return (0);
}
詳細は割愛しますが、struct udpcb 用の領域を確保しそのポインタを inp->inp_ppcb に格納しています。
udp_attach() の最後の処理です。
INP_WUNLOCK(inp);
INP_INFO_WUNLOCK(&V_udbinfo);
return (0);
INP_WUNLOCK(inp) は、udp_attach() から呼び出した in_pcballc() で実行した INP_WLOCK(inp) に対するロックの解除です。INP_INFO_WUNLOCK(&V_udbinfo) は、udp_attach() の最初の方で実行した INP_INFO_WLOCK(&V_udbinfo) に対するロックの解除です。
最後に 0 を返却して udp_attach() を終了し、socreate() に戻ります。udp_addach() から戻った socreate() の処理を見ていきます。以下は、udp_attach() を呼び出す直前からを掲載しています。
CURVNET_SET(so->so_vnet);
error = (*prp->pr_usrreqs->pru_attach)(so, proto, td);
CURVNET_RESTORE();
if (error) {
KASSERT(so->so_count == 1, ("socreate: so_count %d",
so->so_count));
so->so_count = 0;
sodealloc(so);
return (error);
}
*aso = so;
return (0);
CURVNET_SET()/CURVNET_RESTORE() は、本連載の 九回目 で説明しているので、詳細は割愛します。
*aso = so;
return (0);
socreate() の第二引数 struct socket **aso に socreate() で確保した struct socket の領域のポインタを代入し、0 を返却して sys_socket() に戻ります。
ここまでのデータ構造を以下に示します。
今回の処理で確保した領域を緑、変更した変数を青、変更した値は赤で示しています。参照している値も赤線で示してあります。
socreate() から戻ったところの処理を、socreate() も含めて掲載します。
/* An extra reference on `fp' has been held for us by falloc(). */
error = socreate(uap->domain, &so, uap->type, uap->protocol,
td->td_ucred, td);
if (error) {
fdclose(fdp, fp, fd, td);
} else {
finit(fp, FREAD | FWRITE, DTYPE_SOCKET, so, &socketops);
td->td_retval[0] = fd;
}
fdrop(fp, td);
return (error);
socreate() から戻ると finit() を実行します。finit() の第四引数として渡している socketops は、/usr/src/sys/kern/sys_socket.c (http://svnweb.freebsd.org/base/release/9.1.0/sys/kern/sys_socket.c?view=markup) で定義されており、以下のように定義されています。
struct fileops socketops = {
.fo_read = soo_read,
.fo_write = soo_write,
.fo_truncate = soo_truncate,
.fo_ioctl = soo_ioctl,
.fo_poll = soo_poll,
.fo_kqfilter = soo_kqfilter,
.fo_stat = soo_stat,
.fo_close = soo_close,
.fo_chmod = invfo_chmod,
.fo_chown = invfo_chown,
.fo_flags = DFLAG_PASSABLE
};
socketops は、soo_read(), soo_write() などの関数へのポインタを保持しています。
finit() は、/usr/src/sys/kern/kern_descrip.c (http://svnweb.freebsd.org/base/release/9.1.0/sys/kern/kern_descrip.c?view=markup) で以下のように定義されています。
void
finit(struct file *fp, u_int flag, short type, void *data, struct fileops *ops)
{
fp->f_data = data;
fp->f_flag = flag;
fp->f_type = type;
atomic_store_rel_ptr((volatile uintptr_t *)&fp->f_ops, (uintptr_t)ops);
}
最初の三行までは単なる代入なので詳細は割愛します。次の atomic_store_rel_prt() は、/usr/src/sys/*/アーキテクチャー/include/atomic.h で定義されています。i386 であれば /usr/src/sys/i386/include/atomic.h (http://svnweb.freebsd.org/base/release/9.1.0/sys/i386/include/atomic.h?view=markup) です。
本連載の 四回目 で atomic_add_int() の解説を行なっています。atomic_add_int() は指定した変数を加算する際に他のプロセス (CPU) との間でその変数への競合アクセスを避けるものですが、atomic_store_rel_ptr() は同様に変数への代入の競合を回避しているものと推測します。
finit() の次の処理です。
td->td_retval[0] = fd;
これは SOCKET(2) システムコールの戻り値 (int) を struct thread の td_retval[0] に格納しています。最後に error (ここでは 0) を返却して sys_socket() を終了します。
ここまでのデータ構造を以下に示します。
今回の処理で変更した変数を青、変更した値は赤で示しています。
sys_socket() の解析が終了したので、sys_socket() およびそこから呼び出される関数で確保した領域、変更した変数と値を以下に示します。ここまでのデータ構造を以下に示します。
今回の処理で確保した領域を緑、変更した変数を青、変更した値は赤で示しています。