FreeBSD kernel SOCKET I/F 探検 (12)

socket() システムコール (10)

必要とする知識など:
  • 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() およびそこから呼び出される関数で確保した領域、変更した変数と値を以下に示します。ここまでのデータ構造を以下に示します。

今回の処理で確保した領域を緑、変更した変数を青、変更した値は赤で示しています。

記事執筆者: asou
記事公開日:2014年07月09日