章节:
一. Alan Cox 文章的翻译和补充
二. 报文是如何从网卡传递到相应协议的
linux的网络设计和实现
=====================
主要参考:
UNIX高级教程 系统技术内幕
(USA) Uresh Vahalia 著
聊鸿斌 曲广之 王元鹏 等译
清华大学出版社
//该书内容极其丰富, 强力推荐, 实是不可不读
Network Buffers And Memory Management(in KHG)
by Alan Cox
他是linux 内核的主要维护者, 也写了大量的程式,
在源程式中他的名字到处可见.
linux的源程式并没有想象中的难读.
一般的说法是linux不如FreeBSD的网络功能稳定. 可惜, 我不懂FreeBSD.
假如有人理解这一点, 请于我们联系, 谢谢!
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
章节:
一. Alan Cox 文章的翻译和补充
二. 报文是如何从网卡传递到相应协议的
一. Alan Cox 文章的翻译和补充
-----------------------------
linux中的网络层设计是一种面向对象的设计结构.
一些key:
Device or Interface:
一个网络接口是能够接收或发送packet. 包括, 网卡和lo.
Protocol:
每一种协议描述了一种网络语言. 在linux中, 一个协议是一组代码向socket层提
供服务.
Socket:
一个socket是个网络连接, 向用户程式提供类似于文档I/O风格的服务. 在
kernel中, 个socket是一对结构描述了高层的socket接口和低层的socket接口.
sk_buff:
任何网络层使用的buffer都是sk_buffs. 由于这种统一性, 存在一组例程为整个网
络层服务. sk_buffs为网络层提供了通用的缓冲和流控机制.
一个sk_buff带有一块内存的控制结构. 他有两组例程库. 第一组用于操纵由
sk_buffs组成的双向链表, 第二组用于操纵其控制的内存. 这种双向链表按一般网络操作的
方式进行优化, 通常是从表头取数据向表尾加数据.
下面是个例子:
void append_frame(char *buf, int len)
{
struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC);
if (skb == NULL)
my_dropped ;
else {
skb_put(skb, len);
memcpy(skb->data, data, len);
skb_append(&my_list, skb);
}
}
void process_queue(void)
{
struct sk_buff *skb;
while ((skb = skb_dequeue(&my_list)) != NULL) {
process_data(skb);
kfree_skb(skb, FREE_READ);
}
}
append_frame()有点象网卡驱动程式通过中断来取得一个报文.
process_frame()有点象将报文传递给相应的协议.
在 net/core/dev.c 中的 netif_rx() 和 net_bh() 中也有类似的程式, 由于他们需要处理
将报文正确投递和流控所以代码较复杂.
skb_put() 增长数据区的长度来为memcpy准备空间. 许多的网络操作需要加入一些桢头, 这
能够使用skb_push来将数据区向后推, 为头留出空间.
请参见下图:
----------------------------------------
| head | data | |
----------------------------------------
skb_put
-----------------------------------------
| head | data | put_data | |
-----------------------------------------
skb_push
------------------------------------------
| head | push_data | data | put_data | |
------------------------------------------
更有一个函数skb_reserve()用于在添加数据前, 移动头指针来保留空间.
参见下面的例程:
skb = alloc_skb(len headspace, GFP_KERNEL);
skb_reserve(skb, headspace);
skb_put(skb,len);
memcpy_fromfs(skb->data, data, len);
pass_to_m_protocol(skb);
看一看下面的函数:
o skb_dequeue()
struct sk_buff *__skb_dequeue(struct sk_buff_head *list);
用于从一个链表中取得数据, 第一个参数为一个链表. 能够使用
skb_queue_head 和 skb_queue_tail 来向链表中添加数据.
o skb_queue_head()
void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk);
用于将一个缓冲区放到链表的开头.
o skb_queue_tail()
void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk);
用于将一个缓冲区放到链表的结尾.
o skb_unlink()
void skb_unlink(struct sk_buff *skb);
从链表中删除一个缓冲区无论他在那个链表中. 该缓冲区并不被释放掉.
o skb_insert skb_append
void skb_insert(struct sk_buff *old, struct sk_buff *newsk);
void skb_append(struct sk_buff *old, struct sk_buff *newsk);
由于一些网络协议需要对他们的数据进行排序, 这两个函数是将一个新包加在某个
包的前面或后面.
o alloc_skb()
#define alloc_skb(size, priority)
(struct sk_buff *) kmalloc(size,priority)
用于创建一个新的sk_buff. 通常作如下的操作 : skb->free=1; 表明他是自由的.
o kfree_skb()
void kfree_skb(struct sk_buff *skb);
用于释放一个sk_buff.
o skb_clone()
struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask);
作一份sk_buff的拷贝, 但并不拷贝数据区.
o skb_copy()
int skb_copy_datagram(struct sk_buff *skb, int offset,
char *to, int size);
拷贝一个sk_buff的内容.文章整理:西部数码--专业提供域名注册、虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!




