[an error occurred while processing this directive] [an error occurred while processing this directive]

ソケットAPI (socket, send, recv)

現代、ネットワークプログラミングを行うのは、TCP/IPのソケットAPIを使うのが一般的です。 socket でソケットを作り、サーバはbind,listen でポートで待ち受け、クライアントは connect で接続します。
TCP/IP の規格はRFCで決まっていますが、TCP/IP プログラミングの規格はRFCでは決められていません。 でも、Unixりの場合は Posix ソケットと呼ばれる規格を用いるのが一般的です。 これにより、様々な Unix で、統一的にネットワーク Uniux Linuxでは、 sys_socket を見てみる

libc の API, recv()

linux + x86 の環境では、socket, accept, listen などは glibc の関数で、実体は socketcall というシステムコールの場合もある ともあれ実体は、sys_accept, sys_socket - setsockopt もシステムコール。
ユーザが外部から呼べる関数です。man にも載っています。
RECV(2)                    Linux Programmer’s Manual                   RECV(2)

NAME
       recv, recvfrom, recvmsg - receive a message from a socket

SYNOPSIS
       #include 
       #include 

       ssize_t recv(int s, void *buf, size_t len, int flags);

recv() in glibc:: socket/bits/socket2.h

ユーザ空間の関数 recv() の実体は、glibc のsocket/bits/socket2.h で定義されている recv 関数です。
__extern_always_inline ssize_t
recv (int __fd, void *__buf, size_t __n, int __flags)
{
  if (__bos0 (__buf) != (size_t) -1)
    {
      if (!__builtin_constant_p (__n))
        return __recv_chk (__fd, __buf, __n, __bos0 (__buf), __flags);

      if (__n > __bos0 (__buf))
        return __recv_chk_warn (__fd, __buf, __n, __bos0 (__buf), __flags);
    }
  return __recv_alias (__fd, __buf, __n, __flags);
}

recv_chk() in glibc:: debug/recv_chk.c

引数をチェックしたのち、__recv() を呼んでいます。
ssize_t
recv_chk (int fd, void *buf, size_t n, size_t buflen, int flags)
{
  if (n > buflen)
    __chk_fail ();

  return __recv (fd, buf, n, flags);
}

__recv() in glibc:: sysdeps/unix/sysv/linux/recv.S

socket を recv, __socket を __libc_recv と定義して、 socket.S を読み込んでいます。
これにより、socket.S 内の socket および __socket シンボルが置換され、 __libc_recv という関数が定義される。 この関数の動作は、引数に SOCKOP_recv をセットして、socketcall システムコールを呼び出すことである。

最後に、この__libc_recv の別名として __recv を与えている。
#define socket  recv
#define __socket __libc_recv
#define NARGS   4
#define NEED_CANCELLATION
#include 
weak_alias (__libc_recv, __recv)

__socket() in sysdeps/unix/sysv/linux/socketcall.h

関数 (実際には recv() や send() に展開される)
#define SOCKOP_recv             10
関数の定義になる、socket.S は以下の通りである。
glibc :: sysdeps/unix/sysv/linux/i386/socket.S
       /* C からは __socket() という関数に見える。 */
       /* 引数は 
.globl __socket
ENTRY (__socket)
#if defined NEED_CANCELLATION && defined CENABLE
        SINGLE_THREAD_P
        jne 1f
#endif

        /* Save registers.  */
        movl %ebx, %edx
        cfi_register (3, 2)

        /* ここで、%eax に socketcall のシステムコール番号 (102) がセットされる */
        movl $SYS_ify(socketcall), %eax /* System call number in %eax.  */

        /* Use ## so `socket' is a separate token that might be #define'd.  */
        /* ここに、socketcall 内で分岐するための番号が入る */
        /* (socket, send, recv, accept, listen など)       */
        /* SOCKOP_ と socket が結合されて、 SOCKOP_recv みたいになる。*/
        /* この値は sysdeps/sysv/linux/socketcall.h で定義されている。 */
        movl $P(SOCKOP_,socket), %ebx   /* Subcode is first arg to syscall.  */
        lea 4(%esp), %ecx               /* Address of args is 2nd arg.  */

        /* Do the system call trap.  */
        /* ここでトラップ命令 (sysenter か 0x80) が呼ばれて、*/
        /* 特権モードに入る。*/
        ENTER_KERNEL 

        /* 特権モードから戻ってきた後の処理。*/
        /* Restore registers.  */
        movl %edx, %ebx
        cfi_restore (3)

        /* %eax is < 0 if there was an error.  */
        cmpl $-125, %eax
        jae SYSCALL_ERROR_LABEL

        /* Successful; return the syscall's value.  */
L(pseudo_end):
        ret

システムコール変換テーブル (in arch/x86/kernel/syscall_table_32.S)

ようやくカーネル空間にたどりつきました。
トラップ命令後です。
ENTRY(sys_call_table)
        .long sys_restart_syscall       /* 0 - old "setup()" system call, used for restarting */
...
...
        .long sys_fstatfs       /* 100 */
        .long sys_ioperm
        .long sys_socketcall
%eax に 102 をセットしてトラップ命令を呼ぶと、sys_socketcall に飛びます。

sys_socketcall in net/socket.c

asmlinkage long sys_socketcall(int call, unsigned long __user *args)
{
        unsigned long a[6];
        unsigned long a0, a1;
        int err;

        if (call < 1 || call > SYS_PACCEPT)
                return -EINVAL;

        /* copy_from_user should be SMP safe. */
        if (copy_from_user(a, args, nargs[call]))
                return -EFAULT;

        err = audit_socketcall(nargs[call] / sizeof(unsigned long), a);
        if (err)
                return err;

        a0 = a[0];
        a1 = a[1];

        switch (call) {
        case SYS_SOCKET:
                err = sys_socket(a0, a1, a[2]);
                break;
        case SYS_BIND:
                err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
                break;
...
         case SYS_RECV:
                err = sys_recv(a0, (void __user *)a1, a[2], a[3]);
                break;

sys_recv in net/socket.c

/*
 *      Receive a datagram from a socket.
 */

asmlinkage long sys_recv(int fd, void __user *ubuf, size_t size,
                         unsigned flags)
{
        return sys_recvfrom(fd, ubuf, size, flags, NULL, NULL);
}

sys_recvfrom() in net/socket.c

/*
 *      Receive a frame from the socket and optionally record the address of the
 *      sender. We verify the buffers are writable and if needed move the
 *      sender address from kernel to user space.
 */

asmlinkage long sys_recvfrom(int fd, void __user *ubuf, size_t size,
                             unsigned flags, struct sockaddr __user *addr,
                             int __user *addr_len)
{
        struct socket *sock;
        struct iovec iov;
        struct msghdr msg;
        struct sockaddr_storage address;
        int err, err2;
        int fput_needed;

        sock = sockfd_lookup_light(fd, &err, &fput_needed);
        if (!sock)
                goto out;

        msg.msg_control = NULL;
        msg.msg_controllen = 0;
        msg.msg_iovlen = 1;
        msg.msg_iov = &iov;
        iov.iov_len = size;
        iov.iov_base = ubuf;
        msg.msg_name = (struct sockaddr *)&address;
        msg.msg_namelen = sizeof(address);
        if (sock->file->f_flags & O_NONBLOCK)
                flags |= MSG_DONTWAIT;
=>      err = sock_recvmsg(sock, &msg, size, flags);

        if (err >= 0 && addr != NULL) {
                err2 = move_addr_to_user((struct sockaddr *)&address,
                                         msg.msg_namelen, addr, addr_len);
                if (err2 < 0)
                        err = err2;
        }

        fput_light(sock->file, fput_needed);
out:
        return err;
}

net/socket.c sock_recvmsg() --- int sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, int flags) { struct kiocb iocb; struct sock_iocb siocb; int ret; init_sync_kiocb(&iocb, NULL); iocb.private = &siocb; => ret = __sock_recvmsg(&iocb, sock, msg, size, flags); if (-EIOCBQUEUED == ret) ret = wait_on_sync_kiocb(&iocb); return ret; } --- net/socket.c --- static inline int __sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size, int flags) { int err; struct sock_iocb *si = kiocb_to_siocb(iocb); si->sock = sock; si->scm = NULL; si->msg = msg; si->size = size; si->flags = flags; err = security_socket_recvmsg(sock, msg, size, flags); if (err) return err; => return sock->ops->recvmsg(iocb, sock, msg, size, flags); } --- さまざまな recv 関数が呼べるようになっているが、 今回見ているのは ipv4 。 *** net/ipv4/af_inet.c: recvmsg[857] .recvmsg = sock_common_recvmsg, --- net/core/sock.c --- int sock_common_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size, int flags) { struct sock *sk = sock->sk; int addr_len = 0; int err; => err = sk->sk_prot->recvmsg(iocb, sk, msg, size, flags & MSG_DONTWAIT, flags & ~MSG_DONTWAIT, &addr_len); if (err >= 0) msg->msg_namelen = addr_len; return err; } --- raw.c --- static int raw_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size, int flags) { struct sock *sk = sock->sk; struct sk_buff *skb; int err = 0; int noblock; noblock = flags & MSG_DONTWAIT; flags &= ~MSG_DONTWAIT; skb = skb_recv_datagram(sk, flags, noblock, &err); if (!skb) return err; if (size < skb->len) msg->msg_flags |= MSG_TRUNC; else size = skb->len; err = memcpy_toiovec(msg->msg_iov, skb->data, size); if (err < 0) { skb_free_datagram(sk, skb); return err; } sock_recv_timestamp(msg, sk, skb); if (msg->msg_name) { msg->msg_namelen = sizeof(struct sockaddr_can); memcpy(msg->msg_name, skb->cb, msg->msg_namelen); } skb_free_datagram(sk, skb); return size; } ---
[an error occurred while processing this directive]