通过insmod或者modprobe命令加载驱动,这两个命令为应用程序,在应用程序里调用了一个系统调用:

extern long init_module(void *, unsigned long, const char *);该系统调用最终调用内核函数:

asmlinkage long

sys_init_module(void __user *umod,

unsigned long len,

const char __user *uargs)

该函数调用驱动程序中用module_init定义的函数e1000_init_module。

module_init(e1000_init_module);

三、驱动如何匹配设备

pci_driver结构体中有一个成员id_table,其类型为struct pci_device_id *,pci驱动里都需要初始化该成员:

struct pci_device_id {

__u32 vendor, device;/* Vendor and device ID or PCI_ANY_ID*/

__u32 subvendor, subdevice;/* Subsystem ID's or PCI_ANY_ID */

__u32 class, class_mask;/* (class,subclass,prog-if) triplet */

kernel_ulong_t driver_data;/* Data private to the driver */

};

该结构中最重要的两个成员为ve

ndor和device。vendor为厂商的ID,该值是唯一的,device为设备的ID。所有PCI设备的配置空间里都保存了这两个值。下面是PCI规范里对这两个字段的定义:

Vendor ID This field identifies the manufacturer of the device.

Valid vendoridentifiers are allocated by the PCI SIG

to ensure uniqueness.0 FFFFh is an invalid value for

Vendor ID.

Device ID This field identifies the particular device.

This identifier is allocatedby the vendor.

当同一个系统中的两个设备的vendor和device相同时,用subvendor和subdevice区分不同的设备。

1、MODULE_DEVICE_TABLE

static DEFINE_PCI_DEVICE_TABLE(e1000e_pci_tbl) = {

{

PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_COPPER), board_82571}, {

PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_FIBER), board_82571}, {

PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER), board_82571},

{

PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER_LP), board_82571},

...

该结构体数组列出了所有该驱动支持的设备,主要是设备的Vender ID和Device ID,在pci_register_driver中会用到该结构体数组。

驱动中还用宏MODULE_DRVICE_TABLE定义了一个变量:

MODULE_DEVICE_TABLE(pci, e1000e_pci_tbl);

上面的例子中,宏MODULE_DEVICE_TABLE的定义如下:

#define MODULE_DEVICE_TABLE(type,name)\

MODULE_GENERIC_TABLE(type##_device,name)

#define MODULE_GENERIC_TABLE(gtype,name)\

extern const struct gtype##_id __mod_##gtype##_table\

__attribute__ ((unused, alias(__stringify(name))))

该宏定义替换以后就变成如下语句:

extern const struct pci_driver_id __mod_pci_driver_id_table

__attribute__ ((unused,alias("e1000e_pci_table")))

该宏创建一个struct pci_device_id类型的变量__mod_pci_device_table,该变量的别名是e1000e_pci_table,意味着该变量同样指向e1000e_pci_tbl这个结构体(struct pci_driver_id)数组。在稍后的内核构建过程中,depmod程序从所有的模块中搜索符号__mod_pci_device_table,如果找到该符号,就会把数组中的数据添加到/lib/module/KERNEL_VERSION/modules.pcimap中。当depmod结束之后,内核模块支持的所有PCI设备连通他们的模块名都在该文件中被列出。当内核告知热插拔系统一个新的 PCI设备已经被发现时,热插拔系统使用modules.pcimap文件来寻找要状态的恰当的驱动。

上面结构体列出中所有的驱动支持的设备都在下面的文件里列出了:

$cat modules.pcimap | grep e1000e

e1000e 0x00008086 0x0000105e 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0

e1000e 0x00008086 0x0000105f 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0

e1000e 0x00008086 0x000010a4 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0

e1000e 0x00008086 0x000010bc 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0

e1000e 0x00008086 0x000010a5 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0

e1000e 0x00008086 0x00001060 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0

e1000e 0x00008086 0x000010d9 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0

e1000e 0x00008086 0x000010da 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0

e1000e 0x00008086 0x000010d5 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0

e1000e 0x00008086 0x000010b9 0xffffffff 0xffffffff 0x00000000 0x00000000 0x0

...

上面的方法是针对热插拔设备的,但是,不是所有的PCI设备都是热插拔设备,一般的网卡就不是热插拔设备。

2、pci_register_driver

一般的网卡不是热插拔设备,当驱动调用pci_register_driver函数后,内核搜寻系统中的存在的设备,如果有匹配e1000_pci_tbl结构体数组中所列出的设备,就会调用驱动程序中的probe函数。调用过程如下:

pci_register_driver

|

__pci_register_driver

|

bus_add_driver

|

driver_attach

|

bus_for_each_dev /*遍历所有PCI总线上的设备*/

|

__driver_attach

|

driver_probe_device

|

drv->bus->match(dev, drv)

|

dev->bus->probe(dev)

在调用probe之前,内核遍历所有的PCI总线上的设备,调用match函数,在此match的实例为pci_bus_match,看是否有设备跟e1000_pci_tbl中支持的设备匹配:

struct bus_type pci_bus_type = {

.name= "pci",

.match= pci_bus_match,

.uevent= pci_uevent,

.probe= pci_device_probe,

.remove= pci_device_remove,

.suspend= pci_device_suspend,

.shutdown= pci_device_shutdown,

.resume= pci_device_resume,

.dev_attrs= pci_dev_attrs,

};

pci_bus_match最终调用pci_match_one_device函数进行匹配:

static inline const struct pci_device_id *

pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)

{

if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&

(id->device == PCI_ANY_ID || id->device == dev->device) &&

(id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) &&

(id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) &&

!((id->class ^ dev->class) & id->class_mask))

return id;

return NULL;

}

如果有设备匹配了e1000_pci_tbl中的设备,就会调用pci_bus_type实例中的probe函数,在此probe的实例为pci_device_probe,最终调用函数pci_call_probe函数,该函数调用驱动程序中pci_driver实例中的probe函数:

static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev,

const struct pci_device_id *id)

{

int error;

...

error = drv->probe(dev, id);

...

return error;

}如果系统中存在多个相同的设备,就会多次调用

驱动中的probe函数,但是驱动给设备分配的资源(IO内存、中断等)及设备名是不同的,比如系统中有两个网卡,驱动分配的网络设备名可能为eth0和eth1。

如下转来的内容可以说明设备驱动加载时是如何匹配设备的:

对于所有的PCI设备,在系统引导时,会建立一种,把每个总线都关联一份已侦测并且使用该总线的设备列表。对于PCI设备来说,系统中就存在着这样一个数据库,其中保存着所有使用PCI总线的设备ID,此ID即上文提到的pci_device_id。

此时,(a)图就代表着所有使用PCI总线的设备数据库。当设备驱动程序A被加载时,会调用pci_register_driver并提供 pci_driver实例与PCI层注册,同时pci_driver结构中包含一个驱动程序所能处理的设备ID表(即e1000_pci_tbl);接着,PCI子系统使用该表去查在已经保存的设备数据库中是否存在匹配,于是会建立该驱动程序的设备列表,如图(b)所示;此外,对每个匹配的设备而言,PCI层会调用相匹配的驱动程序中的 pci_driver结构中所提供的probe函数。

Linux查看网卡加载驱动,linux网卡驱动分析之驱动加载相关推荐

  1. Linux查看硬件配置-cpu内存磁盘网卡

    Linux查看硬件配置 资产管理命令dmidecode 查看服务器型号 查看主板的序列号 查看系统序列号 查看cpu 查看占用cpu最多的几个进程 查看内存 查看内存信息 查看内存数量和内存大小 查询 ...

  2. linux查看宽带ip是否动态,linux CentOS系统查看实时宽带的办法

    Linux中查看网卡流量工具有iptraf.iftop以及nethogs等,iftop可以用来监控网卡的实时流量(可以指定网段).反向解析IP.显示端口信息等. 安装iftop的命令如下: CentO ...

  3. linux查看服务器品牌和型号,linux 查看服务器型号

    linux 查看服务器型号 内容精选 换一换 登录弹性云服务器查询磁盘设备信息,发现磁盘的设备名称与控制台上显示的挂载点不一致,不清楚磁盘具体挂载在哪个设备上或磁盘对应的逻辑卷标识.本节操作介绍如何根 ...

  4. Linux查看WAS的jvm信息,linux 下使用命令查看jvm信息

    java程序员除了编写业务代码之外,特别是项目上线之后,更需要关注的是系统的性能表现,这个时候就需要了解一下jvm的性能表现了,可以借助于java虚拟机自带的一些分析工具,主要有三个常用的命令. 1. ...

  5. linux查看安装的所有内核,Linux怎么查看系统已安装内核

    当我们需要在Linux系统中安装一些软件而去下载安装文件时,一般都需要确认到底下载哪个版本的安装包,这就需要我们知道自己的Linux系统到底是什么版本.什么内核,下面跟着学习啦小编一起来了解一下Lin ...

  6. linux查看进程运行日志文件,【Linux】常用指令、ps查看进程、kill杀进程、启动停止tomcat命令、查看日志、查看端口、find查找文件...

    1.说出 10 个 linux 常用的指令 1) ls 查看目录中的文件 2)cd /home 进入 '/ home' 目录:cd .. 返回上一级目录:cd ../.. 返回上两级目录 3)mkdi ...

  7. linux查看某进程的连接,linux下查看指定进程的所有连接信息(转)

    定位某个进程的网络故障时经常需要用到的一个功能就是查找所有连接的信息.通常查找某个端口的连接信息使用 ss 或者 netstat 可以轻松拿到,如果是主动与别的机器建立的连接信息则可以通过 lsof ...

  8. linux查看进程详细信息top,linux查看系统进程信息命令 px,top详解

    linux查看系统进程信息命令 px,top详解 发表于:2011-03-10来源:作者:点击数: linux查看系统进程信息命令 px,top详解 软件测试 ps ax命令是显示一个当前系统进程的列 ...

  9. linux查看存储类型及型号,linux怎么看内存型号

    Linux可安装在各种计算机硬件设备中,比如手机.平板电脑.路由器.视频游戏控制台.台式计算机.大型机和超级计算机.下面是学习啦小编带来的关于linux怎么看内存型号的内容,欢迎阅读! linux怎么 ...

  10. linux查看主板最大内存容量,Linux 查看内存插槽数、最大容量的方法

    Linux 查看内存插槽数.最大容量的方法 查看内存插槽数: dmidecode|grep -P -A5 "Memory\s+Device"|grep Size|grep -v R ...

最新文章

  1. 如何开发一个区块链应用程序
  2. 张海腾:语音识别实践教程
  3. 利用BH1750光度传感器测量一些发光体
  4. sqlserver2008r2数据库关联孤立账号的方法
  5. jmeter测试java代码
  6. 1.25 包(package)详解
  7. pyspark subtract代码示例
  8. Qt Assistant快速指南
  9. java p代表哪种数据类型_java数据类型(八种基本数据类型+三种引用类型)
  10. 【软件开发底层知识修炼】十八 快速学习GDB调试五 使用GDB进行调试的一些小技巧
  11. 【C++】 67_经典问题分析 五
  12. typescript和coffeescript简介
  13. 「Linux」VMware安装centos7(一)
  14. 原生PHP 做网站,php原生方法监控服务网站的运行情况
  15. 用阿里云盘,找不到资源怎么办???
  16. 扩展kalman滤波matlab程序,扩展卡尔曼滤波算法的matlab程序
  17. STC15单片机定时器0工作模式介绍
  18. 两个理想的90°电桥构成的非线性平衡电路
  19. 小程序开发-Step1
  20. 什么是虚拟主机?虚拟主机是什么意思

热门文章

  1. Java | GUI 图形用户界面
  2. Metabase的基本使用:10分钟快速入门
  3. AOJ 606.LOL系列之德玛短路
  4. 利用 wordXP 实现自动排班
  5. 学摄影:三分钟了解直方图
  6. 迪特里希·朋霍费尔:愚蠢是一种道德上的缺陷
  7. Bert-BiLSTM-CRF pytorch 代码解析-3:def _viterbi_decode
  8. chrome浏览器——书签
  9. uniapp中uni.showToast最多显示多少个汉字?
  10. 如何从CDN加载jQuery?