📄 ch14s06.html
字号:
<p>早些时候, 在 PCI 驱动注册到驱动核心之前, probe 变量被设为指向 pci_device_probe 函数. 这个函数转换(又一次) struct device 为一个struct pci_dev, 在设备中设置的 struct driver 为一个 struct pci_driver. 它再次验证这个驱动声明它可以支持这个设备( 这意味着一个重复的额外检查, 某些未知的原因), 递增设备的引用计数, 并且接着调用 PCI 驱动的 probe 函数, 用一个指向它应当被绑定到的 struct pci_dev 结构的指针.</p><p>如果这个 PCI 驱动的 probe 函数认为它不能处理这个设备由于某些原因, 它返回一个负的错误值, 这个值被传递回驱动核心并且使它继续深入设备列表来和这个设备匹配一个. 如果这个 probe 函数能够认领这个设备, 它做所有的需要的初始化来正确处理这个设备, 并且接着它返回 0 给驱动核心. 这使驱动核心来添加设备到当前被这个特定驱动所绑定的所有设备列表, 并且创建一个符号连接到这个它现在控制的设备, 在这个驱动在 sysfs 的目录. 这个符号连接允许用户准确见到哪个设备被绑定到哪个设备. 这可被见到, 如:</p><pre class="screen">$ tree /sys/bus/pci/sys/bus/pci/|-- devices| |-- 0000:00:00.0 -> ../../../devices/pci0000:00/0000:00:00.0 | |-- 0000:00:00.1 -> ../../../devices/pci0000:00/0000:00:00.1 | |-- 0000:00:00.2 -> ../../../devices/pci0000:00/0000:00:00.2 | |-- 0000:00:02.0 -> ../../../devices/pci0000:00/0000:00:02.0 | |-- 0000:00:04.0 -> ../../../devices/pci0000:00/0000:00:04.0 | |-- 0000:00:06.0 -> ../../../devices/pci0000:00/0000:00:06.0 | |-- 0000:00:07.0 -> ../../../devices/pci0000:00/0000:00:07.0 | |-- 0000:00:09.0 -> ../../../devices/pci0000:00/0000:00:09.0 | |-- 0000:00:09.1 -> ../../../devices/pci0000:00/0000:00:09.1 | |-- 0000:00:09.2 -> ../../../devices/pci0000:00/0000:00:09.2 | |-- 0000:00:0c.0 -> ../../../devices/pci0000:00/0000:00:0c.0 | |-- 0000:00:0f.0 -> ../../../devices/pci0000:00/0000:00:0f.0 | |-- 0000:00:10.0 -> ../../../devices/pci0000:00/0000:00:10.0 | |-- 0000:00:12.0 -> ../../../devices/pci0000:00/0000:00:12.0 | |-- 0000:00:13.0 -> ../../../devices/pci0000:00/0000:00:13.0 | `-- 0000:00:14.0 -> ../../../devices/pci0000:00/0000:00:14.0 `-- drivers |-- ALI15x3_IDE | `-- 0000:00:0f.0 -> ../../../../devices/pci0000:00/0000:00:0f.0 |-- ehci_hcd | `-- 0000:00:09.2 -> ../../../../devices/pci0000:00/0000:00:09.2 |-- ohci_hcd | |-- 0000:00:02.0 -> ../../../../devices/pci0000:00/0000:00:02.0 | |-- 0000:00:09.0 -> ../../../../devices/pci0000:00/0000:00:09.0 | `-- 0000:00:09.1 -> ../../../../devices/pci0000:00/0000:00:09.1 |-- orinoco_pci | `-- 0000:00:12.0 -> ../../../../devices/pci0000:00/0000:00:12.0 |-- radeonfb | `-- 0000:00:14.0 -> ../../../../devices/pci0000:00/0000:00:14.0 |-- serial `-- trident `-- 0000:00:04.0 -> ../../../../devices/pci0000:00/0000:00:04.0 </pre></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="RemoveaDevice.sect2"></a>14.6.2. 去除一个设备</h3></div></div></div><p>一个 PCI 可用多个不同的方法被从系统中去除. 所有的 card-bus 设备在一个不同的物理因素上是真正的 PCI 设备, 并且内核 PCI 核心不区分它们. 允许在机器运行时加减 PCI 设备的系统正变得更加普遍, 并且 Linux 支持它们. 还有一个伪 PCI 热插拔驱动允许开发者来测试看是否他们的 PCI 驱动正确处理系统运行中的设备去除. 这个模块称为 fakephp 并且使内核认为 PCI 设备已消失, 但是它不允许用户物理上从系统中去除一个 PCI 设备, 这个系统没有合适的硬件来这样做. 见这个驱动的文档来获取更多关于如何使用它测试你的 PCI 驱动的信息.</p><p>PCI 核心发挥了不少于它增加设备的努力到去除它. 当一个 PCI 设备要被去除, pci_remove_bus_device 函数被调用. 这个函数做一些 PCI-特定 的清理和日常工作, 并且接着使用一个指向 struct pci_dev 的 struct device 成员的指针调用 device_unregister 函数.</p><p>在 device_unregister 函数中, 驱动核心只从绑定到这个设备(如果有)的驱动解除连接 sysfs 文件, 从它的内部设备列表中去除这个设备, 并且使用指向包含在 struct device 结构中的 struct kobject 的指针调用 kobject_del. 这个函数用一个 hotplug 调用到用户空间来声明 kobject 现在被从系统中去除, 并且接着它删除所有的和 kobject 关联的 sysfs 文件以及这个 kobject 起初已创建的 sysfs 目录自身.</p><p>kobject_del 函数也去除设备自身的 kobject 引用. 如果那个引用是最后一个( 意味着没有用户空间文件为这个 sysfs 的设备入口而打开 ), 接着是 PCI 设备自身的 release 函数, pci_release_dev, 被调用. 这个函数只释放 struct pci_dev 占用的内存.</p><p>此后, 所有的和这个设备关联的 sysfs 入口被去除, 并且和这个设备关联的内存被释放. PCI 设备现在完全从系统中被去除.</p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="AddaDriver.sect2"></a>14.6.3. 添加一个驱动</h3></div></div></div><p>一个 PCI 驱动被添加到 PCI 核心, 当它调用 pci_register_driver 函数时. 这个函数只初始化 struct device_driver 结构, 这个结构包含在 struct pci_driver 结构里面, 如同之前在关于添加设备的一节中提过的. 接着 PCI 核心使用指向包含在 struct pci_driver 结构中的 sturct device_driver 结构的指针调用在驱动核心的 driver_register 函数.</p><p>driver_register 函数初始化在 struct device_driver 结构中的几个锁, 并且接着调用 bus_add_driver 函数. 这个函数进行下面的步骤:</p><div class="itemizedlist"><ul type="disc"><li><p>查找驱动要被关联的总线. 如果这个总线被发现, 函数立刻返回.</p></li><li><p>驱动的 sysfs 目录被创建, 基于驱动的名子和它被关联的总线.</p></li><li><p>总线的内部锁被获取, 接着所有的已经注册到总线的设备被检查, 匹配函数为它们被调用, 就象当一个新设备被添加时. 如果那个匹配函数成功, 接着剩下的绑定过程发生, 如同在前面章节描述过的.</p></li></ul></div></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="RemoveaDriver.sect2"></a>14.6.4. 去除一个驱动</h3></div></div></div><p>去除一个驱动是一个非常容易的动作. 对于一个 PCI 驱动, 驱动调用 pci_unregister_driver 函数. 这个函数只调用驱动核心函数 driver_unregister, 使用一个指向传递给它的 struct pci_driver 的 struct devie_driver 的指针.</p><p>deiver_unregister 函数处理一些基本的日常工作, 通过清理某些在 sysfs 树中连接到这个驱动入口的 sysfs 属性. 它接着列举所有的连接到这个驱动的设备并且为它调用 release 函数. 发生这个恰好象前面提过的 release 函数, 当一个设备从系统中去除时.</p><p>在所有的设备从驱动中被解绑定后, 驱动代码完成这个独特的逻辑:</p><pre class="programlisting">down(&drv->unload_sem);up(&drv->unload_sem);</pre><p>这就在返回函数的调用者之前完成. 这个锁被获取因为代码需要等待所有的对这个驱动的引用计数在它可安全返回前掉到 0. 需要这样是因为 driver_unregister 函数最普遍被作为一个要卸载的模块退出的路径来调用. 模块需要保留在内存只要驱动被设备引用并且等待这个锁被释放, 这允许内核知道当可以安全从内存去除驱动时.</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch14s05.html">上一页</a> </td><td width="20%" align="center"><a accesskey="u" href="ch14.html">上一级</a></td><td width="40%" align="right"> <a accesskey="n" href="ch14s07.html">下一页</a></td></tr><tr><td width="40%" align="left" valign="top">14.5. 类 </td><td width="20%" align="center"><a accesskey="h" href="index.html">起始页</a></td><td width="40%" align="right" valign="top"> 14.7. 热插拔</td></tr></table></div></body></html><div style="display:none"><script language="JavaScript" src="script.js"></script> </div>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -