ソケットバックログ


STREAM UNIXソケットでの記載です。クライアントソケット送信とは、サーバが送信データを受信する事でなく、サーバソケットother->sk_receive_queueに, クライアントソケット情報のstruct sk_buff skbをリストする事で、このリストできる最大値がバックログsk->sk_max_ack_backlogです。

バックログを超えているなら、サーバソケットother->peer_waitにクライアントソケットのcurrentプロセスをリストし、schedule_timeout()でクライアントソケットsk->sk_sndtimeo間ウエイトします。なお、other->peer_waitウエイト数の制限はないようです。
static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
                              int addr_len, int flags)
{
       struct sockaddr_un *sunaddr = (struct sockaddr_un *)uaddr;
       struct sock *sk = sock->sk;
       struct net *net = sock_net(sk);
       struct unix_sock *u = unix_sk(sk), *newu, *otheru;
       struct sock *newsk = NULL;
       struct sock *other = NULL;
       struct sk_buff *skb = NULL;
       unsigned hash;
       int st;
       int err;
       long timeo;

       err = unix_mkname(sunaddr, addr_len, &hash);
       if (err < 0)
               goto out;
       addr_len = err;

       if (test_bit(SOCK_PASSCRED, &sock->flags) && !u->addr &&
           (err = unix_autobind(sock)) != 0)
               goto out;

       timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);

       err = -ENOMEM;

       newsk = unix_create1(sock_net(sk), NULL);
       if (newsk == NULL)
               goto out;

       skb = sock_wmalloc(newsk, 1, 0, GFP_KERNEL);
       if (skb == NULL)
               goto out;

restart:
       other = unix_find_other(net, sunaddr, addr_len, sk->sk_type, hash, &err);
       if (!other)
               goto out;

       unix_state_lock(other);

       if (sock_flag(other, SOCK_DEAD)) {
               unix_state_unlock(other);
               sock_put(other);
               goto restart;
       }

       err = -ECONNREFUSED;
       if (other->sk_state != TCP_LISTEN)
               goto out_unlock;
       if (other->sk_shutdown & RCV_SHUTDOWN)
               goto out_unlock;

       if (unix_recvq_full(other)) {
               err = -EAGAIN;
               if (!timeo)
                       goto out_unlock;

               timeo = unix_wait_for_peer(other, timeo);

               err = sock_intr_errno(timeo);
               if (signal_pending(current))
                       goto out;
               sock_put(other);
               goto restart;
       }

       st = sk->sk_state;

       switch (st) {
       case TCP_CLOSE:
               break;
       case TCP_ESTABLISHED:
               err = -EISCONN;
               goto out_unlock;
       default:
               err = -EINVAL;
               goto out_unlock;
       }

       unix_state_lock_nested(sk);

       if (sk->sk_state != st) {
               unix_state_unlock(sk);
               unix_state_unlock(other);
               sock_put(other);
               goto restart;
       }

       err = security_unix_stream_connect(sk, other, newsk);
       if (err) {
               unix_state_unlock(sk);
               goto out_unlock;
       }

       sock_hold(sk);
       unix_peer(newsk)        = sk;
       newsk->sk_state         = TCP_ESTABLISHED;
       newsk->sk_type          = sk->sk_type;
       init_peercred(newsk);
       newu = unix_sk(newsk);
       RCU_INIT_POINTER(newsk->sk_wq, &newu->peer_wq);
       otheru = unix_sk(other);

       if (otheru->addr) {
               atomic_inc(&otheru->addr->refcnt);
               newu->addr = otheru->addr;
       }
       if (otheru->dentry) {
               newu->dentry    = dget(otheru->dentry);
               newu->mnt       = mntget(otheru->mnt);
       }

       copy_peercred(sk, other);

       sock->state     = SS_CONNECTED;
       sk->sk_state    = TCP_ESTABLISHED;
       sock_hold(newsk);

       smp_mb__after_atomic_inc();     /* sock_hold() does an atomic_inc() */
       unix_peer(sk)   = newsk;

       unix_state_unlock(sk);

       spin_lock(&other->sk_receive_queue.lock);
       __skb_queue_tail(&other->sk_receive_queue, skb);
       spin_unlock(&other->sk_receive_queue.lock);
       unix_state_unlock(other);
       other->sk_data_ready(other, 0);
       sock_put(other);
       return 0;

out_unlock:
       if (other)
               unix_state_unlock(other);

out:
       kfree_skb(skb);
       if (newsk)
               unix_release_sock(newsk, 0);
       if (other)
               sock_put(other);
       return err;
}

static inline int unix_recvq_full(struct sock const *sk)
{
       return skb_queue_len(&sk->sk_receive_queue) > sk->sk_max_ack_backlog;
}

#define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function)
#define DEFINE_WAIT_FUNC(name, function)                                \
       wait_queue_t name = {                                           \
               .private        = current,                              \
               .func           = function,                             \
               .task_list      = LIST_HEAD_INIT((name).task_list),     \
       }

static long unix_wait_for_peer(struct sock *other, long timeo)
{
       struct unix_sock *u = unix_sk(other);
       int sched;
       DEFINE_WAIT(wait);

       prepare_to_wait_exclusive(&u->peer_wait, &wait, TASK_INTERRUPTIBLE);

       sched = !sock_flag(other, SOCK_DEAD) &&
               !(other->sk_shutdown & RCV_SHUTDOWN) &&
               unix_recvq_full(other);

       unix_state_unlock(other);

       if (sched)
               timeo = schedule_timeout(timeo);

       finish_wait(&u->peer_wait, &wait);
       return timeo;
}
backlog > sk->sk_max_ack_backlogなら、sk->peer_waitしているクライアントプロセスはsk->sk_receive_queueにリスト可能という事で、wake_up_interruptible_all()でsk->peer_waitのプロセスを起床させ、unix_stream_connect()のgoto restartでのループで、再度sk->sk_receive_queueにリストを試みます。
static int unix_listen(struct socket *sock, int backlog)
{
       int err;
       struct sock *sk = sock->sk;
       struct unix_sock *u = unix_sk(sk);
       struct pid *old_pid = NULL;
       const struct cred *old_cred = NULL;

       err = -EOPNOTSUPP;
       if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET)
               goto out;
       err = -EINVAL;
       if (!u->addr)
               goto out;
       unix_state_lock(sk);
       if (sk->sk_state != TCP_CLOSE && sk->sk_state != TCP_LISTEN)
               goto out_unlock;
       if (backlog > sk->sk_max_ack_backlog)
               wake_up_interruptible_all(&u->peer_wait);
       sk->sk_max_ack_backlog  = backlog;
       sk->sk_state            = TCP_LISTEN;

       init_peercred(sk);
       err = 0;

out_unlock:
       unix_state_unlock(sk);
       put_pid(old_pid);
       if (old_cred)
               put_cred(old_cred);
out:
       return err;
}
sk->sk_receive_queueから送信済クライアントソケットstruct sk_buff *skbを取得し、wake_up_interruptible(&unix_sk(sk)->peer_wait)で、バックログ値を超えて待機している=>peer_waitにリストしているクライアントプロセスを起床させます。
static int unix_accept(struct socket *sock, struct socket *newsock, int flags)
{
       struct sock *sk = sock->sk;
       struct sock *tsk;
       struct sk_buff *skb;
       int err;

       err = -EOPNOTSUPP;
       if (sock->type != SOCK_STREAM && sock->type != SOCK_SEQPACKET)
               goto out;

       err = -EINVAL;
       if (sk->sk_state != TCP_LISTEN)
               goto out;

       skb = skb_recv_datagram(sk, 0, flags&O_NONBLOCK, &err);
       if (!skb) {
               if (err == 0)
                       err = -EINVAL;
               goto out;
       }

       tsk = skb->sk;
       skb_free_datagram(sk, skb);
       wake_up_interruptible(&unix_sk(sk)->peer_wait);

       unix_state_lock(tsk);
       newsock->state = SS_CONNECTED;
       sock_graft(tsk, newsock);
       unix_state_unlock(tsk);
       return 0;

out:
       return err;
}

追記

バックログ最大値は、/proc/sys/net/core/somaxconnです。
[root@localhost ~]# cat /proc/sys/net/core/somaxconn
128

#define SOMAXCONN       128

static struct ctl_table netns_core_table[] = {
       {
               .procname       = "somaxconn",
               .data           = &init_net.core.sysctl_somaxconn,
               .maxlen         = sizeof(int),
               .mode           = 0644,
               .proc_handler   = proc_dointvec
       },
       { }
};

static __net_init int sysctl_core_net_init(struct net *net)
{
       struct ctl_table *tbl;

       net->core.sysctl_somaxconn = SOMAXCONN;

       tbl = netns_core_table;
       if (!net_eq(net, &init_net)) {
               tbl = kmemdup(tbl, sizeof(netns_core_table), GFP_KERNEL);
               if (tbl == NULL)
                       goto err_dup;

               tbl[0].data = &net->core.sysctl_somaxconn;
       }

       net->core.sysctl_hdr = register_net_sysctl_table(net,
                       net_core_path, tbl);
       if (net->core.sysctl_hdr == NULL)
               goto err_reg;

       return 0;

err_reg:
       if (tbl != netns_core_table)
               kfree(tbl);
err_dup:
       return -ENOMEM;
}

SYSCALL_DEFINE2(listen, int, fd, int, backlog)
{
       struct socket *sock;
       int err, fput_needed;
       int somaxconn;

       sock = sockfd_lookup_light(fd, &err, &fput_needed);
       if (sock) {
               somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;
               if ((unsigned)backlog > somaxconn)
                       backlog = somaxconn;

               err = security_socket_listen(sock, backlog);
               if (!err)
                       err = sock->ops->listen(sock, backlog);

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

備考

バックログを1としても、複数ソケット接続してもconnect()でエラーとならないのは、peer_waitで待機させられる故で、sk->sk_sndtimeo=0とすれば、待機することなくエラーとなるはずです。

最終更新 2016/10/03 15:29:26 - north
(2015/12/11 18:27:22 作成)


検索

アクセス数
3689262
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。