- 必要とする知識など:
- UNIX (含む Linux) 上での C 言語と SOCKET によるプログラミング経験および TCP/IP に関する知識。
socket() システムコール、六回目です。soalloc() から socreate() に戻ったところです。
TAILQ_INIT(&so->so_incomp);
TAILQ_INIT(&so->so_comp);
TAILQ_INIT() に渡している so_incomp と so_comp は、struct socekt のメンバ変数ですが、以下のように定義されています。
struct socket {
...
TAILQ_HEAD(, socket) so_incomp; /* (e) queue of partial unaccepted connections */
TAILQ_HEAD(, socket) so_comp; /* (e) queue of complete unaccepted connections */
コメントから察するところ、ユーザプログラムから ACCEPT(2) の呼び出しを実施されていない接続の待ちキューのようです。TCP などのように、呼を張る [1] インターフェースで使用するものだと思われます。partial/complete の違いは、カーネル内で接続処理が完了して いない/いる の違いではないかと思います。
TAILQ_HEAD() は、前回 (http://www.soum.co.jp/misc/asou/fbsd-kernel-socket/7.html) 解説済みなので詳細は割愛しますが、TAILQ_HEAD() を展開すると、上記の struct socket は以下のようになります。
struct socket {
...
struct {
struct socket *fqh_first;
struct socket **fqh_last;
} so_incomp;
struct {
struct socket *fqh_first;
struct socket **fqh_last;
} so_comp;
TAILQ_INIT() も、前回 (http://www.soum.co.jp/misc/asou/fbsd-kernel-socket/7.html) 解説済みなので詳細は割愛しますが、ここではキューを管理するポインタの初期化を行なっています。
次の処理は、so->so_type の初期化です。
so->so_type = type;
本関数 (socreate()) の第三引数 int type を so->so_type に代入しています。この type は、server.c から SOCKET(2) を呼び出した際の第二引数 SOCK_DGRAM です。
次は、crhold() の呼び出しです。
so->so_cred = crhold(cred);
crhold() は、本連載の第四回 (http://www.soum.co.jp/misc/asou/fbsd-kernel-socket/4.html) でも解説しているので詳細は割愛しますが、認証情報 (struct ucred *cred) が保持している参照カウンタ (cr_ref) に 1 を加算しています。なお、srhold() は引数で渡した cred を戻り値として返却するので、so->so_cred には cred が代入されます。
次の処理は、so->so_fibnum の初期化です。
if ((prp->pr_domain->dom_family == PF_INET) ||
(prp->pr_domain->dom_family == PF_INET6) ||
(prp->pr_domain->dom_family == PF_ROUTE))
so->so_fibnum = td->td_proc->p_fibnum;
else
so->so_fibnum = 0;
prp->pr_domain->dom_family の値は AF_INET (== PF_INET) なので、ここでは so->so_fibnum に td->td_proc->p_fibnum を代入します。
次の処理は、so->so_proto の初期化です。
so->so_proto = prp;
本関数 (socreate()) の始めのところで、pffindtype() で取得した struct protosw *prp を代入しています。
次の処理は、knlist_init_mtx() の呼び出しです。
knlist_init_mtx(&so->so_rcv.sb_sel.si_note, SOCKBUF_MTX(&so->so_rcv));
knlist_init_mtx(&so->so_snd.sb_sel.si_note, SOCKBUF_MTX(&so->so_snd));
knlist_init_mtx() は、/usr/src/sys/kern/kern_event.c (http://svnweb.freebsd.org/base/release/9.1.0/sys/kern/kern_event.c?view=markup) で以下のように定義されています。
void
knlist_init_mtx(struct knlist *knl, struct mtx *lock)
{
knlist_init(knl, lock, NULL, NULL, NULL, NULL);
}
ここで呼び出している knlist_init() は、同じ /usr/src/sys/kern/kern_event.c で以下のように定義されています。
void
knlist_init(struct knlist *knl, void *lock, void (*kl_lock)(void *),
void (*kl_unlock)(void *),
void (*kl_assert_locked)(void *), void (*kl_assert_unlocked)(void *))
{
if (lock == NULL)
knl->kl_lockarg = &knlist_lock;
else
knl->kl_lockarg = lock;
...
...
SLIST_INIT(&knl->kl_list);
}
ロックに関する初期化を行なっているようです。kern_event.c というファイル名から、イベントに関する処理を行なっているようですが、knlist_init_mtx() に送受信バッファ (so->snd/so->so_rcv) 内の変数のポインタを渡しているので、NIC (Network Interface Controler) からの送受信割り込みに関するものではないかと推測しています。knlist_init() の詳細は割愛しますが、knlist_init() は第一引数である struct klist の各メンバ変数の初期化を行なっています。
knlist_init_mtx() の呼び出し時に参照している so_rcv/so_snd は、struct socket (/usr/src/sys/sys/socketvar.h (http://svnweb.freebsd.org/base/release/9.1.0/sys/sys/socketvar.h?view=markup) で以下のように定義されています。
struct socket {
...
struct sockbuf so_rcv, so_snd;
struct sockbuf は、/usr/src/sys/sys/sockbuf.h (http://svnweb.freebsd.org/base/release/9.1.0/sys/sys/sockbuf.h?view=markup) で定義されています。
knlist_init_mtx() の呼び出し時に参照している sb_sel は、同じく /usr/src/sys/sys/sockbuf.h で以下のように定義されています。
struct sockbuf {
struct selinfo sb_sel; /* process selecting read/write */
struct selinfo は、/usr/src/sys/sys/selinfo.h (http://svnweb.freebsd.org/base/release/9.1.0/sys/sys/selinfo.h?view=markup) で定義されています。
knlist_init_mtx() の呼び出し時に参照している si_note は、同じく /usr/src/sys/sys/selinfo.h で以下のように定義されています。
struct selinfo {
...
struct knlist si_note; /* kernel note list */
struct knlist は、/usr/src/sys/sys/event.h (http://svnweb.freebsd.org/base/release/9.1.0/sys/sys/event.h?view=markup) で定義されています。
knlist_init_mtx() の呼び出し時に使用している SOCKBUF_MTX は、/usr/src/sys/sys/sockbuf.h (http://svnweb.freebsd.org/base/release/9.1.0/sys/sys/sockbuf.h?view=markup) で以下のように定義されています。
#define SOCKBUF_MTX(_sb) (&(_sb)->sb_mtx)
knlist_init() の最後で使用している SLIST_INIT() は、/usr/src/sys/sys/queue.h (http://svnweb.freebsd.org/base/release/9.1.0/sys/sys/queue.h?view=markup) で以下のように定義されています。
#define SLIST_INIT(head) do { \
SLIST_FIRST((head)) = NULL; \
} while (0)
SLIST_FIRST() は、同じく /usr/src/sys/sys/queue.h で以下のように定義されています。
#define SLIST_FIRST(head) ((head)->slh_first)
従って、knlist_init() の SLIST_INIT(&knl->kl_list); は以下のように展開されます。
do {
((&knl->kl_list)->slh_first) = NULL;
} while(0);
knl->kl_list は、/usr/src/sys/sys/event.h で以下のように定義されています。
struct knote;
SLIST_HEAD(klist, knote);
struct knlist {
struct klist kl_list;
SLIST_HEAD() は、/usr/src/sys/sys/queue.h で以下のように定義されています。
#define SLIST_HEAD(name, type) \
struct name { \
struct type *slh_first; /* first element */ \
}
この定義を上記の struct knlist に適用したものを以下に示します。
struct knote;
struct klist {
struct knote *slh_first; /* first element */
}
struct knlist {
struct klist kl_list;
ここでは struct knote の宣言のみを行なっていますが、/usr/src/sys/sys/event.h の後半で struct knote を定義しています。
次の処理は、so->so_count の初期化です。
so->so_count = 1;
ここまでのデータ構造を以下に示します。
今回の処理で変更した変数を青、変更した値は赤で示しています。参照している値も赤線で示してあります。
[1] | TCP などのように、事前に仮想的な通信路を作成する仕組みに対して、その通信路を確立する 方法/手順 などのことを「呼を張る」と表現します。 |