zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    Oracle 推出 ODAC for Entity Framework 和 LINQ to Entities Beta版
    Entity Framework Feature CTP 5系列文章
    MonoDroid相关资源
    MSDN杂志上的Windows Phone相关文章
    微软学Android Market推出 Web Windows Phone Marketplace
    使用 Visual Studio Agent 2010 进行负载压力测试的安装指南
    MonoMac 1.0正式发布
    Shawn Wildermuth的《Architecting WP7 》系列文章
    使用.NET Mobile API即51Degrees.mobi检测UserAgent
    MongoDB 客户端 MongoVue
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/11750550.html
Copyright © 2011-2022 走看看