Linux E1000网卡驱动分析
- 格式:doc
- 大小:204.50 KB
- 文档页数:8
linux上网etho解决网卡驱动的问题注意:本文是在linux系统完全安装的条件下进行的。
有的朋友安装linux是定制安装,因此可能会出现某些必要的软件包没有的情况。
这就需要自己解决了。
不难的呵呵一查看网卡是否安装1 ifconfig -a 如果没有ethX 就很可能是网卡没有安装,至少可以断定网卡没有自动加载。
如果这时输入ifconfig eth0 xxx.xxx.xx.xx 系统会提示没有eth0 该设备。
2 可以用 dmesg | grep ethx 来查看系统能不能检查到该模块。
如果能会返回ATL2: eth0 NIC Link is up <100M full duplex>否则没反应3 用lsmod 查看开机时是否有加载如果有的,会有atl2 或eg3之类的4,查看网卡驱动安装的目录#uname -r 查看内核版本#cd lib/modules/'uname -r'/kernel/drives/net 网卡驱动安装在该目录下#ls显示相关的驱动,如果没有就要手动安装了二,安装驱动1 查看你的驱动类型。
我切换到2003系统查看网卡型号。
2 网上下载驱动。
我下载的地址如下:/downloads/downloadsView.aspx?Langid=3&PNid=4&PFid =4&Level=5&Conn=4&DownTypeID=3&GetDown=false&Dow nloads=true#RTL8110S-32/RTL8169S-32/RTL8110SB(L)/RTL8169SB(L)<br>RTL8110SC(L)/RTL8169SC(L)3 通过U盘拷贝到linux中。
1)#modprobe usb-storage 必须的命令好像这样才能读出U 盘2) # mount /dev/sdb? /mnt (?为几可以用fdisk -l查看一下,我的是sda)注意:有的U盘可能linux无法读出。
Linux下查看网卡驱动和版本信息查看网卡生产厂商和信号查看基本信息:lspci查看详细信息:lspci -vvv # 3个小写的v查看网卡信息:lspci | grep Ethernet查看网卡驱动查看网卡驱动信息:lspci -vvv # 找到网卡设备的详细信息,包括网卡驱动# lsmod 列出加载的所有驱动,包括网卡驱动查看网卡驱动版本查看模块信息:modifo<module name> # 其中包含version信息或# ethtool-i <device name>RHEL 6.3中的网卡驱动版本:# modinfo igbfilename: /lib/modules/2.6.32-279.el6.x86_64/kernel/drivers/net/igb/igb.ko version: 3.2.10-klicense: GPLdescription: Intel(R) Gigabit Ethernet Network Driver# modinfo e1000efilename: /lib/modules/2.6.32-279.el6.x86_64/kernel/drivers/net/e1000e/e1000e.ko version: 1.9.5-klicense: GPLdescription: Intel(R) PRO/1000 Network Driverauthor: Intel Corporation,<linux.nics@># modinfo e1000filename: /lib/modules/2.6.32-279.el6.x86_64/kernel/drivers/net/e1000/e1000.ko version: 8.0.35-NAPIlicense: GPLdescription: Intel(R) PRO/1000 Network Driver# modinfo ixgbefilename: /lib/modules/2.6.32-279.el6.x86_64/kernel/drivers/net/ixgbe/ixgbe.ko version: 3.6.7-klicense: GPLdescription: Intel(R) 10 Gigabit PCI Express NetworkDriver# modinfo r8169filename: /lib/modules/2.6.32-279.el6.x86_64/kernel/drivers/net/r8169.ko version: 2.3LK-NAPIlicense: GPLdescription: RealTek RTL-8169 Gigabit Ethernet driver查看网络接口队列数查看网卡接口的中断信息:#cat /proc/interrupts | grep eth0或# ethtool-S eth0查看网卡驱动源码的版本号解压Intel网卡驱动源码,打开解压缩目录下的*.spec文件查看驱动的版本。
Linux⽹卡驱动架构分析⼀、⽹卡驱动架构 由上到下层次依次为:应⽤程序→系统调⽤接⼝→协议⽆关接⼝→⽹络协议栈→设备⽆关接⼝→设备驱动。
⼆、重要数据结构 1、Linux内核中每⼀个⽹卡由⼀个net_device结构来描述。
2、⽹卡操作函数集:net_device_ops,这个数据结构是上⾯net_device的⼀个成员。
3、⽹络数据包:sk_buff。
三、⽹卡驱动代码分析 所⽤⽂件为cs89x0.c,主要分析三个部分:⽹卡初始化、发送数据、接收数据。
㈠⽹卡初始化 ⽹卡驱动初始化主要在函数init_module中完成,部分代码如下:int __init init_module(void){ struct net_device *dev = alloc_etherdev(sizeof(struct net_local)); struct net_local *lp; int ret = 0; ... dev->irq = irq; dev->base_addr = io; ... ret = cs89x0_probe1(dev, io, 1); ...} cs89x0_probe1函数部分代码如下:static int __init cs89x0_probe1(struct net_device *dev, int ioaddr, int modular){ struct net_local *lp = netdev_priv(dev); static unsigned version_printed; int i; int tmp; unsigned rev_type = 0; int eeprom_buff[CHKSUM_LEN]; int retval; ... writeword(ioaddr, ADD_PORT, PP_ChipID); tmp = readword(ioaddr, DATA_PORT); //对硬件的初始化 ... for (i = 0; i < ETH_ALEN/2; i++) //初始化MAC地址 { dev->dev_addr[i*2] = eeprom_buff[i]; dev->dev_addr[i*2+1] = eeprom_buff[i] >> 8; } ... dev->netdev_ops = &net_ops; //初始化netdev_ops ... retval = register_netdev(dev); //注册⽹卡驱动} 由代码可以看出 1、定义并分配net_device结构,使⽤alloc_etherdev函数。
收稿日期:2001208202(修改稿) 作者简介:郭学理(1946-),男,河南遂平人,教授,主要研究方向:计算机网络、分布并行处理; 潘松(1978-),男,湖南常德人,硕士研究生,主要研究方向:计算机网络通信; 韦智(1977-),男,广西柳州人,硕士研究生,主要研究方向:计算机网络通信.文章编号:1001-9081(2001)11-0023-02Linux 下网络驱动程序分析郭学理,潘 松,韦 智(武汉大学软件工程国家重点实验室,湖北武汉430072)摘 要:讨论了Linux 网络驱动程序的基本原理以及它的基本实现过程,并对它的两种驱动方式———模块驱动和内核启动驱动进行了比较。
关键词:Linux ;内核;网络驱动程序;模块驱动;内核驱动中图分类号:TP393;TP316.89 文献标识码:AANALYSIS OF LINUX BASED ON NETWORK DRIVERG UO Xue 2li ,PAN S ong ,WEI Zhi(State K ey Laboratory o f So ftware Engineering ,Wuhan Univer sity ,Wuhan Hubei 430072,China )Abstract :In this article ,we first discuss the basic principles and the basic implementation procedure of netw ork driver ,and then present a detailed comparion of its tw o driven m odes ,the m odule -driven m ode and the kernel -driven m ode.K ey w ords :Linux ;kernel ;netw ork driver ;m odule -driven ;kernel -driven Linux 是基于PC 机且与Unix 兼容的一种操作系统,它是由世界上许多优秀的程序员共同开发设计实现的一种免费发行软件。
linux查看⽹卡驱动信息⽅法⼀:1:ethtool -i ethx如:linux:/mnt # ethtool -i eth1driver: e1000eversion: 1.0.2-k2firmware-version: 1.9-0bus-info: 0000:0b:00.0linux:/mnt # ethtool -i eth16driver: igbversion: 2.1.0-k2firmware-version: 1.4-1bus-info: 0000:0a:00.0linux:/mnt #2:使⽤ modinfo igb 查看驱动信息linux:~ # modinfo igbfilename: /lib/modules/2.6.32.12-0.7-default/kernel/drivers/net/igb/igb.koversion: 5.2.5license: GPLdescription: Intel(R) Gigabit Ethernet Network Driverauthor: Intel Corporation, <e1000-devel@>srcversion: 0E80ABCD0117D822FE8B271alias: pci:v00008086d000010D6sv*sd*bc*sc*i*alias: pci:v00008086d000010A9sv*sd*bc*sc*i*alias: pci:v00008086d000010A7sv*sd*bc*sc*i*alias: pci:v00008086d000010E8sv*sd*bc*sc*i*alias: pci:v00008086d00001526sv*sd*bc*sc*i*alias: pci:v00008086d0000150Dsv*sd*bc*sc*i*alias: pci:v00008086d000010E7sv*sd*bc*sc*i*alias: pci:v00008086d000010E6sv*sd*bc*sc*i*alias: pci:v00008086d00001518sv*sd*bc*sc*i*alias: pci:v00008086d0000150Asv*sd*bc*sc*i*alias: pci:v00008086d000010C9sv*sd*bc*sc*i*alias: pci:v00008086d00000440sv*sd*bc*sc*i*alias: pci:v00008086d0000043Csv*sd*bc*sc*i*alias: pci:v00008086d0000043Asv*sd*bc*sc*i*alias: pci:v00008086d00000438sv*sd*bc*sc*i*alias: pci:v00008086d00001516sv*sd*bc*sc*i*alias: pci:v00008086d00001511sv*sd*bc*sc*i*alias: pci:v00008086d00001510sv*sd*bc*sc*i*alias: pci:v00008086d00001527sv*sd*bc*sc*i*alias: pci:v00008086d0000150Fsv*sd*bc*sc*i*alias: pci:v00008086d0000150Esv*sd*bc*sc*i*alias: pci:v00008086d00001524sv*sd*bc*sc*i*alias: pci:v00008086d00001523sv*sd*bc*sc*i*alias: pci:v00008086d00001522sv*sd*bc*sc*i*alias: pci:v00008086d00001521sv*sd*bc*sc*i*alias: pci:v00008086d00001539sv*sd*bc*sc*i*alias: pci:v00008086d0000157Csv*sd*bc*sc*i*alias: pci:v00008086d0000157Bsv*sd*bc*sc*i*alias: pci:v00008086d00001538sv*sd*bc*sc*i*alias: pci:v00008086d00001537sv*sd*bc*sc*i*alias: pci:v00008086d00001536sv*sd*bc*sc*i*alias: pci:v00008086d00001533sv*sd*bc*sc*i*alias: pci:v00008086d00001F45sv*sd*bc*sc*i*alias: pci:v00008086d00001F41sv*sd*bc*sc*i*alias: pci:v00008086d00001F40sv*sd*bc*sc*i*depends: hwmon,dcasupported: externalvermagic: 2.6.32.12-0.7-default SMP mod_unload modversionsparm: InterruptThrottleRate:Maximum interrupts per second, per vector, (max 100000), default 3=adaptive (array of int) parm: IntMode:Change Interrupt Mode (0=Legacy, 1=MSI, 2=MSI-X), default 2 (array of int)parm: Node:set the starting node to allocate memory on, default -1 (array of int)parm: LLIPort:Low Latency Interrupt TCP Port (0-65535), default 0=off (array of int)parm: LLIPush:Low Latency Interrupt on TCP Push flag (0,1), default 0=off (array of int)parm: LLISize:Low Latency Interrupt on Packet Size (0-1500), default 0=off (array of int)parm: RSS:Number of Receive-Side Scaling Descriptor Queues (0-8), default 1, 0=number of cpus (array of int)parm: VMDQ:Number of Virtual Machine Device Queues: 0-1 = disable, 2-8 enable, default 0 (array of int)parm: max_vfs:Number of Virtual Functions: 0 = disable, 1-7 enable, default 0 (array of int)parm: MDD:Malicious Driver Detection (0/1), default 1 = enabled. Only available when max_vfs is greater than 0 (array of int) parm: QueuePairs:Enable Tx/Rx queue pairs for interrupt handling (0,1), default 1=on (array of int)parm: EEE:Enable/disable on parts that support the feature (array of int)parm: DMAC:Disable or set latency for DMA Coalescing ((0=off, 1000-10000(msec), 250, 500 (usec)) (array of int)parm: LRO:Large Receive Offload (0,1), default 0=off (array of int)parm: enable_debug:Set to 1 to enable debug tracing into the syslog (uint)parm: debug:Debug level (0=none, ..., 16=all) (int)linux:~ #⽅法⼆:1:dmesg | grep ethx如:linux:~ # dmesg | grep eth17[ 30.351872] igb 0000:0a:00.1: eth17: (PCIe:2.5Gb/s:Width x4) 00:0b:ab:52:fb:b3[ 30.351952] igb 0000:0a:00.1: eth17: PBA No: ffffff-0ff[429171.548763] device eth17 entered promiscuous mode[429173.116530] igb: eth17 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX [429173.118867] ADDRCONF(NETDEV_UP): eth17: link is not ready[429173.121176] ADDRCONF(NETDEV_CHANGE): eth17: link becomes ready [429183.236266] eth17: no IPv6 routers present[1641503.272376] igb 0000:0a:00.1: eth17: (PCIe:2.5Gb/s:Width x4) 00:0b:ab:52:fb:b3 [1641503.272460] igb 0000:0a:00.1: eth17: PBA No: ffffff-0ff[1641598.356110] device eth17 entered promiscuous mode[1641598.369229] igb: eth17 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX [1641598.372636] ADDRCONF(NETDEV_UP): eth17: link is not ready [1641598.374978] ADDRCONF(NETDEV_CHANGE): eth17: link becomes ready [1641608.564181] eth17: no IPv6 routers present[1701779.787471] igb 0000:0a:00.1: eth17: (PCIe:2.5GT/s:Width x4)[1701779.787473] igb 0000:0a:00.1: eth17: MAC: 00:0b:ab:52:fb:b3[1701779.787555] igb 0000:0a:00.1: eth17: PBA No: FFFFFF-0FF[1702124.805650] device eth17 entered promiscuous mode[1702141.839131] ADDRCONF(NETDEV_UP): eth17: link is not ready [1702144.057474] igb: eth17 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: None [1702144.059425] ADDRCONF(NETDEV_CHANGE): eth17: link becomes ready [1702154.705520] eth17: no IPv6 routers present[1712008.630151] igb 0000:0a:00.1: eth17: (PCIe:2.5GT/s:Width x4)[1712008.630153] igb 0000:0a:00.1: eth17: MAC: 00:0b:ab:52:fb:b3[1712008.630235] igb 0000:0a:00.1: eth17: PBA No: FFFFFF-0FF[1712100.136186] device eth17 entered promiscuous mode[1712101.873823] ADDRCONF(NETDEV_UP): eth17: link is not ready [1712104.159209] igb: eth17 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: None [1712104.161548] ADDRCONF(NETDEV_CHANGE): eth17: link becomes ready [1712114.854722] eth17: no IPv6 routers presentlinux:~ #2:使⽤ modinfo igb 查看驱动信息(结果同⽅法⼀中的)。
Linux服务器网卡驱动安装及故障排除(1)网卡是Linux服务器中最重要网络设备。
据统计,Linux网络故障有35%在物理层、25%在数据链路层、10%在网络层、10%在传输层、10%在对话、7%在表示层、3%在应用层。
由此可以看出,网络故障通常发生在网络七层模型的下三层,即物理层、链路层和网络层。
对应于实际网络也就是使用的网络线缆、网卡、交换机、路由器等设备故障。
Linux的网络实现是模仿FreeBSD的,它支持FreeBSD的带有扩展的Sockets(套接字)和TCP/IP 协议。
它支持两个主机间的网络连接和Sockets通讯模型,实现了两种类型的 Sockets:BSD Sockets和INET Sockets。
它为不同的通信模型和服务质量提供了两种传输协议,即不可靠的、基于消息的UDP传输协议和可靠的、基于流的传输协议TCP,并且都是在 IP网络协议上实现的。
INET sockets是在以上两个协议及IP协议之上实现的。
由于交换机、路由器通常独立于Linux或者其他操作系统。
网卡设置故障是造成Linux 服务器故障最主要原因。
可能因为硬件的质量或性能、磨损老化、人为误操作、不正确的网络设置、管理问题、Linux软件的BUG、系统受到黑客攻击和Linux病毒等原因造成。
Linux 服务器网卡故障排除的思路是:应当遵循先硬件后软件的方法。
因为硬件如果出现物理损坏那么如何设定网卡都不能解决故障。
解决问题的方法可以从自身Linux计算机的网卡查起,如果确定硬件没有问题了,再来考虑软件的设定。
1. 网卡的选择一般来说,2.4版本以后的 Linux可以支持的网卡芯片组数量已经很完备了,包括著名厂商如:Intel 以及使用广泛的 RealTek, Via 等网卡芯片都已经被支持,所以使用者可以很轻易的设定好他们的网络卡。
但是由于Linux发行版本众多(目前超过188个),使用前最好查看Linux发行版本的文档。
sk_buffstruct sk_buff可能是linux网络代码中最重要的数据结构,它表示接收或发送数据包的包头信息,并包含很多成员变量供网络代码中的各子系统使用。
这个结构被网络的不同层(MAC或者其他二层链路协议,三层的IP,四层的TCP或UDP等)使用,并且其中的成员变量在结构从一层向另一层传递时改变。
L4向L3传递前会添加一个L4的头部,同样,L3向L2传递前,会添加一个L3的头部。
添加头部比在不同层之间拷贝数据的效率更高。
由于在缓冲区的头部添加数据意味着要修改指向缓冲区的指针,这是个复杂的操作,所以内核提供了一个函数skb_reserve来完成这个功能。
协议栈中的每一层在往下一层传递缓冲区前,第一件事就是调用skb_reserve在缓冲区的头部给协议头预留一定的空间。
skb_reserve同样被设备驱动使用来对齐接收到包的包头。
如果缓冲区向上层协议传递,旧的协议层的头部信息就没什么用了。
例如,L2的头部只有在网络驱动处理L2的协议时有用,L3是不会关心它的信息的。
但是,内核并没有把L2的头部从缓冲区中删除,而是把有效荷载的指针指向L3的头部,这样做,可以节省CPU时间。
有些sk_buff成员变量的作用是方便查找或者是连接数据结构本身。
内核可以把sk_buff组织成一个双向链表。
当然,这个链表的结构要比常见的双向链表的结构复杂一点。
就像任何一个双向链表一样,sk_buff中有两个指针next和prev,其中,next指向下一个节点,而prev指向上一个节点。
在第一个节点前面会插入另一个结构sk_buff_head,这是一个辅助节点(作为sk_buff双向链表的头),它的定义如下:struct sk_buff_head {struct sk_buff -*next;struct sk_buff -*prev;__u32 qlen;spinlock_t lock;};qlen代表链表元素的个数lock用于防止对链表的并发访问sk_buff和sk_buff_head的前两个元素是一样的:next和prev指针。
Linux下网卡驱动安装我们以安装inter网卡驱动为例。
一、Linux下添加内核源文件在Intel网站直接下载的Linux驱动是e1000-5.2.52.tar.gz(版本可能会有改变),这个压缩包里面没有编译好的.o的文件,需要在Linux系统下编译之后才能使用,因为网卡需要编译,所以要先确认将内核源文件安装好,下面是关于内核源文件的安装。
1.用rpm –qa | grep kernel- source查看是否安装了这个包;如果返回结果中有kernel-source-xxx(其中xxx为当前RedHat的内核版本,如rhel3为2.4.21-4EL), 即已经安装。
如无返回结果则需要安装kernel-source包。
到安装光盘中找到kernel-source-xxx.i386.rpm,用下面命令安装此rpm包:rpm –ivh kernel-source-xxx.i386.rpm挂载光驱:mount /dev/cdrom /mnt/cdrom到目录下:cd /mnt/cdrom/RedHat/RPMS/rpm –ivh kernel-source-xxx.i386.rpm安装完成后,卸载光驱:cdumount /mnt/cdrom如果提示kernel-source没有安装成功,则可能是因为安装此软件包之间的依赖关系。
需要在安装系统的时候,将开发工具里面的软件包全选上为最好。
否则根据提示将依赖的软件包安装好即可安装上kernel-source。
2.如果安装了,用rpm -V kernel-source校验是否有文件丢失,如果没有输出,表示文件完整;3.如果有丢失,用rpm -ivh --force kernel-source-xxxx...把包重新安装一下;这个kernel-source包,在您的RH安装光盘中,在RedHat/RPMS中,如果以前没有安装过这个包,那么用rpm -ivh kernel-source-xxxx...来安装,如果安装过,需要覆盖安装,使用rpm -ivh --force kernel-source-xxxx...这个命令强制安装。
小包转发--LINUX的弱项小包转发效率低下,这既不是SYN问题,也与QUEUE无关,根源在于:1、操作系统对大量随机中断(interrpt)的性能低下:PC体系架构的系统中,网卡接收数据(RECV)基于中断机制(IRQ)。
以前窄带时代带宽小,每秒产生IRQ次数少,占用CPU处理资源也就少。
在宽带环境下,当每秒IRQ数量达到10K以上时,CPU就忙不赢了。
2、操作系统从kernel到user-space的内存复制(copy)效率低下。
为解决IRQ处理瓶颈,现代操作系统改变了网卡的IRQ处理机制,提高了网络处理效率:提高网络性能的几种技术1、FreeBSD下的device polling关闭网卡的中断方式,使用轮循方式收发报文,可以大大提高小包(如syn flood,udp flood)下的路由性能和路由器的稳定性,提升率可以超过100M线速。
2、Linux2.6下的NAPI(New API)关闭网卡的中断方式,使用轮循方式收发报文,可以大大提高小包(如syn flood,udp flood)下的路由性能和路由器的稳定性,提升率可以超过100M线速,但远低于FreeBSD下的decice_polling。
3、Linux下的Timer_based网卡驱动程序(Tuplit网卡)关闭网卡的中断方式,使用基于网卡上定时器进行定时查询方式收发报文,可以大大提高小包(如syn flood,udp flood)下的路由性能和路由器的稳定性,提升率可以超过100M线速,但远低于FreeBSD下的decice_polling。
4、Linux下的NAPI和Circular Buffer技术(DMA—user_space技术)关闭网卡的中断方式,使用轮循方式收发报文,可以大大提高小包(如syn flood,udp flood)下的路由性能和路由器的稳定性,提升率可以超过1000M线速,略高于FreeBSD下的decice_polling。
26.Linux-⽹卡驱动介绍以及制作虚拟⽹卡驱动(详解)1.描述⽹卡的驱动其实很简单,它还是与硬件相关,主要是负责收发⽹络的数据包,它将上层协议传递下来的数据包以特定的媒介访问控制⽅式进⾏发送,并将接收到的数据包传递给上层协议。
⽹卡设备与字符设备和块设备不同,⽹络设备并不对应于/dev⽬录下的⽂件,不过会存放在/sys/class/net⽬录下如下图所⽰,我们通过ls /sys/class/net/命令,可以看到有两个⽹卡:2.Linux系统对⽹络设备驱动定义了4个层次,这4个层次有到下分为:1)⽹络协议接⼝层:实现统⼀的数据包收发的协议,该层主要负责调⽤dev_queue_xmit()函数发送数据, netif_rx()函数接收数据2)⽹络设备接⼝层:通过net_device结构体来描述⼀个具体的⽹络设备的信息,实现不同的硬件的统⼀3)设备驱动功能层:⽤来负责驱动⽹络设备硬件来完成各个功能, 它通过hard_start_xmit() 函数启动发送操作,并通过⽹络设备上的中断触发接收操作,4)⽹络设备与媒介层:⽤来负责完成数据包发送和接收的物理实体, 设备驱动功能层的函数都在这物理上驱动的层次结构如下图所⽰:3.⽹卡驱动初始化⽽我们的⽹卡驱动程序,只需要编写⽹络设备接⼝层,填充net_device数据结构的内容并将net_device注册⼊内核,设置硬件相关操作,使能中断处理等3.1其中net_device结构体的重要成员,整理后如下所⽰:struct net_device{char name[IFNAMSIZ]; //⽹卡设备名称unsigned long mem_end; //该设备的内存结束地址unsigned long mem_start; //该设备的内存起始地址unsigned long base_addr; //该设备的内存I/O基地址unsigned int irq; //该设备的中断号unsigned char if_port; //多端⼝设备使⽤的端⼝类型 unsigned char dma; //该设备的DMA通道unsigned long state; //⽹络设备和⽹络适配器的状态信息struct net_device_stats* (*get_stats)(struct net_device *dev); //获取流量的统计信息 //运⾏ifconfig便会调⽤该成员函数,并返回⼀个net_device_stats结构体获取信息struct net_device_stats stats; //⽤来保存统计信息的net_device_stats结构体unsigned long features; //接⼝特征,unsigned int flags; //flags指⽹络接⼝标志,以IFF_(Interface Flags)开头//当flags =IFF_UP(当设备被激活并可以开始发送数据包时,内核设置该标志)、 IFF_AUTOMEDIA(设置设备可在多种媒介间切换)、IFF_BROADCAST(允许⼴播)、IFF_DEBUG(调试模式,可⽤于控制printk调⽤的详细程度)、 IFF_LOOPBACK(回环)、IFF_MULTICAST(允许组播)、 IFF_NOARP(接⼝不能执⾏ARP,点对点接⼝就不需要运⾏ ARP)和IFF_POINTOPOINT(接⼝连接到点到点链路)等。
Linux网络设备分析潘纲 9811536浙江大学计算机系pg@[摘要] 在本文中,首先概括了网络设备总体特征和工作原理,接着在分析了一个重要的数据结构device后,重点剖析了网络设备的整个初始化工作过程;简单地分析了设备的打开和关闭的操作后,是有关数据包的传输和接收的分析;在最后,本文对写网络设备驱动程序做了一个总结。
以上的每部分的分析,都是在NE2000以太网卡的基础上进行的。
在附录中是一个虚拟的字符设备驱动程序以及写这个程序的体会,该程序已成功使用过,它是在网络设备分析之前本人做的一个小小的试验。
一.网络设备概述在LINUX中,为了简化对设备的管理,所有外围的硬件设备被归结为三类:字符设备(如键盘、鼠标等)、块设备(如硬盘、光驱、软驱等)和网络设备(也称为网络接口,network inferface),如以太网卡。
在本文中,我们将等效使用“网络设备”和“网络接口”这两个概念,而对某个具体的网络设备,我们将称之为“物理设备”或“物理网络设备”。
为了屏蔽网络环境中物理网络设备的多样性,LINUX对所有的物理设备进行抽象并定义了一个统一的概念,称之为接口(Interface)。
所有对网络硬件的访问都是通过接口进行的,接口提供了一个对所有类型的硬件一致化的操作集合来处理基本数据的发送和接收。
一个网络接口被看作是一个发送和接收数据包(packets)的实体。
对于每个网络接口,都用一个device的数据结构表示,有关该数据结构的具体内容,将在本文的后面详细介绍。
通常,网络设备是一个物理设备如以太网卡,但软件也可以作为网络设备,如回送设备(loopback)。
在内核启动时,通过网络设备驱动程序,将登记存在的网络设备。
设备用标准的支持网络的机制来转递收到的数据到相应的网络层。
所有被发送和接收的包都用数据结构sk_buff表示。
这是一个具有很好的灵活性的数据结构,可以很容易增加或删除网络协议数据包的首部。
基于Linux PCI总线驱动模型的网卡驱动分析总线概念:总线是一种传输信号的信道;总线是连接一个或多个导体的电气连线。
总线由电气接口和编程接口组成,我们重点关注编程接口。
PCI(Peripheral Component Interconnect)外围设备互联的简称,是在桌面及更大型的计算机上普遍使用的外设总线。
(驱动程序移植)PCI总线具有三个非常显著的优点:1、在计算机和外设间传输数据时具有更好的性能2、能够尽量独立于具体的平台3、可以方便地实现即插即用体系结构:从结构上,PCI总线是一种不依附于某个具体处理器的局部总线,它是在CPU和原来的系统总线之间的一级总线,具体由一个桥接电路实现对这一层的管理,并实现上下之间的接口以协调数据的传送。
系统的各个部分通过PCI总线和PCI-PCI桥连接在一起。
CPU和RAM通过PCI桥连接到PCI总线0(即主PCI总线),而具有PCI接口的显卡直接连接到主PCI总线上。
PCI-PCI桥是一个特殊的PCI设备,它负责将PCI总线0和PCI 总线1连接在一起。
图中连接到PCI1号总线上的是SCSI卡和以太网卡。
为了兼容旧的ISA总线标准,PCI总线还可以通过PCI-ISA桥来连接ISA总线,从而支持以前的ISA设备,图中ISA总线上连接一个多功能I/O控制器,用于控制键盘、鼠标和软驱等。
PCI设备寻址:(深度优先遍历)每个PCI设备由一个总线号、一个设备号和一个功能号确定。
PCI规范允许一个系统最多拥有256条总线,每条总线最多带32个设备,但每个设备可以是最多8个功能的多功能板(如一个音频设备带一个CD-ROM驱动器)/proc/iomem描述了系统中所有的设备I/O在内存地址空间上的映射。
dc400000-dc40ffff : 0000:02:01.0dc400000-dc40ffff是它所映射的内存空间地址0000:02:01.0是PCI外设的地址,它以冒号和逗号分隔开为4个部分(域16位+总线编号8位+设备号5位+功能号3位):0000表示域,02表示一个总线号,01表示设备号,0表示功能号。
本文档的Copyleft归wwwlkk所有,使用GPL发布,可以自由拷贝、转载,转载时请保持文档的完整性,严禁用于任何商业用途。
E-mail: wwwlkk@来源: /?business&aid=6&un=wwwlkk#7多CPU下基于e1000e驱动的数据包以及网卡中断流程分析图1 e1000e网卡收包环和发包环控制收包环说明:next_to_clean和next_to_use是内核读写的;rdt是内核写,网卡只读;rdh是网卡写,内核只读;next_to_clean和rdh之间的是已经接收到数据包的内存;rdh和rdt之间是还未接收到数据包的内存;next_to_use后的是还未使用的区域。
发包环说明:next_to_clean和next_to_use是内核读写的;tdt是内核写,网卡只读;tdh是网卡写,内核只读;next_to_clean和tdh之间的是已经发送的数据包,可以被释放;rdh和rdt之间是发送的数据包;next_to_use后的是还未使用的区域。
网卡产生中断的时机,一共3个:1)当网卡接收到一个数据包,并通过DMA方式写入内存,此时网卡会向前移动rdh,并发送网卡中断。
2)网卡发送完数据包,并移动tdh,当tdh移动次数达到一定数量,或者tdh等于tdt的时候会发生网卡中断。
3)如果不发送也不接收数据包,网卡也会定时产生中断,比如一秒一次。
以上3个时机产生相同的中断信号,所以会调用相同的中断处理例程。
说明:这里对时机的总结是通过实验获得,不是很精确,但是基本上是正确的。
网卡只是产生网卡中断,但是网卡不能决定由哪个CPU来响应中断。
在多CPU体系下,由中断控制器来决定由哪个CPU响应中断。
图2是linux-2.6.35在没有使用RPS下的数据包接收和发生流程。
在这种模式下,当有一个CPU在处理数据包时,其它CPU就不会再处理数据包了,虽然中断控制器会选择不同的CPU来响应网卡中断,但CPU不是并发的处理数据包,而是串行的处理数据包。
Linux-千兆网卡驱动实现机制浅析作者: Minit, 出处:博客,责任编辑: 罗丽艳,2009-03-29 00:001.引言本分析主要针对e1000网卡,驱动源码为7.3.20-k2。
本文的目的不是为了讲述如何编写驱动程序,主要是分析网卡驱动内部的实现机制。
通过此分析,希望可以理解驱动程序中的各个部分的关系,对网卡发送和接收数据包有直观的了解,同时也希望对设计网卡驱动程序有帮助。
由于网卡驱动程序与硬件和操作系统都有很紧密的联系,故要把某些问题完全弄清楚,需要很多的经验与相关知识,介于自身的水平有限,且自身经验较少,故肯定存在很多问题,希望本文的读者发现了问题不吝与作者联系。
2.网卡驱动的体系结构网卡作为一个PCI设备,其必须遵守相应的PCI规范,即必须为网卡定义相应的标识号,每个PCI外设由一个总线编号、一个设备编号及一个功能编号来标识。
网卡驱动程序则需要定义相应的pci_device_id结构来表示其支持的PCI外设的标识,通过在驱动程序的pci_device_id中查找设备标识号,将驱动程序与设备联系起来。
网卡作为PCI设备,其包括两类空间,一种是配置空间,CPU不能直接访问,访问这个空间,需要借助BIOS功能;另一种是普通的控制寄存器空间,这部分经过映射后,CPU可以直接访问控制。
在硬件加电初始化时,BIOS统一检查所有的PCI设备,并为每个设备分配一个物理地址,该地址通过BIOS获得并写到设备的配置空间内,驱动程序就可以将网卡的普通控制寄存器映射到一段内存空间内,CPU通过访问映射后的虚拟地址来操控网卡的寄存器。
当操作系统初始化时,其为每个PCI设备分配一个pci_dev结构,并将前面分配的物理地址写到pci_dev的resource字段中。
在网卡驱动程序中则可以通过读取pci_dev中的resource字段获得网卡的寄存器配置空间地址,其由函数pci_resource_start()和pci_resource_end()获得该空间的起始位置,通过ioremap()将该段位置映射到主存中,以便CPU访问控制网卡的I/O和内存空间。
如重启网卡设备,则是通过向映射后的网卡的相应寄存器写入命令实现,其通过映射后的首地址及相应的寄存器偏移量找到该寄存器的位置,然后通过函数writeb()写该寄存器。
有关相关寄存器对应的偏移量,一般是通过网卡的相关的datasheet获得。
如果要获取网卡的MAC地址,则一般通过函数readb()读取首地址开始的前六位内容即可得到。
通过pci_read_config_和pci_write_config_系列函数可以读写网卡的配置空间,如开启网卡设备就是将网卡配置空间的command域置1,从而设备就可以将寄存器映射到内存。
如通过函数pci_read_config_byte(pci_devpdev,PCI_INTERRUPT_LINE,&irq)获得设备所分配的中断号并保存在irq中。
pci_read_config_和pci_write_config_系列函数实际上是调用pci_bus_read_config_和pci_bus_write_config_系列函数实现的,这些函数实际操作网卡对应的PCI总线结构。
有关PCI寄存器的配置空间可参考《Linux Device Driver 3rd》或《PCI Bus Demystified》。
网卡作为一个规范的PCI设备,其对应的结构体pci_dev代表了网卡设备,体现了作为PCI设备所应有的规范。
网卡的网络传输性质,实际是通过另一结构体net_device来体现的,该结构体的初始化由网卡驱动程序实现。
内核中对网卡的操作,其实质就是对net_device结构的操作,pci_dev和net_device都表示网卡设备,只是体现的角度不一样。
net_device是对特定适配器的抽象,其为上层协议提供了统一的接口,网卡驱动则基于特定适配器实现了这一抽象。
PCI设备的驱动程序由pci_driver结构体表示,故网卡驱动应该是该结构体的一个实例,在该结构体中应该要定义实现与网卡相关的参数以及相应的操作。
网卡驱动实际操作的特定适配器,是由与硬件相关的adapter所表示的结构体,adapter体现了大部分与硬件相关的属性,网卡驱动除了直接对pci_dev结构操作外,其他对网卡设备的操作基本是对adapter结构体的操作。
adapter体现了net_device与pci_dev的关联,也实现了网络设备的适配器无关性。
与网卡设备pci_dev的通信是通过adapter来实现的,而这个实现则是网卡驱动所要完成的任务。
下面图2-1描述了三个重要数据结构间的关系,pci_dev结构体现了网卡的配置空间和I/O与内存区域,net_device结构则向内核提供了操作网卡的抽象接口,其参数值可按照功能分为5个部分。
e1000_adapter结构除了体现相应的硬件无关性外,还管理了发送与接收数据包的相应缓冲空间,网卡的物理地址空间映射后的虚拟地址也在此结构中保存。
e1000_adapter结构中的e1000_hw结构主要保存网卡的硬件参数,其值就是通过读取pci_dev的内容获取而来的。
以上的数据结构在网卡工作时起着最核心的作用,同时也是编写驱动程序必须操作的结构体。
图2-1 网卡驱动程序的主要数据结构3. 网卡设备的注册与初始化网卡设备的注册与初始化是在其相关的驱动程序的e1000_probe()函数中实现的,有关设备如何与该驱动相关联,以及如何调用到e1000_probe()的,在此不作介绍。
在函数e1000_probe()中首先调用函数pci_enable_device()启用设备,然后声明了DMA空间,接着调用函数alloc_etherdev()生成结构体net_device,该结构体就表示了网卡设备,对net_device的参数进行了初始化后,调用register_netdev()注册该设备。
以上仅是对设备的注册,设备的初始化主要包括对两个结构体的赋值,一个是net_device,另一个则是e1000_adapter。
对e1000_adapter的初始化包括对其中的e1000_hw结构的初始化,其调用函数e1000_sw_init()实现。
在对e1000_hw的初始化过程中使用了ioremap()实现了网卡硬件地址与内存虚拟地址之间的映射。
对网卡设备进行撤销则调用函数free_netdev()实现。
有关网卡设备注册与初始化的更详细的过程可以参考《Understanding Linux Network Internals》。
4. 网卡设备的启动与关闭网卡设备启动时首先调用函数e1000_open(),在该函数中调用e1000_request_irq()申请中断号及其相应的中断处理程序e1000_intr(),其实际是调用request_irq()函数来实现的。
在函数e1000_open()中调用e1000_setup_all_tx_resources()根据发送队列数建立发送缓冲区,每个缓冲区的建立由函数e1000_setup_tx_resources()实现,在e1000_setup_tx_resources()中,主要是对描述发送缓冲区的结构体e1000_tx_ring的初始化,其将DMA缓冲区与网卡所映射的虚拟地址空间联系起来,使用函数pci_alloc_consistent()实现一致性映射。
而虚拟地址空间与网卡的物理地址相对应,故而这三种空间就对应了起来,DMA也就可以在此基础上实现了,当数据包内容被映射到DMA缓冲区后,其将完全由设备操控。
DMA的缓冲区的初始化在驱动程序的e1000_probe()函数中实现。
e1000_open()函数会调用e1000_up()对网卡的一些相关的软硬件参数与空间进行配置,如硬件寄存器的读写,数据包接收与发送空间的处理函数的初始化等。
发送缓冲空间的初始化结构及相互间的关系如图4-1所示。
接收缓冲区的初始化与上述类似,由e1000_setup_all_rx_resources()调用e1000_setup_rx_resources()对结构体e1000_rx_ring进行初始化。
接收缓冲空间的结构如图4-2所示。
图4-1 发送缓冲区的结构图图4-2 接收缓冲区的结构图网卡的关闭由函数e1000_close()实现,其会首先关闭中断,然后释放中断号,并且会释放网卡申请的相应的空间。
5. 发送与接收数据包数据包的发送:图5-1 发送数据包的结构图及相互关系根据发送队列数num_tx_queues建立相应的发送缓冲区结构e1000_tx_ring,在该结构中有描述该区域的指向e1000_tx_desc 结构的desc,该缓冲区指向的dma总线地址,用于接收硬件传送来的用e1000_buffer结构描述的缓冲块数组buffer_info[],另外的几个参数则主要用于描述这些缓冲块,其中count表示缓冲块的个数,next_to_use和next_to_clean主要描述缓冲块的使用状态,如已经接收接收了数据的位置及准备接收的位置,当有新的数据包要发送时,首先由上层协议调用e1000_xmit_frame(),在该函数中接着调用e1000_tx_queue()根据相应的参数找到缓冲块存放,缓冲块的初始化则由函数e1000_tx_map()实现。
buffer_info指向的环形缓冲块区域主要用来接收总线地址映射来的数据包,所有的缓冲块用next_to_match连接成一个环,每个缓冲块用结构体e1000_buffer表示,在该结构中,skb存放数据包的内容,dma表示该数据包所在的总线地址。
此处使用函数pci_map_single()进行流式映射,的映射方向为PCI_DMA_TODEVICE,控制总线会把虚拟地址空间所指内容映射到总线地址,然后将该内容由网卡传送出去。
发送数据包的相关结构图及相互关系如图5-1所示。
e1000_tx_ring结构中的desc所指向的buffer_addr记录了每次发送的缓冲块所映射的总线地址,即buffer_addr记录的是总线地址。
而desc本是一个虚拟地址,该虚拟地址是通过pci_alloc_consistent()映射的发送缓冲区的地址,其与DMA缓冲区中的一段总线地址相对应,该总线地址由e1000_tx_ring结构中的dma成员保存,这种映射关系在对开启网卡时就实现了,其与在发送数据包时映射的总线地址有区别,后者是在发送时动态进行的。
数据包的接收图5-2 接收数据包的结构图及相互关系根据接收队列数num_rx_queues建立相应的接收缓冲区结构e1000_rx_ring,在该结构中有描述该区域的指向e1000_rx_desc结构的desc,该缓冲区指向的dma总线地址,用于接收硬件传送来的用e1000_buffer结构描述的缓冲块数组buffer_info[],另外的几个参数则主要用于描述这些缓冲块,其中count表示缓冲块的个数,next_to_use和next_to_clean主要描述缓冲块的使用状态,如已经接收接收了数据的位置及准备接收的位置,当有新的数据包要到来时,则根据这两个参数找到相应的区域存放。