Linux网卡驱动程序分析
时间:2023-06-06 05:05:15 点击:347

  学习应该是一个先把问题简单化,再把问题复杂化的过程。一开始就着手处理复杂的问题,难免让人有心惊胆颤,捉襟见肘的感觉。读Linux网卡驱动 也是一样。那长长的源码夹杂着那些我们陌生的变量和符号,望而生畏便是理所当然的了。不要担心,事情总有解决的办法,先把一些我们管不着的代码切割出去,留下必须的部分,把框架掌握了,那其他的事情自然就水到渠成了,这是笔者的心得。

  一般在使用的Linux网卡驱动代码动辄3000行左右,这个代码量以及它所表达出来的知识量无疑是庞大的,我们有没有办法缩短一下这个代码量,使我们的学习变的简单些呢?经过笔者的不懈努力,在仍然能够使网络设备正常工作的前提下,把它缩减到了600多行,我们把暂时还用不上的功能先割出去。这样一来,事情就简单多了,真的就剩下一个框架了。

  下面我们就来剖析这个可以执行的框架。

  限于篇幅,以下分析用到的所有涉及到内核中的函数代码,我都不予列出,但给出在哪个具体文件中,请读者自行查阅。

  首先,我们来看看设备的初始化。当我们正确编译完我们的程序后,我们就需要把生成的目标文件加载到内核中去,我们会先 ifconfig eth0 down和rmmod 8139too来卸载正在使用的网卡驱动,然后insmod 8139too.o把我们的驱动加载进去(其中8139too.o是我们编译生成的目标文件)。就像C程序有主函数main()一样,模块也有第一个执行的函数,即 module_init(rtl8139_init_module);在我们的程序中,rtl8139_init_module()在insmod之后首 先执行,它的代码如下:

  static int __init rtl8139_init_module (void)

  {

  return pci_module_init (&rtl8139_pci_driver);

  }

  它直接调用了pci_module_init(),这个函数代码在Linux/drivers/net/eepro100.c中,并且把 rtl8139_pci_driver(这个结构是在我们的驱动代码里定义的,它是驱动程序和PCI设备联系的纽带)的地址作为参数传给了它。 rtl8139_pci_driver定义如下:

  static struct pci_driver rtl8139_pci_driver = {

  name: MODNAME,

  id_table: rtl8139_pci_tbl,

  probe: rtl8139_init_one,

  remove: rtl8139_remove_one,

  };

  pci_module_init()在驱动代码里没有定义,你一定想到了,它是Linux内核提供给模块是一个标准接口,那么这个接口都干了些什么?笔者跟踪了这个函数,里面调用了pci_register_driver(),这个函数代码在Linux/drivers/pci/pci.c 中,pci_register_driver做了三件事情。

  ①是把带过来的参数rtl8139_pci_driver在内核中进行了注册。内核中有一个PCI设备的大的链表,这里负责把这个pci驱动挂到里面去。

  ②是查看总线上所有PCI设备(网卡设备属于PCI设备的一种)的配置空间,如果发现标识信息与rtl8139_pci_driver中的id_table相同,即rtl8139_pci_tbl,而它的定义如下:

  static struct pci_device_id rtl8139_pci_tbl[] __devinitdata = {

  {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},

  {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0,0 },

  {0,}

  };

  那么就说明这个驱动程序就是用来驱动这个设备的,于是调用rtl8139_pci_driver中的probe函数即 rtl8139_init_one,这个函数是在我们的驱动程序中定义了的,它是用来初始化整个设备和做一些准备工作。这里需要注意一下 pci_device_id是内核定义的用来辨别不同PCI设备的一个结构,例如在我们这里0x10ec代表的是Realtek公司,我们扫描PCI设备配置空间如果发现有Realtek公司制造的设备时,两者就对上了。当然对上了公司号后还得看其他的设备号什么的,都对上了才说明这个驱动是可以为这个设备服务的。

  ③是把这个rtl8139_pci_driver结构挂在这个设备的数据结构(pci_dev)上,表示这个设备从此就有了自己的驱动了。而驱动也找到了它服务的对象了。

  PCI是一个总线标准,PCI总线上的设备就是PCI设备,这些设备有很多类型,当然也包括网卡设备,每一个PCI设备在内核中抽象为一个数据结构pci_dev,它描述了一个PCI设备的所有的特性,具体请查询相关文档,本文限于篇幅无法详细描述。但是有几个地方和驱动程序的关系特别大,必须予以说明。PCI设备都遵守PCI标准,这个部分所有的PCI设备都是一样的,每个PCI设备都有一段寄存器存储着配置空间,这一部分格式是一样的,比如第一个寄存器总是生产商号码,如Realtek就是10ec,而Intel则是另一个数字,这些都是商家像标准组织申请的,是肯定不同的。我就可以通过配置空间来辨别其生产商,设备号,不论你什么平台,x86也好,ppc也好,他们都是同一的标准格式。当然光有这些PCI配置空间的统一格式还是不够的,比如 说人类,都有鼻子和眼睛,但并不是所有人的鼻子和眼睛都长的一样的。网卡设备是PCI设备必须遵守规则,在设备里集成了PCI配置空间,但它是一个网卡就必须同时集成能控制网卡工作的寄存器。而寄存器的访问就成了一个问题。在Linux里面我们是把这些寄存器映射到主存虚拟空间上的,换句话说我们的CPU 访存指令就可以访问到这些处于外设中的控制寄存器。总结一下,PCI设备主要包括两类空间,一个是配置空间,它是操作系统或BIOS控制外设的统一格式的空 间,CPU指令不能访问,访问这个空间要借助BIOS功能,事实上Linux的访问配置空间的函数是通过CPU指令驱使BIOS来完成读写访问的。

  而另一类是普通的控制寄存器空间,这一部分映射完后CPU可以访问来控制设备工作。

  现在我们回到上面pci_register_driver的第二步,如果找到相关设备和我们的pci_device_id结构数组对上号了,说明我们找到服务对象了,则调用rtl8139_init_one,它主要做了七件事:

  ① 建立net_device结构,让它在内核中代表这个网络设备。但是读者可能会问,pci_dev也是代表着这个设备,那么两者有什么区别 呢,正如我们上面讨论的,网卡设备既要遵循PCI规范,也要担负起其作为网卡设备的职责,于是就分了两块,pci_dev用来负责网卡的PCI规范,而这里要说的net_device则是负责网卡的网络设备这个职责。

  dev = init_etherdev (NULL, sizeof (*tp));

  if (dev == NULL) {

  printk (“unable to alloc new ethernetn“);

  return -ENOMEM;

  }

  tp = dev->priv;

  init_etherdev函数在Linux/drivers/net/net_init.c中,在这个函数中分配了net_device的内存并进行了初步的初始化。这里值得注意的是net_device中的一个成员priv,它代表着不同网卡的私有数据,比如Intel的网卡和Realtek 的网卡在内核中都是以net_device来代表。但是他们是有区别的,比如Intel和Realtek实现同一功能的方法不一样,这些都是靠着priv 来体现。所以这里把拿出来同net_device相提并论。分配内存时,net_device中除了priv以外的成员都是固定的,而priv的大小是可 以任意的,所以分配时要把priv的大小传过去。

  ②开启这个设备(其实是开启了设备的寄存器映射到内存的功能)

  rc = pci_enable_device (pdev);

  if (rc)

  goto err_out;

  pci_enable_device也是一个内核开发出来的接口,代码在drivers/pci/pci.c中,笔者跟踪发现这个函数主要就是把 PCI配置空间的Command域的0位和1位置成了1,从而达到了开启设备的目的,因为rtl8139的官方datasheet中,说明了这两位的作用 就是开启内存映射和I/O映射,如果不开的话,那我们以上讨论的把控制寄存器空间映射到内存空间的这一功能就被屏蔽了,这对我们是非常不利的,除此之外,pci_enable_device还做了些中断开启工作。

  ③获得各项资源

  mmio_start = pci_resource_start (pdev, 1);

  mmio_end = pci_resource_end (pdev, 1);

  mmio_flags = pci_resource_flags (pdev, 1);

  mmio_len = pci_resource_len (pdev, 1);

  读者也许疑问我们的寄存器被映射到内存中的什么地方是什么时候有谁决定的呢。是这样的,在硬件加电初始化时,Bios固件同一检查了所有的PCI 设备,并统一为他们分配了一个和其他互不冲突的地址,让他们的驱动程序可以向这些地址映射他们的寄存器,这些地址被BIOS写进了各个设备的配置空间,因为这个活动是一个PCI的标准的活动,所以自然写到各个设备的配置空间里而不是他们风格各异的控制寄存器空间里。当然只有BIOS可以访问配置空间。当操作系统初始化时,他为每个PCI设备分配了pci_dev结构,并且把BIOS获得的并写到了配置空间中的地址读出来写到了pci_dev中的 resource字段中。这样以后我们在读这些地址就不需要在访问配置空间了,直接跟pci_dev要就可以了,我们这里的四个函数就是直接从 pci_dev读出了相关数据,代码在include/linux/pci.h中。定义如下:

  #define pci_resource_start(dev,bar) ((dev)->resource[(bar)].start)

  #define pci_resource_end(dev,bar) ((dev)->resource[(bar)].end)

  这里需要说明一下,每个PCI设备有0-5一共6个地址空间,我们通常只使用前两个,这里我们把参数1传给了bar就是使用内存映射的地址空间。

  ④把得到的地址进行映射

  ioaddr = ioremap (mmio_start, mmio_len);

  if (ioaddr == NULL) {

  printk (“cannot remap MMIO, abortingn“);

  rc = -EIO;

  goto err_out_free_res;

  }

  ioremap是内核提供的用来映射外设寄存器到主存的函数,我们要映射的地址已经从pci_dev中读了出来(上一步)

展开 ↓
标签: linux 网卡驱动 Linux网卡驱动程序分析 暗黑3死灵荆棘废了(暗黑3死灵技能分析) 《荒野大镖客2》查尔斯战力分析图(荒野大镖客2查尔斯是好人吗) 《无双大蛇3》貂蝉玩法分析说明图(无双大蛇3u武将测评) 《无双大蛇3》孟获玩法分析说明文(无双大蛇z孟获) 《生化危机:抵抗计划》各角色结局分析图(生化危机抵抗计划好玩吗) 《全面战争:三国》郑姜技能猜想与分析(全面战争三国郑姜技能加点) 《无双大蛇3》女娲玩法分析说明书(无双大蛇3终极版女娲厉害吗) 《原神》可莉输出手法详解(原神可莉强度分析) 《喋血复仇》实用拾荒者卡牌效果及评测分析(喋血复仇抽卡) 《无双大蛇3》鲍三娘玩法分析说明书(《无双大蛇3》鲍三娘玩法分析说明书) 《克苏鲁的呼唤》大召唤结局分析(《克苏鲁的呼唤》大召唤结局分析) 《剑士》游戏突然卡顿原因分析(剑士游戏好玩吗) 《生化危机:抵抗计划》各角色结局分析(生化危机抵抗者计划) 《人类黎明》地图选择分析(人类黎明怎么看地图) 《黑神话:悟空》剧情介绍分析图(《黑神话:悟空》新预告) 《无双大蛇3》练师玩法分析说明(《无双大蛇3》练师玩法分析说明图) 《小镇惊魂2》剧情深度解读分析与介绍(《小镇惊魂2》剧情深度解读分析与介绍视频) 暗黑破坏神3法师技能分析(暗黑破坏神3法师) 《饥荒》哈姆雷特怎么复活?人物复活方法分析(饥荒哈姆雷特攻略知乎) 《荒野大镖客2》约翰战力分析(荒野大镖客2约翰最帅搭配) 《星球工匠》游戏玩法背景解析(星球工厂小程序) 《钢铁战队》机枪塔性能分析(钢铁战队兵种搭配) 《荒野大镖客2》大叔战力分析图(荒野大镖客2大叔任务给了什么衣服) apex英雄应用程序无法正常启动(apex英雄应用程序错误) heroesamongus课文结构分析(heroesamongus课文思维导图) 《无双大蛇3》浓姬玩法分析说明(无双大蛇3浓姬的腿) 《纪元1800》开局地点选择分析(纪元1800开局攻略) apex应用程序无法正常启动(apex应用程序无法正常启动0xc000007b) 《归于沉寂》结局分析原文(《归于沉寂》结局分析原文) 《荒野大镖客2》全武器弹药属性分析(荒野大镖客2各种弹药区别) apex游戏客户端遇到应用程序错误(apex应用程序发生异常0×40) 《地狱守卫》角色分析(地狱守卫ns) rockstarlauncher已停止工作(rockstar出现了一个问题,导致程序停止工作) 奥特曼格斗进化0杰克大招怎么放(奥特曼格斗进化0杰克奥特曼技能分析) 《冬日计划》猎人天赋分析怎么写(冬日计划怎么打狼) 《全面战争模拟器》酋长属性分析(全面战争模拟器:召唤师战争,部落酋长想吃霸王餐!) 《全面战争:三国》坚盾卫士属性分析(全面战争三国佩剑守卫骑兵) 《黑神话:悟空》剧情介绍分析图(黑神话:悟空剧情) 《无双大蛇3》典韦玩法分析说明书(无双大蛇3武将评测) 《星球工匠》部分道具作用效果怎么写(星球工厂小程序) epic启动程序一直转圈(epic启动一直转圈圈) 《全面战争:三国》曹操强度分析(全面战争三国曹操部队配置) 《古剑奇谭3》刘兄结局分析图(《古剑奇谭3》刘兄结局分析图片) 《全面战争:三国》司马懿强度分析图(全面战争三国司马懿事件触发了但是没有) 《烟火》剧情(烟火剧情详解分析) 《剑士》游戏突然卡顿原因分析(剑士很卡怎么办) 《鬼谷八荒》npc好感度分析(鬼谷八荒npc好感怎么刷) 《原神》祭礼剑适用角色分析吗(《原神》祭礼剑适用角色分析吗视频) 《无双大蛇3》雅典娜玩法分析说明(《无双大蛇3》雅典娜玩法分析说明) 《足球经理》分析与统计系统解析pdf(足球经理数据) 暗黑2尸爆最大范围(暗黑2尸爆技能分析) 《荒野大镖客2》全武器弹药属性分析图(《荒野大镖客2》全武器弹药属性分析图) apex应用程序发生异常(apex应用程序错误) 《了不起的修仙模拟器》法宝系统分析(了不起的修仙模拟器法宝模板) 《亡灵诡计》游戏背景故事猜测与分析(电影亡灵结局什么意思) 《太吾绘卷》关于打斗的图解分析(太吾绘卷打头) 《全面战争:三国》赵云技能猜想与分析(三国全面战争赵云特技触发) 啊普索夫诸神终结(阿普索夫诸神终结剧情分析) 《奥伯拉丁的回归》剧情分析(奥伯拉丁的回归剧情解析) 《全面战争模拟器》乐手属性分析(全面战争模拟器作者) 《无双大蛇3》典韦玩法分析说明(无双大蛇3技型武将都有谁) 《太吾绘卷》挂件的作用分析和介绍怎么写(太吾绘卷制作装备一览表) 《足球经理》战术玩法分析图(足球经理20194231战术布置) 《中世纪合金》全方位详细评测分析(《中世纪合金》全方位详细评测分析报告) csgo枪械简介(csgo各种枪分析) 《亿万僵尸》玩法总体分析图(《亿万僵尸》玩法总体分析图解) 《无双大蛇3》郭嘉玩法分析说明文(无双大蛇3郭淮) 《星球工匠》种树方法和技巧(星球工厂小程序) 《无双大蛇3》郭嘉玩法分析说明书(无双大蛇3jc) 《挺进地牢》boss老国王的属性分析图(《挺进地牢》boss老国王的属性分析图) 《人类黎明》地图选择分析图(人类黎明布局图) 《无双大蛇3》练师玩法分析说明图(无双大蛇3角色加点) 《缺氧》相关工作分析介绍怎么写(缺氧工艺) 《剑魄》游戏优点评测分析(剑魄视频) 《全面战争:战锤2》轻甲弓箭手实用性分析图(战锤2全面战争弓箭弹道) 《变量》酸碱度属性分析怎么写(酸碱度区间) 《全面战争:战锤2》掠夺者骑手实用性分析图(《全面战争:战锤2》掠夺者骑手实用性分析图) 《全面战争:三国》神巫属性分析图(全面战争三国鬼神事件) 《赛博朋克2077》恶魔结局好坏分析(赛博朋克2077恶魔结局解读) ff7力竭大师(ff7力竭现象分析no.3) temtem阿纳希尔厉害吗(程序166-阿纳希塔) epic游戏启动不了系统出错(epic启动程序进不去) superliminal需要什么配置(supersu配置程序) 《地狱守卫》角色分析怎么写(ns地狱守卫好玩吗) 《全面战争:三国》强度分析(全面战争三国玩法流派) 《全面战争:战锤2》幽冥行者实用性分析图(战锤2古墓王幽冥行者) 《无双大蛇3》三藏法师玩法分析说明(无双大蛇3法正) 《全面战争:三国》持刀步兵属性分析图(全面战争三国持矛步兵) 《疑案追声》dlc中元节特别篇剧情分析(《疑案追声》dlc中元节特别篇剧情分析在线观看) 《原神》可莉强度评测(原神可莉厉害吗可莉强度测评介绍分析一览) 《全面战争:三国》强度分析(全面战争三国最强部队阵容) 《全面战争:战锤2》毁灭巨轮实用性分析图(战锤2毁灭巨轮和剥皮机) 《冬日计划》雪人天赋分析(冬日计划人物技能) 《僵尸世界大战》游戏优缺点分析图(僵尸世界大战游戏好不好玩) boomsgames(boomsgame数据分析) grounded怎么分析(grounded怎么分析碎石) 《浮岛物语》各大神器属性分析图(浮岛物语神器图鉴) 《全面战争:三国》弩兵属性分析(全面战争三国弩兵不射箭) 《元气骑士》兑换券怎么获得的(元气骑士兑换卷怎么获得?兑换券获得方法及作用分析) 《隐形守护者》第十章剧情预测分析(隐形守护者第十章100) 分析 随机数字命名.pif文件类病毒分析与清除 安全知识:巧设瑞星白名单让信任的程序运行更快 安全防忽悠之安全网关趋势分析 航海王热血航线黑胡子技能展示-航海王热血航线黑胡子提奇技能分析 Linux设备驱动程序 如何编写Linux设备驱动程序 明日方舟保全派驻临时道具效果介绍-明日方舟保全派驻临时道具具体分析 rundll32.exe应用程序错误的解决方法 li

最新游戏更多

最新软件更多

  • 玩家推荐
  • 游戏攻略

金钼软件下载站 Copyright(C) 2008- 601958.cn All Rights Reserved!

闽ICP备2023004188号| 免责声明