📄 10.html
字号:
inline void outb(char value, unsigned short port);<br>
inline void outb_p(char value, unsigned short port);<br>
在include/adm/io.h里定义。<br>
inb_p()、outb_p()与inb()、outb_p()的不同在于前者在存取I/O时有等待(pause)一适应慢速的I/O设备。<br>
为了防止存取I/O时发生冲突,Linux提供对端口使用情况的控制。在使用端口之前,可以检查需要的I/O是否正在被使用,如果没有,则把端口标记为正在使用,使用完后再释放。系统提供以下几个函数做这些工作。<br>
int check_region(unsigned int from, unsigned int extent);<br>
void request_region(unsigned int from, unsigned int extent,const char *name)<br>
void release_region(unsigned int from, unsigned int extent);<br>
其中的参数from表示用到的I/O端口的起始地址,extent标明从from开始的端口数目。name为设备名称。<p>
2.4.5 中断打开关闭<br>
系统提供给驱动程序开放和关闭响应中断的能力。是在include/asm/system.h中的两个定义。<br>
#define cli() __asm__ __volatile__ ("cli"::)<br>
#define sti() __asm__ __volatile__ ("sti"::)<p>
2.4.6 打印信息<br>
类似普通程序里的printf(),驱动程序要输出信息使用printk()。在include/linux/kernel.h里声明。<br>
int printk(const char* fmt, ...);<br>
其中fmt是格式化字符串。...是参数。都是和printf()格式一样的。<p>
2.4.7 注册驱动程序<br>
如果使用模块(module)方式加载驱动程序,需要在模块初始化时把设备注册到系统设备表里去。不再使用时,把设备从系统中卸除。定义在drivers/net/net_init.h里的两个函数完成这个工作。<br>
int register_netdev(struct device *dev);<br>
void unregister_netdev(struct device *dev);<br>
dev就是要注册进系统的设备结构指针。在register_netdev()时,dev结构一般填写前面11项,即到init,后面的暂时可以不用初始化。最重要的是name指针和init方法。name指针空(NULL)或者内容为'\0'或者name[0]为空格(space),则系统把你的设备做为以太网设备处理。以太网设备有统一的命名格式,ethX。对以太网这么特别对待大概和Linux的历史有关。<br>
init方法一定要提供,register_netdev()会调用这个方法让你对硬件检测和设置。<br>
register_netdev()返回0表示成功,非0不成功。<p>
2.4.8 sk_buff<br>
Linux网络各层之间的数据传送都是通过sk_buff。sk_buff提供一套管理缓冲区的方法,是Linux系统网络高效运行的关键。每个sk_buff包括一些控制方法和一块数据缓冲区。控制方法按功能分为两种类型。一种是控制整个buffer链的方法,另一种是控制数据缓冲区的方法。sk_buff组织成双向链表的形式,根据网络应用的特点,对链表的操作主要是删除链表头的元素和添加到链表尾。sk_buff的控制方法都很短小以尽量减少系统负荷。(translated from article written by AlanCox)<br>
常用的方法包括:<br>
.alloc_skb() 申请一个sk_buff并对它初始化。返回就是申请到的sk_buff。<br>
.dev_alloc_skb()类似alloc_skb,在申请好缓冲区后,保留16字节的帧头空<br>
间。主要用在Ethernet驱动程序。<br>
.kfree_skb() 释放一个sk_buff。<br>
.skb_clone() 复制一个sk_buff,但不复制数据部分。<br>
.skb_copy()完全复制一个sk_buff。<br>
.skb_dequeue() 从一个sk_buff链表里取出第一个元素。返回取出的sk_buff,<br>
如果链表空则返回NULL。这是常用的一个操作。<br>
.skb_queue_head() 在一个sk_buff链表头放入一个元素。<br>
.skb_queue_tail() 在一个sk_buff链表尾放入一个元素。这也是常用的一个<br>
操作。网络数据的处理主要是对一个先进先出队列的管理,skb_queue_tail()<br>
和skb_dequeue()完成这个工作。<br>
.skb_insert() 在链表的某个元素前插入一个元素。<br>
.skb_append() 在链表的某个元素后插入一个元素。一些协议(如TCP)对没按<br>
顺序到达的数据进行重组时用到skb_insert()和skb_append()。<p>
.skb_reserve() 在一个申请好的sk_buff的缓冲区里保留一块空间。这个空间<br>
一般是用做下一层协议的头空间的。<br>
.skb_put() 在一个申请好的sk_buff的缓冲区里为数据保留一块空间。在<br>
alloc_skb以后,申请到的sk_buff的缓冲区都是处于空(free)状态,有一个<br>
tail指针指向free空间,实际上开始时tail就指向缓冲区头。skb_reserve()<br>
在free空间里申请协议头空间,skb_put()申请数据空间。见下面的图。<br>
.skb_push() 把sk_buff缓冲区里数据空间往前移。即把Head room中的空间移<br>
一部分到Data area。<br>
.skb_pull() 把sk_buff缓冲区里Data area中的空间移一部分到Head room中。<p>
--------------------------------------------------<br>
| Tail room(free) |<br>
--------------------------------------------------<br>
After alloc_skb()<p>
--------------------------------------------------<br>
| Head room | Tail room(free) |<br>
--------------------------------------------------<br>
After skb_reserve()<p>
--------------------------------------------------<br>
| Head room | Data area | Tail room(free) |<br>
--------------------------------------------------<br>
After skb_put()<p>
--------------------------------------------------<br>
|Head| skb_ | Data | Tail room(free) |<br>
|room| push | | |<br>
| | Data area | |<br>
--------------------------------------------------<br>
After skb_push()<p>
--------------------------------------------------<br>
| Head | skb_ | Data area | Tail room(free) |<br>
| | pull | | |<br>
| Head room | | |<br>
--------------------------------------------------<br>
After skb_pull()<p>
<p>
<br>
<center><A HREF="#Content">[目录]</A></center>
<hr><br><A NAME="I323" ID="I323"></A><center><b><font size=+2>需要注意</font></b></center><br>
三.编写Linux网络驱动程序中需要注意的问题<p>
3.1 中断共享<br>
Linux系统运行几个设备共享同一个中断。需要共享的话,在申请的时候指明共享方式。系统提供的request_irq()调用的定义:<br>
int request_irq(unsigned int irq,<br>
void (*handler)(int irq, void *dev_id, struct pt_regs *regs),<br>
unsigned long irqflags,<br>
const char * devname,<br>
void *dev_id);<br>
如果共享中断,irqflags设置SA_SHIRQ属性,这样就允许别的设备申请同一个中断。需要注意所有用到这个中断的设备在调用request_irq()都必须设置这个属性。系统在回调每个中断处理程序时,可以用dev_id这个参数找到相应的设备。一般dev_id就设为device结构本身。系统处理共享中断是用各自的dev_id参数依次调用每一个中断处理程序。<p>
3.2 硬件发送忙时的处理<br>
主CPU的处理能力一般比网络发送要快,所以经常会遇到系统有数据要发,但上一包数据网络设备还没发送完。因为在Linux里网络设备驱动程序一般不做数据缓存,不能发送的数据都是通知系统发送不成功,所以必须要有一个机制在硬件不忙时及时通知系统接着发送下面的数据。<br>
一般对发送忙的处理在前面设备的发送方法(hard_start_xmit)里已经描述过,即如果发送忙,置tbusy为1。处理完发送数据后,在发送结束中断里清tbusy,同时用mark_bh()调用通知系统继续发送。<br>
但在具体实现我的驱动程序时发现,这样的处理系统好象并不能及时地知道硬件已经空闲了,即在mark_bh()以后,系统要等一段时间才会接着发送。造成发送效率很低。2M线路只有10%不到的使用率。内核版本为2.0.35。<br>
我最后的实现是不把tbusy置1,让系统始终认为硬件空闲,但是报告发送不成功。系统会一直尝试重发。这样处理就运行正常了。但是遍循内核源码中的网络驱动程序,似乎没有这样处理的。不知道症结在哪里。<p>
3.3 流量控制(flow control)<br>
网络数据的发送和接收都需要流量控制。这些控制是在系统里实现的,不需要驱动程序做工作。每个设备数据结构里都有一个参数dev->tx_queue_len,这个参数标明发送时最多缓存的数据包。在Linux系统里以太网设备(10/100Mbps)tx_queue_len一般设置为100,串行线路(异步串口)为10。实际上如果看源码可以知道,设置了dev
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -