Chinaunix首页 | 论坛 | 博客
  • 博客访问: 16807
  • 博文数量: 7
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 30
  • 用 户 组: 普通用户
  • 注册时间: 2012-07-25 14:14
文章分类
文章存档

2014年(3)

2012年(4)

我的朋友

分类:

2012-07-25 14:16:53

转载自:

接着上篇的ip_fragment函数的分析,本人感觉里面有两个最麻烦的函数,他们是ip_options_fragment(),skb_copy_bits()。

第一个函数是为分段头部添加选项,由于选项中涉及很多的字段,我还没来得及仔细分析,目前就先不把结果给列出来了。。

skb_copy_bits()这个函数出现在分段中的slow_path部分,主要的功能是把原始的packet的依次到新的分段中,别看我只有这么一句话就表述了,但是他的过程是相当麻烦的,是分段中比较麻烦的过程。。

下面请看:

1182 /* Copy some data bits from skb to kernel buffer.
1183  * 2009 5.17开始分析此函数 */
1184 /*     
1185  * 传入的参数的含义:
1186  * skb:原始的数据区,即被复制的区域。
1187  * offset:复制的其实位置。
1188  * to:目标区域。
1189  * len:每次复制的数据长度。    
1190  * 注:对于此处的to,有一点很重要,就是to指向的每次在调用该函数后必须被复制满,否则就会出错。
1191  * 这一点尤其对于最后一个分段来说,相比较最后一个分段的to指向区域的大小会稍比前面的小,但依旧要
1192  * 被填满,否则会被报错。即在调用该函数的过程中遵循一条原则,分多大,填多少,必须填满!
1193  *                                                                          ----cdc 09.5.22
1194  */
1195 int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
1196 {  
1197     int i, copy;       /*copy指的是被复制数据的的长度*/
1198     int start = skb_headlen(skb);     /*
1199                                         返回由head指出的缓存大小(不包括内存页中的数据),对于纯slow_path,start的值是被复制的ip packet的大小
1200                                         亦可以
1201                                         是fast_path条件不完全满足的主skb的head指向的大小
1202                                       */
1203 /* 
1204  * 以下的len可以是主skb的len,可以是单个skb的len,亦可以是对于
1205  * fast_path条件不完全满足的主skb的len,该skb的len是所有的skb
1206  * (包括挂载在frag_list上的skb)的len之和
1207  *
1208  */
1209     if (offset > (int)skb->len - len)
1210         goto fault;
1211
1212     /* Copy header. */
1213     if ((copy = start - offset) > 0) {           /*判断数据是否复制完毕*/
1214         if (copy > len)
1215             copy = len;
1216         skb_copy_from_linear_data_offset(skb, offset, to, copy);   /*若剩余被复制的数据区大于mtu(len)的话,就复制数据到该指定的区域*/
1217         if ((len -= copy) == 0)/*
1218                                  若copy>=len的话,以上的代码执行完后就在此退出了函数;
1219                                  若copy1220                                 */
1221             return 0;
1222         /*
1223          * copy1224          * 地未满,下面开始用frags[]里的内容来填充当前的skb
1225          */
1226         offset += copy;/*计算偏移量*/
1227         to     += copy;/*指向即将被内存页中的数据填充的区域*/
1228
1229 /*开始用frags[]填充*/
1230     for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
1231         int end;
1232
1233         WARN_ON(start > offset + len);
1234
1235         end = start + skb_shinfo(skb)->frags[i].size;/*复制区域的总长度*/
1236         /*子复制第一个页面的内容时offset=start*/
1237         if ((copy = end - offset) > 0) {/*copy指的是当前的页面内容的大小*/
1238             u8 *vaddr;/*页面的虚拟地址*/
1239 /*若页面的内容大于在slow_path中对len的定义,将页面的内容复制到目的skb中*/
1240             if (copy > len)
1241                 /*
1242                  * 若存在to的区域未被复制满的话,此时的len已经变成了,剩下未被复制
1243                  * 区域的长度,而并非是传入整个函数的原始len,因为在在最开始复制缓存区域的时候执行了:
1244                  *if ((len-=copy)==0)这段判断代码,此时的len的值已经发生改变,即to所指的skb中剩余区域长度
1245                  */
1246                 copy = len;
1247
1248             vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[i]);/*当前被页面的虚拟地址*/
1249             memcpy(to,
1250                    vaddr + skb_shinfo(skb)->frags[i].page_offset+
1251                    offset - start, copy); /*此处若是复制的是第一个页面,offset=start*/
1252             kunmap_skb_frag(vaddr);
1253
1254             if ((len -= copy) == 0)/*若此时的skb被填充满,则拷贝成功,退出该函数*/
1255                 return 0;
1256             /*若skb依然未被填充满*/
1257             offset += copy;
1258             to     += copy;
1259         }
1260         start = end;/*赋值,改变被复制的数据长度*/
1261     }/*页面填充循环结尾*/
1262 /*
1263  * 以下是针对fast_path判断条件不满足,需进行slow_path的方式
1264  * 若前一个skb的数据被复制完毕后,仍无法填充满当前to指向的复制
1265  * 目的地skb的话,就用后续的frag_list指向的后续的skb来填充
1266  */
1267     if (skb_shinfo(skb)->frag_list) {
1268         struct sk_buff *list = skb_shinfo(skb)->frag_list;
1269
1270         for (; list; list = list->next) {
1271             int end;
1272
1273             WARN_ON(start > offset + len);
1274 /*注意此处的len和主skb的len在概念上是不一样,他只代表当前skb的缓存+内存页*/
1275             end = start + list->len;
1276             if ((copy = end - offset) > 0) {
1277                 if (copy > len)
1278                     copy = len;/*注意此处的len也类似在页面复制,也是由于每次的复制完毕后的判
1279                                  断语句if ((len -= copy)==0),而使len的值发生改变
1280                                 */
1281                 if (skb_copy_bits(list, offset - start,
1282                           to, copy))/*此处是函数的递归调用,若当前的to指向的skb区域被填充满的话,函数返回0*/
1283                     goto fault;
1284                 if ((len -= copy) == 0)/*判断是否已经填充满*/
1285                     return 0;
1286                 offset += copy;
1287                 to     += copy;
1288             }
1289             start = end;         /*end,已经被复制的数据总长度,start的意思是下次复制的起始长度*/
1290         }/*for循环结尾*/
1291     }/*frag_list遍历结尾*/
1292     if (!len)   /*
1293                   若最后一个skb无法填满,即len>0,则说明在为skb分配空间的时候出错,
1294                   skb的空间分配大了。这样的话,收端在检查的时候就会把最后一个分段
1295                   丢弃,这样的话将导致在收端无法完成重组,数据发送失败
1296                  */
1297         return 0;
1298
1299 fault:     /*若最后一个的skb无法填满,即len>0的话,出错*/
1300     return -EFAULT;
1301 }/*skb_copy_bits函数结束。2009 5.19*/


阅读(539) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:Linux内核sk_buff结构分析

给主人留下些什么吧!~~