• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

记录一下linux-514-中-listen-的实现

linux 搞代码 4年前 (2022-03-03) 12次浏览 已收录 0个评论
文章目录[隐藏]

概述

面试时候选人提到了半连贯,全连贯队列别离由内核中的常量,backlog参数决定。这个和之前的了解不符,于是看代码一探到底。

源码分析

源码版本:
https://github.com/torvalds/l…
d992fe5318d8d7af9510b879439a3c7f283da442

入口

搜寻”sys_listen”, 能够看到,backlog的确是取了用户传入和sysctl_somaxconn的最小值。

int __sys_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 int)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;
}

listen的实现

能够看到sock->ops是一个函数指针构造体,搜寻”sock->ops = “,能够找到一系列proto_ops。挑base_sock_ops持续看。

static const struct proto_ops base_sock_ops = {
    .family        = PF_ISDN,
    .owner        = THIS_MODULE,
    .release    = base_sock_release,
    .ioctl        = base_sock_ioctl,
    .bind        = base_sock_bind,
    .getname    = sock_no_getname,
    .sendmsg    = sock_no_sendmsg,
    .recvmsg    = sock_no_recvmsg,
    .listen        = sock_no_listen,
    .shutdown    = sock_no_shutdown,
    .connect    = sock_no_connect,
    .socketpair    = sock_no_socketpair,
    .accept        = sock_no_accept,
    .mmap        = sock_no_mmap
};

发现大部分的proto_ops都没有定义listen的实现。
搜寻”.listen =”,能够找到有 svc_listen, dn_listen, unix_listen, vsock_listen, x25_listen等. 从命名和文件头的形容,猜想inet_listen是理论用的listen,即socket_addr中指定的协定。持续看inet_listen。
能够看到backlog赋值给了sk->sk_max_ack_backlog,搜寻发现其它listen函数也是如此。

int inet_listen(struct socket *sock, int backlog)
{
    struct sock *sk = sock->sk;
    unsigned char old_state;
    int err, tcp_fastopen;

    lock_sock(sk);

    err = -EINVAL;
    if (sock->state != SS_UNCONNECTED || sock->type != SOCK_STREAM)
        goto out;

    old_state = sk->sk_state;
    if (!((1 << old_state) & (TCPF_CLOSE | TCPF_LISTEN)))
        goto out;

    WRITE_ONCE(sk->sk_max_ack_backlog, backlog);
    /* Really, if the socket is already in listen state
     * we can only allow the backlog to be adjusted.
     */

从inet_connection_sock.c:795 能够看到,这个值决定的icsk_accept_queue队列大小,从代码上来看,并没有辨别所谓的半,全链接队列。并且做了一个新生代老年代的优化.
一个是reqsk_timer_handler中有解决syn_ack的重发。

    icsk = inet_csk(sk_listener);
    net = sock_net(sk_listener);
    max_syn_ack_retries = icsk->icsk_syn_retries ? : net->ipv4.sysctl_tcp_synack_retries;
    /* Normally all the openreqs are young and become mature
     * (i.e. converted to established socket) for first timeout.
     * If synack was not acknowledged for 1 second, it means
     * one of the following things: synack was lost, ack was lost,
     * rtt is high or nobody planned to ack (i.e. synflood).
     * When server is a bit loaded, queue is populated with old
     * open requests, reducing effective size of queue.
     * When server is well loaded, queue size reduces to zero
     * after several minutes of work. It is not synflood,
     * it is normal operation. The solution is pruning
     * too old entries overriding normal timeout, when
     * situation becomes dangerous.
     *
     * Essentially, we reserve half of room for young
     * embrions; and abort old ones without pity, if old
     * ones are about to clog our table.
     */
    queue = &icsk->icsk_accept_queue;
    qlen = reqsk_queue_len(queue);
    if ((qlen << 1) > max(8U, READ_ONCE(sk_listener->sk_max_ack_backlog))) {
        int young = reqsk_queue_len_young(queue) << 1;

        while (max_syn_ack_retries > 2) {
            if (qlen < young)
                break;
            max_syn_ack_retries--;
            young <<= 1;
        }
    }

一个是accept中有去find already established connection。

/*
 * This will accept the next outstanding connection.
 */
struct sock *inet_csk_accept(struct sock *sk, int flags, int *err, bool kern)
{
    struct inet_connection_sock *icsk = inet_csk(sk);
    struct request_sock_queue *queue = &icsk->icsk_accept_queue;
    struct request_sock *req;
    struct sock *newsk;
    int error;

    lock_sock(sk);

    /* We need to make sure that this socket is listening,
     * and that it has something pending.
     */
    error = -EINVAL;
    if (sk->sk_state != TCP_LISTEN)
        goto out_err;

    /* Find already established connection */
    if (reqsk_queue_empty(queue)) {
        long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);

        /* If this is a non blocking socket don't sleep */
        error = -EAGAIN;
        if (!timeo)
            goto out_err;

        error = inet_csk_wait_for_connect(sk, timeo);
        if (error)
            goto out_err;
    }
    req = reqsk_queue_remove(queue, sk);
    newsk = req->sk;

论断

backlog决定icsk_accept_queue队列大小。该队列中存在SYNC_RECV/ESTABLISHTED状态的链接。


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:记录一下linux-514-中-listen-的实现

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址