• accept系统调用内核实现


    用户态对accept的标准使用方法:
    if ((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr, &sin_size)) == -1)
      {
       //accept()函数让server接收客户的连接请求
       perror("accept Error
    ");
       continue;
      }
    sockfd是通过socket系统调用,而且经过listen过的套接字:
    sockfd = socket(AF_INET, SOCK_STREAM, 0)
    listen(sockfd, 128)
    
    remote_addr将会存储远端设备的地址信息。
    
    /*
     *	For accept, we attempt to create a new socket, set up the link
     *	with the client, wake up the client, then return the new
     *	connected fd. We collect the address of the connector in kernel
     *	space and move it to user at the very end. This is unclean because
     *	we open the socket then return an error.
     *
     *	1003.1g adds the ability to recvmsg() to query connection pending
     *	status to recvmsg. We need to add that support in a way thats
     *	clean when we restucture accept also.
     */
    
    asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr,
          int __user *upeer_addrlen)
    {
     struct socket *sock, *newsock;
     struct file *newfile;
     int err, len, newfd, fput_needed;
     char address[MAX_SOCK_ADDR];
    
    //通过监听套接字的描写叙述符fd,找到监听套接字
     sock = sockfd_lookup_light(fd, &err, &fput_needed);
     if (!sock)
      goto out;
    
     err = -ENFILE;
    //创建新的socket,即心的套接字,它将被client_fd 描写叙述,用于数据传输,
    //也就是accept系统调用返回值client_fd 所相应的套接口
     if (!(newsock = sock_alloc()))
      goto out_put;
    
    //继承listen_fd相应的的一些属性,包含套接字类型,和操作。
    //不难理解,listen_fd和client_fd 相应的套接口都是tcp,这些不用一一赋值,直接用listen_fd的属性就可以。
     newsock->type = sock->type;
     newsock->ops = sock->ops;
    
     /*
      * We don't need try_module_get here, as the listening socket (sock)
      * has the protocol module (sock->ops->owner) held.
      */
    //不懂
     __module_get(newsock->ops->owner);
    
    //创建新的file,然后返回newfd ,这个fd就是待会被返回的client_fd 
    //到如今为止,这个newfd和newfile是有关联的。
     newfd = sock_alloc_fd(&newfile);
     if (unlikely(newfd < 0)) {
      err = newfd;
      sock_release(newsock);
      goto out_put;
     }
    //使得这个newsock绑定刚才新建的newfile
    //即如今为止,newfd  newfile newsock之间是有关联的
     err = sock_attach_fd(newsock, newfile);
     if (err < 0)
      goto out_fd_simple;
    
     err = security_socket_accept(sock, newsock);
     if (err)
      goto out_fd;
    //newsock是socket结构体,sock->ops->accept的目的是为newsock关联一个sock结构体
    //即三次握手结束后,新建的sock传输控制块,它等待用户accept系统调用“领养”它。
    //sock->ops->accept相应的函数是inet_accept
     err = sock->ops->accept(sock, newsock, sock->file->f_flags);
     if (err < 0)
      goto out_fd;
    
     if (upeer_sockaddr) {
      if (newsock->ops->getname(newsock, (struct sockaddr *)address,
           &len, 2) < 0) {
       err = -ECONNABORTED;
       goto out_fd;
      }
      err = move_addr_to_user(address, len, upeer_sockaddr,
         upeer_addrlen);
      if (err < 0)
       goto out_fd;
     }
    .........................................
    }
    
    /*
     *	Accept a pending connection. The TCP layer now gives BSD semantics.
     */
    int inet_accept(struct socket *sock, struct socket *newsock, int flags)
    {
    //第一个參数sock是监听套接字代表的套接字
    //newsock是刚才我们新建的套接字,用以描写叙述数据传输
    //显然,此函数的目的,是找到通过套接字,找到套接字上挂着的以及完毕三次握手的sk
    //这个被找到的sk,将被关联到newsock
    
    //sk1 是监听套接字相应的传输控制块
     struct sock *sk1 = sock->sk;
     int err = -EINVAL;
    
    //sk2(三次握手后建立的传输控制块) 是挂在sk1(监听套接字的传输控制块) 中的完毕三次握手后的sk
    // sk1->sk_prot->accept 相应的函数是inet_csk_accept
     struct sock *sk2 = sk1->sk_prot->accept(sk1, flags, &err);
    
     if (!sk2)
      goto do_err;
    
     lock_sock(sk2);
    
     WARN_ON(!((1 << sk2->sk_state) &
        (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_CLOSE)));
    
    //这个被找到的sk,将被关联到newsock
    //三次握手后建立的传输控制块sk(sock 结构体),是不正确应不论什么socket结构体的,所以我们关联上
    //这样三次握手后建立的传输控制块sk,就和文件系统有关联了
    //关联上后,我们就能对其调用send,recvfrom等系统调用了
     sock_graft(sk2, newsock);
    
     newsock->state = SS_CONNECTED;
     err = 0;
     release_sock(sk2);
    do_err:
     return err;
    }
    
    /*
     * This will accept the next outstanding connection.
     */
    //inet_csk_accept的功能时获取建立3次握手后的sk,一次调用返回一个sk
    struct sock *inet_csk_accept(struct sock *sk, int flags, int *err)
    {
    //第一个參数是监听套接字相应的传输控制块。
     struct inet_connection_sock *icsk = inet_csk(sk);
     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;
    //假设你传进的參数正确,监听套接字相应的传输控制块的状态,肯定是TCP_LISTEN
     if (sk->sk_state != TCP_LISTEN)
      goto out_err;
    
     /* Find already established connection */
    //icsk->icsk_accept_queue挂的是request_sock,request_sock上挂的就是三次握手后新建的sk
    //reqsk_queue_empty(&icsk->icsk_accept_queue)推断是否空,就进入if,怎样设置为堵塞,则休眠
     if (reqsk_queue_empty(&icsk->icsk_accept_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;
     }
    
    //reqsk_queue_get_child的逻辑是:
    //1:取出icsk_accept_queue的request_sock,然后取出request_sock中的sk
    //2:listen的sk中,sk_ack_backlog计数减一,sk->sk_ack_backlog--,由于sk_ack_backlog有上限。
    //3:删除request_sock
    //4:return取出的sk
     newsk = reqsk_queue_get_child(&icsk->icsk_accept_queue, sk);
     WARN_ON(newsk->sk_state == TCP_SYN_RECV);
    out:
     release_sock(sk);
     return newsk;
    out_err:
     newsk = NULL;
     *err = error;
     goto out;
    }
    

  • 相关阅读:
    raid0
    GitHub 标星 11000+,阿里开源的微服务组件如何连续 10 年扛住双十一大促?
    写给大家看的“不负责任” K8s 入门文档
    快速迁移 Next.js 应用到函数计算
    轻松搭建基于 Serverless 的 Go 应用(Gin、Beego 举例)
    阿里巴巴副总裁肖力:云原生安全下看企业新边界——身份管理
    从零开始入门 K8s | K8s 安全之访问控制
    深度解读!阿里统一应用管理架构升级的教训与实践
    CNCF 2019 年度报告重磅发布 | 云原生生态周报 Vol. 41
    HTML+CSS技术实现网页滑动门效果
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/3901351.html
Copyright © 2020-2023  润新知