Chinaunix首页 | 论坛 | 博客
  • 博客访问: 303125
  • 博文数量: 94
  • 博客积分: 2163
  • 博客等级: 大尉
  • 技术积分: 932
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-20 09:23
文章存档

2012年(2)

2011年(92)

分类: LINUX

2011-10-21 18:45:22

                        PS:期望值:这里给出重要的函数分支=(我认为比较重要会给出解释)


首先是总体的调用顺序:(从用户通过tcp/协议进入物理层。当然还有udp,icmp等,以后再说。)(用户程序)read()-> sys_read()->sock_read()->inet_read()->tcp_read();先说到这里。
 从sock_read()开始
调用接口函数为 static int sock_read(struct inode* inode, struct file *file, char *ubuf, int size)
{
   1、sock = socki_loopup(indoe) ;  /*这个根据节点找到对应socket结构的指针,如果感兴趣见前面对从内存到文件系统的分析 (文件我已经写好),最重要的一部分*/
  其他的是对sock的属性的设置;细节必须自己看
  2、verify_arae(VERIFY_WRITE, ubuf, size)/*给内核提供写时赋值的渠道*/
   3、sock->ops->read(sock, ubuf, size, (file->f_flags & O_NONBLOCK)) /*这里就是调用inet_read()*/
}
这个结构来自这里: /*sock->ops为struct proto_ops结构,因为是inet域,sock->ops被赋值为inet_proto_ops。如果是unix域,就被赋值为unix_proto_ops类似的。
static struct proto_ops inet_proto_ops ={
 AF_INET,
  inet_create,
 . . .
 inet_read,
 . . .
}

所以后面调用的是inet_read()函数:
static int inet_read(struct socket *sock, char *ubuf, int size, int noblock) /*noblock,发生阻塞时是否立即返回, *ubuf指向用户内存的地址,int size,读取的大小,sock 对应的接口文件*/
{
   struct sock *sk = (struct sock *) sock->data;  /*data域一般都是存放里面的小的重要的数据结构类型,比如sokect->data 是sock 类型,而sock->data 就是sk_buff类型,真方便*/
  1、inet_autobind(sk) /*这个函数为sk结构分配一个端口号*/
  2、 sk->prot->read(sk, (unsigned char *)ubuf, size, noblock, 0);
}
先说inet_autobind:
static inet_auto_bind(struct sock *sk)
{
   . . .
   1.1、sk->num = get_new_socknum(sk->prot, 0); /*这里真正是分配端口号的函数*/
   1.2 put_sock(sk->num, sk);  /*这里是将sock结构的sk,送入其对应协议的端口号队列,sk->num是大于1024的*/
   . . .
}

/*分配规则:就是在这256个数组队列中,找到数目最少的sock结构进行分配*/


1.1         unsigned short get_new_socknum(struct proto *prot, unsigned short base)
{
   . . . . .
  {
   
if (base == 0) base = PROT_SOCK+1+(start & 1023); /*书中原代有错误,此来自net/ipv4/af_inet.c*/ if (base <= PROT_SOCK) { base += PROT_SOCK+(start & 1023); /* }   /*PROT 为 1024*/
  }
   for (i = 0; i< SOCK_ARRAY_SIZE; i ++) /*SOCK_ARRAY_SIZE 为256; i最大值为 255 */
     {
                  . . . .
         sk = prot->sock_array[(i + base + 1)  &(SOCK_ARRAY_SIZE - 1)]; /*proto结构中有个sock_array是存储本协议的sock队列的*/
      while(sk != NULL)
 {
   sk = sk->next;
   j ++; /*j是计算在本队列中连续个sock的个数,因为入队列的时候也是连续的,所以这就是本队列中所有的sock个数*/
   }
 if (j == 0)
 {
   start = (i + 1 + start) &1023 /*程序开头定义static start  = 0;这里出现这个我很无奈,不清楚*/
   return (i +base + 1); /*返回一个那个从base开始的第一个端口*/
  }
  . . . . .
  }
 }

 1.1.1   while (sk_inuse(prot, base+best + 1))/这里调用来确认这个端口没有被使用*/
  {
   bset = SOCK_ARRAY_SIZE;
  }
  return (best + base + 1);
/*这里是调用的函数*/
 
 1.1.1   static sk_inuse(struct proto *prot, int num)
{
          for(sk = prot->sock_array[num &(SOCK_ARRAY_SIZE - 1)]; sk != NULL; sk = sk->next)
           {
                      if (sk->num = num)
                       return (1);     /*此sock结构已经使用了这个端口号,返回1,这里可以如果是不同协议的话,可以有相同的端口号,但是相同协议下,却不可以。想想也是*/
               }
   return (0);/*如果确实没有使用,那么就返回0*/
}

1.2
   void put_sock(unsigned short num, struct sock *sk)
{
        struct sock *sk1;
        struct sock *sk2; /*这里定义两个sock*结构为了让sk进入队列任意两个结构之间,后面见到*/
        . . . .
        num = num &(SOCK_ARRAY_SIZE - 1); /*因为数组中队列最大下标为 255。*/
        save_flags(flags); /*保存当前的falgs,为了禁止中断*/
        sk->prot->inuse += 1;/*sk->prot使用计数加1*/
      

= 0xff000000; != 0xffffffff; = ( >> 8) | )
& ->) && (mask & sk->saddr) != (mask & 0xfffffff)  /*这里是算有效位(1)的数目,以8位为一个段,目前我还没有弄明白作者的深意*/
= << 8;
       }/*这里判断用于此ip地址的相应mask,为了使用利用前面的1的数量来选择位置*/
       sk1 = sk->prot->sock_array[num];/*取当前的第一个sock结构,用其中的saddr来和新的sk->addr进行比较*/
       for(sk2 = sk1; sk != NULL; sk2 = sk2->next)
    {              
          if(!(sk2->saddr &mask)) /*sk->saddr 和sk 的mask是对应的*/
               if(sk == sk1)
               {
                  sk->next = sk->prot_array_array[num];
                  sk->prot-sock_array[num] = sk;
                  sti();
                  return ;
                }
           . . . . . . /*后面就是插入两个sock之间的操作*/
     }
}
 举例:函数中实现的插入顺序有效位(即非零位)从多到少排列的,如
10.16.1.23 排在 10.16.1.0 之前,10.16.1.0 排在 10.16.0.0 之前,依次类推。


2、这里为tcp_read了。
static int tcp_read(struct sock *sk, unsigned char *to, int len, int nonblock, unsigned flags)
{
   struct wait_queue wait = {current, NULL};/*这是等待进程初始化为current当前进程*/
   其他的一些情况判断:
   比如if (sk->state == TCP_LISTEN) /*侦听套结字不负责数据传送,其receive_queue缓存的均是请求连接(数据包(SYN数据包),如果读取的套结字状态是侦听,说明用户调用出现了问题*/
    if(flags &MSG_OOB) /*如果请求读紧急数据,就返回读取紧急函数读取值*/
   
 1.2.1   return tcp_read_urg(sk, nonblock, to , len, flags);
   
  /*分配要更新的变量,如果仅仅是PEEK预处理,不对内核变量更新*/
   peek_seq = sk->copied_seq;
   seq = &sk->copied_seq;
   if (flags &MSG_PEEK)
        seq = &peek_seq;
  /*将前面的wait变量加到sock结构的sleep睡眠队列中,下文代码随时可以进入睡眠等待状态*/
   add_wait_queue(sk->sleep, &wait);
  
   while(len > 0) /*len 表示用户需要读取的数据长度,满足就返回*/
  {
     /*如果有紧急数据,立即跳出循环*/
     if (copied && sk->urg_data && sk->urg_seq == *seq) /*
                   break;
     current->state = TASK_INTERRUPTABLE; /*程序为可中断状态,条件不满足时,可以随时进入睡眠*  skb = skb_peek(&sk->receive_queue); /*将接受队列中的下一个sk_buff结构取出来,为NULL,是返回NULL*/
         do{
           if(!skb)
             break; /*如果为NULL,中断do .. while循环*/
         if (before(*seq, skb->h.th->seq))
                     break;              /*要读取的序列号在当前接受到的序列号前,说明出现了点问题*/
           offset = *seq - skb->h.th->seq; /*读取的序列号和当前sk_buff序列号中相对偏移*/
                if(skb->h.th->syn)
                     offset -- ; /*如果sk_buff是个syn数据包,不算入偏移*/
                if(offset < skb->len)
                   goto found_ok_skb; /*如果offset(序列号) 小于skb->len的长度,那么表示skb所表示的数据包中有可用数据*/
                if(skb->h.th->fin);
                  goto found_fin_ok;  /*跳转到fin包处理;
                if(!flags &MSG_PEEK)) /*PEEK表示预处理,不做处理*/
                    skb->used = 1;   /*skb->used置一,表示数据包已作处理*/
                   skb = skb->next;
                 }while(skb != (struct sk_buff *)&sk->receive_queue); /*将sk->receive中的sk_buff都处理完*/
                 if (copied) /*如果拷贝了数据,就跳出最外层while循环*/
                     break; 
              . . . . . .
              一些判断
         

  1.2.2           cleanup_rbuf(sk);
  1.2.3           release_sock(sk);
                 sk->sockect->flags |= SO_WAITDATA;
  /*这个不算*/           schedule(); /*进程调用,前面本进程已经设置为TASK_INTERRUPTABLE状态了*/
                   sk->inuse = 1;
                   if (current->signal &~current->blocked)
                  {         
                        copied = -ERESTARTSYS;
                        break;
                    }
                continue;
        }
     found_ok_skb:
                      skb->users ++; /*防止内核其他部分将其释放*/
          used = skb->len - offset; /*sk->len表示数据包长度,offset表示可读取的数据的起始偏移量,相减得到:used 表示可读取的数据字节数*/
          if (len < used)  /*上层要读取的小于此处提供的,那么只读取要求的数量*/
                 used = len; /*被初始化本次需要读取的字节数*/
           if(sk->urg_data)/*如果包含紧急数据,此版本紧急数据为 一个字节*/
           unsigned long urg_offset = sk->urg_seq - *seq;
            if {. . . . .)
                       
                 else
                      used = urg_offset;/*如果普通数据中有紧急数据,那么不能越过此进行处理*/
         memcpy_tofs(to, ((unsigned char *)skb->h.th) + skb->h.th->doff * 4 +offset, used);/*拷贝到用户空间*/
           从tcpheadr + 本身长度(skb->h.th->doff * 4(头部以4字节为一个长度单位)+offset(此为可读取的偏移地址),used表示数据的长度,如果有紧急数据,此长度就是紧急数据长度*/
           . . . . .
           found_fin_ok:
               ++ *seq; /*fin数据包只占一个序列号*/
       }end of the while
    remove_wait_queue(sk->sleep, &wait);
      current->state = TASK_RUUNING;  /*将本进程从睡眠队列中删除,被重新设置成可运行状态*/
       cleanup_rbuf(sk);
       release_sock(sk);
        return copied; /*copied 记录拷贝了多少字节的值*/
 }


1..2.1
static int tcp_read_urg(struct sock *sk, int nonblock, unsigned char *to, int len, unsigned flags)
{
     . . . . . ./*紧急数据为一个字节,所以用char c来接受*/
       char c = sk->urg_data;
       put_fs_byte(c, to);  /*将c放入to的用户空间*/
1.2.1.1       release_sock(sk);
       return 1;
      . . . .. .
}   
1.2.2:
最后一个函数:
void release_sock(struct sock*sk) /*将读取的那个sock结构对应在sk->back_log中sk_buff都读到receive_queue中,正好继续来处理*/
{
  . . . . . .
    while(skb = skb_dequeue(&sk->back_log)) ! = NULL)
       sk->blog = 1;
       if (sk->prot->rcv)
1.2.2.1             sk->prot->rcv(skb, skb->dev, skb->opt, skb->saddr, skb->len, skb->daddr, 1, (struct
inet_protocol *)sk->pair); /*此函数的作用调用get_sock()函数和tcp_check函数(此函数用户计算校验和)。
 . . . . . .
  if (sk->dead &&sk->state == TCP_CLOSE)
     {
 1.2.2.2         reset_timer(sk, TIME_DONE, min(sk->rtt*2, TCP_DONE_TIME);/*如果sk->state为关闭状态,设定时器*/
}
}                
 
1.2.2.1:
           
int tcp_rcv(strcut sk_buff *skb, struct device *dev, struct options *opt, unsigned long daddr, unsigned short len, unsigned long saddr, int redo, struct inet_protocol *protocol)
{
 skb : 为被接受的数据包,dev:为接受此数据包的设备:IP选项:
len为:IP负载长度;
redo:  0:表示一个新的数据包,redo为1,表示缓存的;
 if (!redo)
 {
           if(tcp_check(th, len ,addr, daddr)) /*校验,返回0为成功*/
                    kfree_skb(skb, FREE_READ); /*释放kmalloc分配的空间*/
                if (sk == NULL)
 1.2.2.1.1         tcp_reset(daddr, saddr, th, &tcp_prot, opt, dev, skb, skb->ip_hdr->tos, 255);/*发送一个RST复位数据包,使对方断开连接,如果对方还连接,对方再次发出connect;*/
         if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf)
{
                kfree_skb(skb, FREE_READ);/*如果接受缓冲区剩余空间小,就丢包*/
                relese_sock(sk); /*这里将可以传送进receive_queue就送进来*/
                 return (0); /*返回/*
 
           根据sock的一系列属性,进行对应操作比如:
            如果是个
             if(sk->state = TCP_LISTEN)
                   tcp_conn_request(sk, skb, daddr, saddr, opt,dev, tcp_init_seq())/*这个函数完成新通信套结字的创建和初始化工作,返回后请求数据包的发送以及后面的操作由此套结字负责,侦听套结字只负责第一个SYN数据包。*/
            /*创建一个新套结字状态设置为TCP_SYN_RECV,并将此sock通过put_sock()送入相关队列,下一次处理时,查找的将是这个新的套结子*/
              }
              if(sk->state == TCP_SYN_SENT)/*本地(客户端发出SYN信号,用于通信开始)
           {
              if(th->ack)
              {
   1.2.2.1.2             if(!tcp_ack(sk, th, addrlen, len)) /*
                     {
                          tcp_statistics.TcpAttemptFails ++;
                        . . . . . .
                   }
                 if(th->rst)
                       return tcp_std_reset(sk, skb); 
                      
 }

 1.2.2.1.2:  extern __inline__ int tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr,
int len)           
 {
                      if(sk->zapped) /*此字段为1表示该套结字之前收到远端的RST数据包*/
                          return (1); 
                     ack = ntohl(th->ack_seq); /*期望的下一个序列号*/
                  . . . . . .
                  开启定时器,并发送,比较长。*/
                                 
}
                 
阅读(2705) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~