专栏首页原创分享linux1.2.13源码中,管理sock结构体的数据结构及操作函数

linux1.2.13源码中,管理sock结构体的数据结构及操作函数

tcp和udp等协议在传输层都对应一个sock结构,该结构是实现协议的重要结构体,而传输层实现的就是对该结构体的管理。利用一个哈希链表根据端口号保存sock结构体。有了保存sock结构的数据结构后,还需要一系列的操作函数。代码如下。

/*
 *    See if a socket number is in use.
 */
// 看socket的端口是否在使用 
static int sk_inuse(struct proto *prot, int num)
{
    struct sock *sk;
    // 根据端口号取得哈希链表中的一个链表
    for(sk = prot->sock_array[num & (SOCK_ARRAY_SIZE -1 )];
        sk != NULL;  sk=sk->next) 
    {
        if (sk->num == num) 
            return(1);
    }
    return(0);
}


/*
 *    Pick a new socket number
 */
// 随机获取一个端口
unsigned short get_new_socknum(struct proto *prot, unsigned short base)
{
    static int start=0;

    /*
     * Used to cycle through the port numbers so the
     * chances of a confused connection drop.
     */

    int i, j;
    int best = 0;
    int size = 32767; /* a big num. */
    struct sock *sk;
    // 大于1024
    if (base == 0) 
        base = PROT_SOCK+1+(start % 1024);
    if (base <= PROT_SOCK) 
    {
        base += PROT_SOCK+(start % 1024);
    }

    /* Now look through the entire array and try to find an empty ptr. */
    for(i=0; i < SOCK_ARRAY_SIZE; i++) 
    {
        j = 0;
        // 找到一条链表
        sk = prot->sock_array[(i+base+1) &(SOCK_ARRAY_SIZE -1)];
        // 找到链表中的最后一个节点
        while(sk != NULL) 
        {
            sk = sk->next;
            j++;
        }
        // 该链表上还没有节点,说明这个端口还没有被使用过,返回该端口号,更新start变量
        if (j == 0) 
        {
            start =(i+1+start )%1024;
            return(i+base+1);
        }
        /*
            j为本次循环的队列的节点数,best记录新端口所属队列的索引,
            size为本次循环为止节点数最少的队列的节点数,为了避免单个队列过长,
            找可用端口的时候,不仅要找到一个可用的端口,而且尽量保证端口所对
            应的队列不会过长,避免查找的时候比较慢,所以for循环是为了找出哈希链表
            中节点数最少的队列对应的索引。然后往该队列插入一个新的端口节点
        */
        if (j < size) 
        {
            best = i;
            size = j;
        }
    }

    /* Now make sure the one we want is not in use. */
    // 在一条队列中找到一个未使用的端口号,SOCK_ARRAY_SIZE保证哈希后对应的是同一个队列
    while(sk_inuse(prot, base +best+1)) 
    {
        best += SOCK_ARRAY_SIZE;
    }
    return(best+base+1);
}

/*
 *    Add a socket into the socket tables by number.
 */

void put_sock(unsigned short num, struct sock *sk)
{
    struct sock *sk1;
    struct sock *sk2;
    int mask;
    unsigned long flags;

    sk->num = num;
    sk->next = NULL;
    num = num &(SOCK_ARRAY_SIZE -1);

    /* We can't have an interrupt re-enter here. */
    save_flags(flags);
    cli();
    // 使用的socket数
    sk->prot->inuse += 1;
    // 最多使用的socket数
    if (sk->prot->highestinuse < sk->prot->inuse)
        sk->prot->highestinuse = sk->prot->inuse;
    // 链表为空,sk成为第一个节点
    if (sk->prot->sock_array[num] == NULL) 
    {
        sk->prot->sock_array[num] = sk;
        restore_flags(flags);
        return;
    }
    restore_flags(flags);
    // mask为0xff000000 => 0xffff0000 => 0xffffff00 => 0xffffffff
    for(mask = 0xff000000; mask != 0xffffffff; mask = (mask >> 8) | mask) 
    {
        if ((mask & sk->saddr) &&
            (mask & sk->saddr) != (mask & 0xffffffff)) 
        {
            mask = mask << 8;
            break;
        }
    }
    cli();
    // 根据端口找到对应的链表,找到对应的位置插入队列
    sk1 = sk->prot->sock_array[num];
    for(sk2 = sk1; sk2 != NULL; sk2=sk2->next) 
    {
        if (!(sk2->saddr & mask)) 
        {
            if (sk2 == sk1) 
            {
                sk->next = sk->prot->sock_array[num];
                sk->prot->sock_array[num] = sk;
                sti();
                return;
            }
            sk->next = sk2;
            sk1->next= sk;
            sti();
            return;
        }
        sk1 = sk2;
    }

    /* Goes at the end. */
    sk->next = NULL;
    sk1->next = sk;
    sti();
}

/*
 *    Remove a socket from the socket tables.
 */

static void remove_sock(struct sock *sk1)
{
    struct sock *sk2;
    unsigned long flags;

    if (!sk1->prot) 
    {
        printk("sock.c: remove_sock: sk1->prot == NULL\n");
        return;
    }

    /* We can't have this changing out from under us. */
    save_flags(flags);
    cli();
    sk2 = sk1->prot->sock_array[sk1->num &(SOCK_ARRAY_SIZE -1)];
    // 是队列的第一个节点
    if (sk2 == sk1) 
    {
        sk1->prot->inuse -= 1;
        sk1->prot->sock_array[sk1->num &(SOCK_ARRAY_SIZE -1)] = sk1->next;
        restore_flags(flags);
        return;
    }
    // 找sk1
    while(sk2 && sk2->next != sk1) 
    {
        sk2 = sk2->next;
    }
    // 找到
    if (sk2) 
    {
        sk1->prot->inuse -= 1;
        sk2->next = sk1->next;
        restore_flags(flags);
        return;
    }
    restore_flags(flags);
}

/*
 *    Destroy an AF_INET socket
 */

void destroy_sock(struct sock *sk)
{
    struct sk_buff *skb;

      sk->inuse = 1;          /* just to be safe. */

      /* In case it's sleeping somewhere. */
      if (!sk->dead) 
          sk->write_space(sk);

      remove_sock(sk);

      /* Now we can no longer get new packets. */
      delete_timer(sk);
      /* Nor send them */
    del_timer(&sk->retransmit_timer);

    while ((skb = tcp_dequeue_partial(sk)) != NULL) {
        IS_SKB(skb);
        kfree_skb(skb, FREE_WRITE);
    }

    /* Cleanup up the write buffer. */
      while((skb = skb_dequeue(&sk->write_queue)) != NULL) {
        IS_SKB(skb);
        kfree_skb(skb, FREE_WRITE);
      }

      /*
       *  Don't discard received data until the user side kills its
       *  half of the socket.
       */

    if (sk->dead) 
    {
          while((skb=skb_dequeue(&sk->receive_queue))!=NULL) 
          {
        /*
         * This will take care of closing sockets that were
         * listening and didn't accept everything.
         */
            // 处理listen型的socket,监听套接字接收队列里的skb关联的sock结构是一个新建的而不是sk
            if (skb->sk != NULL && skb->sk != sk) 
            {
                IS_SKB(skb);
                skb->sk->dead = 1;
                // 关闭连接
                skb->sk->prot->close(skb->sk, 0);
            }
            IS_SKB(skb);
            kfree_skb(skb, FREE_READ);
        }
    }   

    /* Now we need to clean up the send head. */
    cli();
    // 清空为了重传而缓存的数据包
    for(skb = sk->send_head; skb != NULL; )
    {
        struct sk_buff *skb2;

        /*
         * We need to remove skb from the transmit queue,
         * or maybe the arp queue.
         */
        if (skb->next  && skb->prev) {
/*            printk("destroy_sock: unlinked skb\n");*/
            IS_SKB(skb);
            skb_unlink(skb);
        }
        skb->dev = NULL;
        // unlink后link3指针仍然指向下一个skb节点
        skb2 = skb->link3;
        kfree_skb(skb, FREE_WRITE);
        skb = skb2;
    }
    sk->send_head = NULL;
    sti();

      /* And now the backlog. */
    // 还没来得及移到receive_queue队列的而缓存在back_log队列的skb
      while((skb=skb_dequeue(&sk->back_log))!=NULL) 
      {
        /* this should never happen. */
/*        printk("cleaning back_log\n");*/
        kfree_skb(skb, FREE_READ);
    }

    /* Now if it has a half accepted/ closed socket. */
    if (sk->pair) 
    {
        sk->pair->dead = 1;
        sk->pair->prot->close(sk->pair, 0);
        sk->pair = NULL;
      }

    /*
     * Now if everything is gone we can free the socket
     * structure, otherwise we need to keep it around until
     * everything is gone.
     */

      if (sk->dead && sk->rmem_alloc == 0 && sk->wmem_alloc == 0) 
      {
        kfree_s((void *)sk,sizeof(*sk));
      } 
      else 
      {
        /* this should never happen. */
        /* actually it can if an ack has just been sent. */
        sk->destroy = 1;
        sk->ack_backlog = 0;
        sk->inuse = 0;
        reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME);
      }
}


struct sock *get_sock(struct proto *prot, unsigned short num,
                unsigned long raddr,
                unsigned short rnum, unsigned long laddr)
{
    struct sock *s;
    struct sock *result = NULL;
    int badness = -1;
    unsigned short hnum;

    hnum = ntohs(num);

    /*
     * SOCK_ARRAY_SIZE must be a power of two.  This will work better
     * than a prime unless 3 or more sockets end up using the same
     * array entry.  This should not be a problem because most
     * well known sockets don't overlap that much, and for
     * the other ones, we can just be careful about picking our
     * socket number when we choose an arbitrary one.
     */

    for(s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
            s != NULL; s = s->next) 
    {
        int score = 0;

        if (s->num != hnum) 
            continue;

        if(s->dead && (s->state == TCP_CLOSE))
            continue;
        /* local address matches? */
        if (s->saddr) {
            if (s->saddr != laddr)
                continue;
            score++;
        }
        /* remote address matches? */
        if (s->daddr) {
            if (s->daddr != raddr)
                continue;
            score++;
        }
        /* remote port matches? */
        if (s->dummy_th.dest) {
            if (s->dummy_th.dest != rnum)
                continue;
            score++;
        }
        /* perfect match? */
        // 全匹配,直接返回
        if (score == 3)
            return s;
        /* no, check if this is the best so far.. */
        if (score <= badness)
            continue;
        // 记录最好的匹配项
        result = s;
        badness = score;
      }
      return result;
}

协议每次新建一个socket的时候就会在底层生成一个sock结构体,然后插入大到哈希链表中,收到数据时候根据ip和端口从哈希链表中找到对应的sock结构体。

本文分享自微信公众号 - 编程杂技(theanarkh)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-02-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 网络编程函数socket源码分析

    theanarkh
  • tcp紧急数据处理源码浅析

    tcp紧急数据用于一端有紧急通知需要告之对端的时候,他传输的其实是一种命令或者说信号,而不算是数据,因为他只有一个字节。对端收到紧急数据后会给对应的进程发送一个...

    theanarkh
  • UDP协议源码解析之接收

    调用读取数据的函数前,我们要先调用bind绑定socket对应的地址信息,因为系统是根据地址和端口去查找一个socket的。由代码可以知道,入口没有什么逻辑,主...

    theanarkh
  • 网络编程函数socket源码分析

    theanarkh
  • Linux内核UDP收包为什么效率低?能做什么优化?

    现在很多人都在诟病Linux内核协议栈收包效率低,不管他们是真的懂还是一点都不懂只是听别人说的,反正就是在一味地怼Linux内核协议栈,他们的武器貌似只有DPD...

    IT大咖说
  • 曾经那些坑之第三方sdk的引入import <> 引用编译器的类库路径下的头文件import “” 引用工程目录的相对路径的头文件

    rectinajh
  • 【Python】从C++/Java到Python入门(2)

    1.元组(tuple)类似于列表(list),但是其元素不可修改,所以相比列表有更好的安全性。

    ZifengHuang
  • 万事开头难!入门TensorFlow,这9个问题TF Boys必须要搞清楚

    作为目前最普及的深度学习框架,TensorFlow 实不必多做介绍。 无论国内国外,有相当数量的程序员以 TensorFlow 入门深度学习开发,逐步走上职业机...

    AI研习社
  • leetcode: 52. N-Queens II

    JNingWei
  • easyswoole一键安装脚本,宝塔安装错误

    本人作为easyswoole开发组组员之一。为生态的完善和偷懒着想,在某一天讨论中就开始有了这个想法。

    宣言言言

扫码关注云+社区

领取腾讯云代金券