• TCP层accept系统调用的实现分析


    inet_csk_accept函数实现了tcp协议accept操作,其主要完成的功能是,从已经完成三次握手的队列中取控制块,如果没有已经完成的连接,则需要根据阻塞标记来来区分对待,若非阻塞则直接返回,若阻塞则需要在一定时间范围内阻塞等待;

     1 /*
     2  * This will accept the next outstanding connection.
     3  */
     4 struct sock *inet_csk_accept(struct sock *sk, int flags, int *err, bool kern)
     5 {
     6     struct inet_connection_sock *icsk = inet_csk(sk);
     7     struct request_sock_queue *queue = &icsk->icsk_accept_queue;
     8     struct request_sock *req;
     9     struct sock *newsk;
    10     int error;
    11 
    12     lock_sock(sk);
    13 
    14     /* We need to make sure that this socket is listening,
    15      * and that it has something pending.
    16      */
    17     error = -EINVAL;
    18 
    19     /* 不是listen状态 */
    20     if (sk->sk_state != TCP_LISTEN)
    21         goto out_err;
    22 
    23     /* Find already established connection */
    24 
    25     /* 还没有已完成的连接 */
    26     if (reqsk_queue_empty(queue)) {
    27 
    28         /* 获取等待时间,非阻塞为0 */
    29         long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
    30 
    31         /* If this is a non blocking socket don't sleep */
    32         error = -EAGAIN;
    33         /* 非阻塞立即返回错误 */
    34         if (!timeo)
    35             goto out_err;
    36 
    37         /* 等待连接到来 */
    38         error = inet_csk_wait_for_connect(sk, timeo);
    39         if (error)
    40             goto out_err;
    41     }
    42 
    43     /* 从已完成连接队列中移除 */
    44     req = reqsk_queue_remove(queue, sk);
    45 
    46     /* 设置新控制块指针 */
    47     newsk = req->sk;
    48 
    49     /* TCP协议 && fastopen */
    50     if (sk->sk_protocol == IPPROTO_TCP &&
    51         tcp_rsk(req)->tfo_listener) {
    52         spin_lock_bh(&queue->fastopenq.lock);
    53         if (tcp_rsk(req)->tfo_listener) {
    54             /* We are still waiting for the final ACK from 3WHS
    55              * so can't free req now. Instead, we set req->sk to
    56              * NULL to signify that the child socket is taken
    57              * so reqsk_fastopen_remove() will free the req
    58              * when 3WHS finishes (or is aborted).
    59              */
    60             req->sk = NULL;
    61             req = NULL;
    62         }
    63         spin_unlock_bh(&queue->fastopenq.lock);
    64     }
    65 out:
    66     release_sock(sk);
    67 
    68     /* 释放请求控制块 */
    69     if (req)
    70         reqsk_put(req);
    71 
    72     /* 返回找到的连接控制块 */
    73     return newsk;
    74 out_err:
    75     newsk = NULL;
    76     req = NULL;
    77     *err = error;
    78     goto out;
    79 }

    如果请求队列中没有已完成握手的连接,并且套接字已经设置了阻塞标记,则需要加入调度队列等待连接的到来,inet_csk_wait_for_connect函数完成了这个功能;

     1 /*
     2  * Wait for an incoming connection, avoid race conditions. This must be called
     3  * with the socket locked.
     4  */
     5 static int inet_csk_wait_for_connect(struct sock *sk, long timeo)
     6 {
     7     struct inet_connection_sock *icsk = inet_csk(sk);
     8     DEFINE_WAIT(wait);
     9     int err;
    10 
    11     /*
    12      * True wake-one mechanism for incoming connections: only
    13      * one process gets woken up, not the 'whole herd'.
    14      * Since we do not 'race & poll' for established sockets
    15      * anymore, the common case will execute the loop only once.
    16      *
    17      * Subtle issue: "add_wait_queue_exclusive()" will be added
    18      * after any current non-exclusive waiters, and we know that
    19      * it will always _stay_ after any new non-exclusive waiters
    20      * because all non-exclusive waiters are added at the
    21      * beginning of the wait-queue. As such, it's ok to "drop"
    22      * our exclusiveness temporarily when we get woken up without
    23      * having to remove and re-insert us on the wait queue.
    24      */
    25     for (;;) {
    26 
    27         /* 加入等待队列 */
    28         prepare_to_wait_exclusive(sk_sleep(sk), &wait,
    29                       TASK_INTERRUPTIBLE);
    30         release_sock(sk);
    31 
    32         /* 如果为空计算,进行调度 */
    33         if (reqsk_queue_empty(&icsk->icsk_accept_queue))
    34             timeo = schedule_timeout(timeo);
    35         sched_annotate_sleep();
    36         lock_sock(sk);
    37         err = 0;
    38         /* 队列不为空*/
    39         if (!reqsk_queue_empty(&icsk->icsk_accept_queue))
    40             break;
    41         err = -EINVAL;
    42         /* 连接状态不是LISTEN */
    43         if (sk->sk_state != TCP_LISTEN)
    44             break;
    45         /* 信号打断 */
    46         err = sock_intr_errno(timeo);
    47         if (signal_pending(current))
    48             break;
    49         err = -EAGAIN;
    50         /* 调度超时 */
    51         if (!timeo)
    52             break;
    53     }
    54     /* 结束等待 */
    55     finish_wait(sk_sleep(sk), &wait);
    56     return err;
    57 }

    reqsk_queue_remove函数完成了将完成握手的控制块从请求队列移除的工作;

     1 static inline struct request_sock *reqsk_queue_remove(struct request_sock_queue *queue,
     2                               struct sock *parent)
     3 {
     4     struct request_sock *req;
     5 
     6     spin_lock_bh(&queue->rskq_lock);
     7 
     8     /* 找到队列头 */
     9     req = queue->rskq_accept_head;
    10     if (req) {
    11         /* 减少已连接计数 */
    12         sk_acceptq_removed(parent);
    13         /* 头部指向下一节点 */
    14         queue->rskq_accept_head = req->dl_next;
    15 
    16         /* 队列为空 */
    17         if (queue->rskq_accept_head == NULL)
    18             queue->rskq_accept_tail = NULL;
    19     }
    20     spin_unlock_bh(&queue->rskq_lock);
    21     return req;
    22 }
  • 相关阅读:
    【分区】使用 MBR 分区表分区并格式化
    微信小程序公司开发前必读
    Delphi 经典书籍
    sybase 通过select into创建新表
    sybase 创建触发器
    delphi 判断exe重复执行
    git 的诞生
    git 常用命令
    mvn spring-boot:run运行不了的解决办法
    git 提交代码
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/11750550.html
Copyright © 2020-2023  润新知