前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >网络函数accept源码分析

网络函数accept源码分析

原创
作者头像
theanarkh
修改2019-01-10 10:10:41
1.5K0
修改2019-01-10 10:10:41
举报
文章被收录于专栏:原创分享原创分享
代码语言:c
复制
static int sock_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen)
{
    struct file *file;
    struct socket *sock, *newsock;
    int i;
    char address[MAX_SOCK_ADDR];
    int len;
    if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
        return(-EBADF);
    // 根据文件描述符找到对应的file结构体和socket结构
      if (!(sock = sockfd_lookup(fd, &file))) 
        return(-ENOTSOCK);
    if (sock->state != SS_UNCONNECTED) 
    {
        return(-EINVAL);
    }
    // socket没有调用过listen,报错,该标记位在listen中设置
    if (!(sock->flags & SO_ACCEPTCON)) 
    {
        return(-EINVAL);
    }
    // 分配一个新的socket结构体
    if (!(newsock = sock_alloc())) 
    {
        printk("NET: sock_accept: no more sockets\n");
        return(-ENOSR);    /* Was: EAGAIN, but we are out of system
                   resources! */
    }
    newsock->type = sock->type;
    newsock->ops = sock->ops;
    // 创建一个底层的sock结构体和socket结构体互相关联
    if ((i = sock->ops->dup(newsock, sock)) < 0) 
    {
        sock_release(newsock);
        return(i);
    }
    i = newsock->ops->accept(sock, newsock, file->f_flags);
    if ( i < 0) 
    {
        sock_release(newsock);
        return(i);
    }
    // 返回一个新的文件描述符
    if ((fd = get_fd(SOCK_INODE(newsock))) < 0) 
    {
        sock_release(newsock);
        return(-EINVAL);
    }
    // 是否需要获取socket对应的地址
    if (upeer_sockaddr)
    {
        newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 1);
        move_addr_to_user(address,len, upeer_sockaddr, upeer_addrlen);
    }
    return(fd);
}

accept函数主要的两个操作是

1 sock->ops->dup,该函数的是inet_create函数的封装,就是新建一个sock结构体并且和socket结构体互相关联,前面的文章已经分析过。

2 sock->ops->accept,该函数底层是inet_accept函数

代码语言:javascript
复制
static int inet_accept(struct socket *sock, struct socket *newsock, int flags)
{
    struct sock *sk1, *sk2;
    int err;
    sk1 = (struct sock *) sock->data;
    /*
     * We've been passed an extra socket.
     * We need to free it up because the tcp module creates
     * its own when it accepts one.
     */
    // 销毁新socket结构体中的socke结构体,d调用底层的accept会返回一个新的sock
    if (newsock->data)
    {
          struct sock *sk=(struct sock *)newsock->data;
          newsock->data=NULL;
          sk->dead = 1;
          destroy_sock(sk);
    }
    if (sk1->prot->accept == NULL) 
        return(-EOPNOTSUPP);
    /* Restore the state if we have been interrupted, and then returned. */
    if (sk1->pair != NULL ) 
    {
        sk2 = sk1->pair;
        sk1->pair = NULL;
    } 
    else
    {    // 返回一个新的sock结构体
        sk2 = sk1->prot->accept(sk1,flags);
        if (sk2 == NULL) 
        {
            if (sk1->err <= 0)
                printk("Warning sock.c:sk1->err <= 0.  Returning non-error.\n");
            err=sk1->err;
            sk1->err=0;
            return(-err);
        }
    }
    // 互相关联
    newsock->data = (void *)sk2;
    // 复制socket结构的wait字段,用于控制进程的阻塞和唤醒
    sk2->sleep = newsock->wait;
    sk2->socket = newsock;
    newsock->conn = NULL;
    if (flags & O_NONBLOCK) 
        return(0);
    cli(); /* avoid the race. */
    // sock是接收syn状态则阻塞当前进程
    while(sk2->state == TCP_SYN_RECV) 
    {
        interruptible_sleep_on(sk2->sleep);
        if (current->signal & ~current->blocked) 
        {
            sti();
            sk1->pair = sk2;
            sk2->sleep = NULL;
            sk2->socket=NULL;
            newsock->data = NULL;
            return(-ERESTARTSYS);
        }
    }
    sti();
    if (sk2->state != TCP_ESTABLISHED && sk2->err > 0) 
    {
        err = -sk2->err;
        sk2->err=0;
        sk2->dead=1;    /* ANK */
        destroy_sock(sk2);
        newsock->data = NULL;
        return(err);
    }
    // 设置sock为已经建立连接状态
    newsock->state = SS_CONNECTED;
    return(0);
}

可以看到inet_accept函数的两个核心逻辑是

1 sk1->prot->accept

2 interruptible_sleep_on

代码语言:javascript
复制
static struct sock *tcp_accept(struct sock *sk, int flags)
{
    struct sock *newsk;
    struct sk_buff *skb;
  /*
   * We need to make sure that this socket is listening,
   * and that it has something pending.
   */
    if (sk->state != TCP_LISTEN) 
    {
        sk->err = EINVAL;
        return(NULL); 
    }
    /* Avoid the race. */
    cli();
    sk->inuse = 1;
    // 从sock的receive_queue队列摘取已建立连接的节点,
    while((skb = tcp_dequeue_established(sk)) == NULL) 
    {    
        // 没有已经建立连接的节点,但是设置了非阻塞模式,直接返回
        if (flags & O_NONBLOCK) 
        {
            sti();
            release_sock(sk);
            sk->err = EAGAIN;
            return(NULL);
        }
        release_sock(sk);
        //阻塞进程,如果后续建立了连接,则进程被唤醒的时候,就会跳出while循环
        interruptible_sleep_on(sk->sleep);
        if (current->signal & ~current->blocked) 
        {
            sti();
            sk->err = ERESTARTSYS;
            return(NULL);
        }
        sk->inuse = 1;
      }
    sti();
    /*
     *    Now all we need to do is return skb->sk. 
     */
    // 拿到一个新的sock结构,由建立连接的时候创建的
    newsk = skb->sk;
    kfree_skb(skb, FREE_READ);
    // 队列节点数减一
    sk->ack_backlog--;
    release_sock(sk);
    // 返回新的sock结构体
    return(newsk);
}

interruptible_sleep_on的核心代码是

代码语言:javascript
复制
// struct wait_queue wait = { current, NULL };p就是socket中的wait,下面的代码就是把当前进程挂到wait队列里
if (!*p) {
    wait->next = wait;
    *p = wait;
} else {
    wait->next = (*p)->next;
    (*p)->next = wait;
}

上面的全部说明了如果调用accept的时候,如果已经有已经完成了连接的节点,则直接返回一个节点,否则就进入阻塞。

接下分析一下什么会被唤醒,在tcp_rcv中有一个分支

代码语言:javascript
复制
if(sk->state==TCP_SYN_SENT){
        if(th->ack)
        {
            if(!tcp_ack(sk,th,saddr,len))
            {
                //...
            }
        }
    }

tcp_ack中有一个分支

代码语言:javascript
复制
if(sk->state==TCP_SYN_RECV)
    {
        tcp_set_state(sk, TCP_ESTABLISHED);
        tcp_options(sk,th);
        sk->dummy_th.dest=th->source;
        sk->copied_seq = sk->acked_seq;
        if(!sk->dead)
            sk->state_change(sk);
        if(sk->max_window==0)
        {
            sk->max_window=32;    /* Sanity check */
            sk->mss=min(sk->max_window,sk->mtu);
        }
    }

其中state_change对应的代码是

代码语言:javascript
复制
static void def_callback1(struct sock *sk)
{
    if(!sk->dead)
        wake_up_interruptible(sk->sleep);
    // 获取sleep队列中的所有进程,sleep即socket的wait,即inode的wait
}

最后accept函数返回。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档