FreeBSD kernel SOCKET I/F 探検

第5回 socket() システムコール (3)

2013.11.07 (2014.01.16更新)

必要とする知識など:
  • UNIX (含む Linux) 上での C 言語と SOCKET によるプログラミング経験および TCP/IP に関する知識。

socket() システムコールの kernel 側の入口である sys_socket() 関数が実装されているソースファイルは uipc_syscalls.c ですが、この「uipc」は、たぶん「UNIX Interprocess Communication」だと思います。参考 URL: http://ja.wikipedia.org/wiki/%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E9%96%93%E9%80%9A%E4%BF%A1

socket() システムコール、三回目です。falloc_noinstall() から falloc() に戻ったところです。

falloc_noinstall() から falloc() に戻ると、if (error) でエラーの判定をしていますが、この説明は割愛します。今後も、関数呼出後の戻り値の確認に関しては、必要が無い限り割愛します。

次は finstall() の呼び出しです。

error = finstall(td, fp, &fd, flags);

finstall() も falloc() と同じ /usr/src/sys/kern/kern_descrip.c (http://svnweb.freebsd.org/base/release/9.1.0/sys/kern/kern_descrip.c?revision=243808&view=markup) で以下のように定義されています。

finstall(struct thread *td, struct file *fp, int *fd, int flags)
{
        struct filedesc *fdp = td->td_proc->p_fd;
        int error;

        KASSERT(fd != NULL, ("%s: fd == NULL", __func__));
        KASSERT(fp != NULL, ("%s: fp == NULL", __func__));

        FILEDESC_XLOCK(fdp);
        if ((error = fdalloc(td, 0, fd))) {
                FILEDESC_XUNLOCK(fdp);
                return (error);
        }

最初の処理は、struct filedesc *fdp の初期化です。

struct filedesc *fdp = td->td_proc->p_fd;

これは、前々回 説明した sys_socket() の最初の処理と同じです。次の KASSERT() は飛ばしてその次

FILEDESC_XLOCK(fdp);

詳細は割愛しますが、直前で取得した struct filedesc *fdb に対してロックを掛けているようです。struct filedesc に対する別スレッドからの操作を安全に実行するためのロックであると推測します。

FILEDESC_XLOCK は、/usr/src/sys/filedesc.h (http://svnweb.freebsd.org/base/release/9.1.0/sys/sys/filedesc.h?view=markup) で以下のように定義されています。

#define FILEDESC_XLOCK(fdp) sx_xlock(&(fdp)->fd_sx)

sx_xlock は、/usr/src/sys/sys/sx.h (http://svnweb.freebsd.org/base/release/9.1.0/sys/sys/sx.h?view=markup) で以下のように定義されています。

#define sx_xlock_(sx, file, line) \
        (void)_sx_xlock((sx), 0, (file), (line))

_sx_xlock は、/usr/src/sys/kern_sx.c (http://svnweb.freebsd.org/base/release/9.1.0/sys/kern/kern_sx.c?view=markup) で定義されています。

これを追い掛けていくと長くなるので、ここまでとします。なお、sx_xlock の頭の sx ですが、kern_sx.c の先頭に以下の記述があります。

/*
 * Shared/exclusive locks.

次に進みます。

if ((error = fdalloc(td, 0, fd))) {

fdalloc() も同様に kern_descrip.c で以下のように定義されています。

int
fdalloc(struct thread *td, int minfd, int *result)
{
        ...
        int fd = -1, maxfd;

fdalloc() の詳細に関しては割愛しますが、int のファイルディスクリプタを新たに割り当て、それを int fd に代入します。fdalloc() の return 直前の処理を以下に示します。

fdp->fd_ofileflags[fd] = 0; /* XXX needed? */
fdused(fdp, fd);
*result = fd;
return (0);

fd_ofileflags[fd] = 0 は、コメントにもあるように不要な処理なのかも知れません。

fdused() は、新たに割り当てた int のファイルディスクリプタが使用中であることをビットマップで管理しています。fdused() も同様に kern_descrip.c で定義されています。fdused() も詳細に関しては割愛しますが、以下が fdused() でのビットマップの操作です。

fdp->fd_map[NDSLOT(fd)] |= NDBIT(fd);

fd_map は、struct filedesc のメンバ変数で、以下のように定義されています。

#define NDSLOTTYPE      u_long
        NDSLOTTYPE *fd_map;             /* bitmap of free fds */

NDSLOT と NDBIT は、kern_descrip.c で以下のように定義されています。

#define NDSLOT(x)     ((x) / NDENTRIES)
#define NDBIT(x)      ((NDSLOTTYPE)1 << ((x) % NDENTRIES))

fdalloc() で割り当てた int のファイルディスクリプタ番のビットを 1 にして struct filedesc のメンバ変数 fd_map に格納しています。

fdalloc() から finstall() に戻ります。

fhold(fp);

fhold() は、struct file と同じ /usr/src/sys/sys/file.h (http://svnweb.freebsd.org/base/release/9.1.0/sys/sys/file.h?view=markup) で定義されています。

#define fhold(fp) \
        (refcount_acquire(&(fp)->f_count))

refcount_acquire() は 前回 説明しているので詳細は省きますが、この処理では fp->f_count に 1 を加算しています。

fhold() の次の処理です。

fdp->fd_ofiles[*fd] = fp;
if ((flags & O_CLOEXEC) != 0)
        fdp->fd_ofileflags[*fd] |= UF_EXCLOSE;
FILEDESC_XUNLOCK(fdp);
return (0);

まず、struct file *fp を struct filedesc *fdp のメンバ変数 fd_ofiles の配列の要素番号 int *fd に代入しています。次の if 文ですが、この flagsは sys_socket() が falloc() の呼び出しで第四引数 int flags に渡した値がそのまま渡ります。sys_socket() は falloc() を呼び出す際に、第四引数 int flags に 0 を渡しているので、この if 文は偽になります。次の FILEDESC_XUNLOCK() は、finstall() の最初の方で実施した FILEDESC_XLOCK() の解除です。最後に 0を返却して、falloc() に戻ります。

falloc() は、finstall() から戻ると以下の処理を実施します。

if (error) {
        fdrop(fp, td);          /* one reference (fp only) */
        return (error);
}

if (resultfp != NULL)
        *resultfp = fp;         /* copy out result */
else
        fdrop(fp, td);          /* release local reference */

if (resultfd != NULL)
        *resultfd = fd;

return (0);

ほぼ見たままですが、resultfp および resultfd が NULL で無ければ、それぞれ今割り当てた struct file * fp と int fd を代入し、最後に 0 を返却してfalloc() から sys_socket() に戻ります。

エラー発生時あるいは resultfp が NULL の場合に呼び出している fdrop() は /usr/src/sys/sys/file.h (http://svnweb.freebsd.org/base/release/9.1.0/sys/sys/file.h?view=markup) で定義されています。説明は割愛します。

#define fdrop(fp, td)                                                   \
      (refcount_release(&(fp)->f_count) ? _fdrop((fp), (td)) : _fnoop())

今回の処理で変更した変数を青、変更した値は赤で示しています。図の横幅が狭い長方形は、char やポインタなどの配列を示します。長方形の中にある「[fd]」は、配列の添字の意味です。NDSLOTTYPE は「[fd]」を適切に表現できなかったので省いています。

著者プロフィール

asou

好きな OS は、FreeBSD です。が、最近購入した Mac mini がなかなか快適なので、Mac でいいかなと思うようになってきました。

記事一覧Index