Linux协议栈(10)——网络驱动
网络驱动区别于其他驱动(例如块驱动)的一个方面是:块驱动的运行只是响应来自内核的请求,但是网络驱动从外边异步地接收报文。
Linux内核的网络子系统设计成完全独立于协议的。
相关内容可以参考:linux网络驱动初始化module_init函数跟踪
1.1.1.1 注册网络设备
函数alloc_netdev分配一个新的net_device实例。然后使用特定于协议的函数用典型值来填充该结构,以太网使用ether_setup。这两个函数可以合起来就是alloc_etherdev
填充完毕后,调用register_netdevie()(定义于文件net/core/dev.c)注册到系统中,注销可以通过unregister_netdevice()。
register_netdev函数将设备注册到内核,并将设备注册到通用设备机制内。会创建一个sysfs项/sys/class/net/<device>中。
driver_register在总线上注册驱动。netdev_register_kobject给网络设备创建sysfs入口。
1.1.1.2 加载和卸载
以e1000驱动为例,驱动加载时候先运行e1000_init_module函数,主要实现注册到PCI子系统中。
通过调用pci_register_driver函数注册e1000_driver(为pci_driver为驱动主结构)驱动,将其注册到PCI设备的链表中。
卸载是e1000_exit_module函数,直接调用pci_unregister_driver,和设备打开和关闭。
根据e1000_netdev_ops结构体中的定义,打开设备使用的函数为e1000_open。当网络接口激活的时候被调用。
关闭函数为e1000_close也定义在e1000_netdev_ops结构体中,用于禁用网络接口。
1.1.1.3 数据接收和发送
数据传输是net_device接口的关键组件。驱动程序提供一个hard_start_xmit函数,协议层在发送时调用它来向下传递数据包,现在也是定义在net_dev_ops结构体。
可以看到发送函数为e1000_xmit_frame。
现在的中断驱动程序都采用的是NAPI方式,需要提供poll函数,本驱动中是e1000_netpoll轮询函数。
当有新数据包要发送时候,首先上层协议调用e1000_xmit_frame函数,然后在该函数中调用e1000_tx_queue来根据相应的参数找到缓冲块存放,缓冲块中有dma成员,表示该数据包所在的总线地址,控制总线会把内容映射到总线地址,然后由网卡传送出去。
当有新数据包达到时,首先触动中断处理函数e1000_intr,该中断函数会将数据包放在buffer_info的缓冲块中(),就是讲总线地址指向的内容复制到skb中,然后根据skb中的协议将其传给上层协议的接收函数。
具体会在后面针对e1000驱动进行介绍。
1.1.1.4 总结:
本系列从总体上对linux网络子系统有了介绍。也标注了源码的出处和部分相关函数的定义,但是并未针对代码进行深入。主要是从两方面考虑,一方面是本系列初衷是对网络子系统有总体的掌控,深入代码后容易走到细枝末节上;另一方面是,一旦深入代码本身,那么会延伸到很远,本人也没有做好这个准备,需要对知识点再做细分。这个是后续需要做的事情。
Linux系统中的网络子系统非常庞大,这里介绍的只是是冰的一角,虽是轮廓但也只能算是描边,给大家参考阅读,欢迎指正。后续会继续深入之。还会增加Linux网络模块的其他机制例如:netlink,netfilter等。
最后祝大家玩得愉快。
1.1.1.4 参考:
《深入Linux内核架构》第十二章
《深入理解linux网络技术内幕》
《精通linux内核网络》
《linux设备驱动》
《TCP/IP详解卷1——原理》
《Linux内核源码剖析TCP IP实现》