内核, 模块, 驱动
更新日期:
介绍
- Linux 是一个配备了多种硬件类型内置驱动程序的内核.
- 但是对于较新的硬件, 安装程序并不是总能配置一个合适的驱动…
- …或是在我们发行版的内核中找不到相应的驱动程序.
- 在这些情况下, 我们会想去找到一个正确的驱动安装并且配置它, 使得能够正常工作.
- 这次的讲座里, 我们将会获得一个”怎样做”的初步想法.
Once again, remember what sets off “the hitch-hikers guide to the galaxy(银河系漫游指南)” from “the encyclopedia Galactica(银河系百科全书)”: It has, written on its top, the phrase: Don’t Panic!
Linux 内核
- 是一个作为操作系统的计算机程序.
- 允许应用程序以更加通用的方式访问硬件.
- 使得在系统启动时获得加载, 并启动进程, 使我们能够登录并运行程序.
- 是由驻留在一个单独的文件, 和一堆只有它们需要时才加载的较小的模块所组成一个”核心”.
内核, 进程和硬件
如果我们画一个操作系统的抽象布局设计, 它可能看起来像下图:
硬件诊断
- 为了给各种硬件配置驱动程序, 我们首先必须正确的识别它.
- 通常情况下, 我们买来的产品的名称是未知的, 或者过于笼统.
- 我们最感兴趣的是什么呢? 就是在硬件设备中找到的制造商名称和芯片(组)模块…
- …因为这就是驱动程序最终要与之通讯的.
lspci 命令
- 使我们能够在系统里识别硬件型号.
- 提供的这些是PCI卡(而不是旧的ISA卡).
输出如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | [choo@simey ~]$ /sbin/lspci 00:00.0 Host bridge: VIA Technologies, Inc.: Unknown device 3189 00:01.0 PCI bridge: VIA Technologies, Inc.: Unknown device b168 00:0a.0 Communication controller: Conexant HSF 56k HSFi Modem (rev 01) 00:0b.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8139/8139C (rev 10) 00:0c.0 Multimedia audio controller: Creative Labs SB Live! EMU10k1 (rev 0a) 00:0c.1 Input device controller: Creative Labs SB Live! MIDI/Game Port (rev 0a) 00:10.0 USB Controller: VIA Technologies, Inc. USB (rev 80) 00:10.1 USB Controller: VIA Technologies, Inc. USB (rev 80) 00:10.2 USB Controller: VIA Technologies, Inc. USB (rev 80) 00:10.3 USB Controller: VIA Technologies, Inc. USB 2.0 (rev 82) 00:11.0 ISA bridge: VIA Technologies, Inc.: Unknown device 3177 00:11.1 IDE interface: VIA Technologies, Inc. Bus Master IDE (rev 06) 00:13.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8139/8139C (rev 10) 01:00.0 VGA compatible controller: nVidia Corporation NV17 [GeForce4 MX440] (rev a3) |
一个调制解调器(modem), 2块声卡, 2块网卡, 一个USB接口, 一个IDE控制器, 和一个屏幕控制器.
使用你的眼睛
- 附注: 当你的内核不能识别一些硬件设备时, 才使用这种方法 (例如: 原因是这些硬件在全球首次发售前, 你的Linux发行版就发布了).
- 打开你的电脑机箱(如果这些设备是在机箱内).
- 搜寻你想看到的硬件设备.
- 查看最大的那个芯片, 读取其标签.
- 记下全部的内容, 把一部分内容放到Google搜索.
通过 /proc 目录验证
/proc
目录是一个虚拟的目录, 它允许直接与运行中的Linux内核交互.- 当我们试图去打印某个文件时, 内核就会立刻(on-the-fly:实时)生成它的内容. 在这个目录里是没有真实的磁盘文件存在的.
以下是一些有趣的的硬件诊断文件(使用
cat <源文件>
命令来查看它们):/proc/interrupts
- 不同的驱动程序目前所使用的IRQ中断号列表./proc/ioports
- 驱动程序目前所使用的I/O地址列表./proc/pci
- 关于PCI驱动程序的信息./proc/cpuinfo
- 关于我们CPU的信息.
设备文件
- 代表硬件设备到用户模式应用程序.
- 第一眼看起来像是普通的文件.
- 不包含任何数据 - 它们只用做通讯用.
- 使用
ls -l
, 第一字母是c
或b
要取决于设备的类型(将在后面说明). - 有一个主要的数字, 它描述了设备的类型.
- 有一个次要的数字, 它描述了设备的实例(允许多个相同类型的设备).
- 看起来像这样:
1 2 3 4 | [choo@simey ~]$ ls -l /dev/hda brw-rw---- 1 root disk 3, 0 Apr 11 2002 /dev/hda [choo@simey ~]$ ls -l /dev/ttyS1 crw-rw---- 1 root uucp 4, 65 Apr 11 2002 /dev/ttyS1 |
/dev 目录
- 系统中所有设备文件的标准路径…
- …但是它能够在其他的目录创建设备文件(例如RedHat安装过程中, 在
/tmp
目录会创建硬盘的设备文件). 例如常见的文件名字如下:
hda
- 首个IDE设备(硬盘, CDROM).hdb3
- 第2个IDE设备的第2个分区(必须是硬盘. CDROM是没有分区的).ttyS0
- 首个串行端口(“COM1”).sda1
- 首个SCSI设备的第一个分区 (硬盘, 一个仿真SCSI设备, 等等.).lp0
- 首个并行端口(LPT1).
字符设备 Vs. 块设备
- 字符设备(
c
)是一个通过发送和接受单个字符(字节, 8位字节)与驱动程序进行通信. - 块设备(
b
)是一个通过发送全部数据块与驱动程序进行通信. - 举个字符设备的例子: 串行端口, 并行端口, 声卡.
- 举个块设备的例子: 硬盘, USB摄像头, 闪存盘.
- 写给用户, 设备类型(块设备或字符设备)并不重要 - 你只要关心硬盘的分区和声卡.
- 然而, 驱动程序员必须关心, 但是这些超出了我们的范围.
内核模块
- 为了减少Linux内核的大小, 它会被分为一个’核心(core:内核)’, 和大量的内核模块.
- 一个内核模块是能被运行中的内核随意加载或卸载的小文件(下一代Linux内核将不再允许卸载模块, 除非另有告知).
- 内核的很多功能能被直接编译到内核中, 或编译成模块.
- 这种设计使得可以更快的加载内核(无需加载或初始化不必要的模块), 以及开发驱动程序(如果你发现了一个bug, 只要卸载模块, 再修复它, 重新编译后, 并再加载它一遍).
使用 lsmod 命令列出加载的模块
为了看到当前加载的模块列表, 使用lsmod
命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | [choo@simey ~]$ /sbin/lsmod Module Size Used by Not tainted ppp_synctty 6368 0 (unused) ppp_async 8032 1 ppp_generic 23692 3 [ppp_synctty ppp_async] slhc 6220 0 [ppp_generic] ipt_LOG 4384 19 (autoclean) ipt_TCPMSS 3168 1 (autoclean) iptable_nat 19668 1 (autoclean) ip_conntrack 20268 1 (autoclean) [iptable_nat] iptable_filter 2464 1 (autoclean) ip_tables 13632 6 [ipt_LOG ipt_TCPMSS iptable_nat iptable_filter] vfat 11804 1 (autoclean) fat 36184 0 (autoclean) [vfat] ext3 64800 3 (autoclean) jbd 47924 3 (autoclean) [ext3] emu10k1 60832 1 ac97_codec 12256 0 [emu10k1] sound 69260 0 [emu10k1] soundcore 6212 7 [emu10k1 sound] mousedev 5024 1 hid 20608 0 (unused) input 5696 0 [mousedev hid] ehci-hcd 16864 0 (unused) usb-uhci 24292 0 (unused) usbcore 71104 1 [hid ehci-hcd usb-uhci] |
/lib/modules 目录
- 包含了我们已经安装的不同内核版本的模块.
- 每个内核一个以内核版本号命名的目录.
模块根据类别分别存放于各目录.
pcmcia
- 笔记本的 PCMCIA 驱动程序.kernel/net
- 与网络相关的模块(防火墙, 额外支持的协议等等…).kernel/drivers
- 各类硬件驱动程序(包括网络驱动程序).kernel/fs
- 文件系统支持(ext3, vfat等等…).kernel/arch
- 特定于体系结构的支持(例如:用来处理特定CPU或主板功能的驱动程序).
加载和卸载内核模块 - insmod/rmmod
- 模块的加载和卸载仅能通过root来执行.
- 使用
insmod
命令加载一个内核模块:
1 2 | [root@simey ~]# insmod eeprom Using /lib/modules/2.4.18-17.7.x/kernel/drivers/sensors/eeprom.o |
使用lsmod
检查, 确认模块已被加载.
- 使用
rmmod
命令卸载一个内核模块:
1 | [root@simey ~]# rmmod eeprom |
使用lsmod
检查, 确认模块已被加载.
处理内核模块的依赖 - depmod 和 modprobe
- 模块可以互相依赖. 例如, 如果加载模块
lm78
, 我们首先需要加载i2c-core
和i2c-proc
. - 使用
depmod
命令构建一个模块依赖列表 - 即以便加载每个模块所需的其他模块. 运行如下:
1 2 3 4 5 6 7 | [root@simey ~]# depmod -a depmod: *** Unresolved symbols in /lib/modules/2.4.18-17.7.x/kernel/arch/i386/kernel/longhaul.o depmod: *** Unresolved symbols in /lib/modules/2.4.18-17.7.x/kernel/arch/i386/kernel/p4-clockmod.o depmod: *** Unresolved symbols in /lib/modules/2.4.18-17.7.x/kernel/arch/i386/kernel/speedstep.o |
- 为了使系统自动照顾这些加载的依赖, 使用
modprobe
命令加载一个模块:
1 | [root@simey ~]# modprobe lm78 |
或使输出详细的信息:
1 2 3 4 5 6 7 8 | [root@simey ~]# modprobe -v lm78 /sbin/insmod /lib/modules/2.4.18-17.7.x/kernel/drivers/i2c/i2c-core.o Using /lib/modules/2.4.18-17.7.x/kernel/drivers/i2c/i2c-core.o Symbol version prefix '' /sbin/insmod /lib/modules/2.4.18-17.7.x/kernel/drivers/i2c/i2c-proc.o Using /lib/modules/2.4.18-17.7.x/kernel/drivers/i2c/i2c-proc.o /sbin/insmod /lib/modules/2.4.18-17.7.x/kernel/drivers/sensors/lm78.o Using /lib/modules/2.4.18-17.7.x/kernel/drivers/sensors/lm78.o |
/etc/modules.conf文件
- 我们使用的模块通常包含”默认”参数(例如网络驱动程序, 声卡等等…).
- 常用命令行:
alias
- 指定为一个给定的硬件设备使用一个给定的模块(驱动程序). 例如:alias eth0 8139too
options
- 给一个被加载的给定的模块提供选项. 例如:options sb io=0x220 irq=5 dma=1 dma16=0 mpu_io=0x310
- 附注: 模块选项也可以被当做参数供给
insmod
和modprobe
命令.
获得一个内核模块的信息 - modinfo
- 为了获得一个模块的信息(作者, 所提供的选项), 我们可以使用
modinfo
命令. - 例如有关
mousedev
模块的信息:
1 2 3 4 5 6 7 | [choo@simey ~]$ /sbin/modinfo mousedev filename: /lib/modules/2.4.18-17.7.x/kernel/drivers/input/mousedev.o description: "Input driver to PS/2 or ImPS/2 device driver" author: "Vojtech Pavlik " license: "GPL" parm: xres int, description "Horizontal screen resolution" parm: yres int, description "Vertical screen resolution" |
- 也能使用模块的源代码获得关于它的信息. 在某种情况下, 在源代码文件的顶部会有很多有趣的注释.
标准的内核驱动程序
- 许多驱动程序做为发行版内核的一部分. 使用它们.
- 正如我们所看到的, 这些驱动程序被存储在
/lib/modules/
目录中. - 有时候这些模块文件名将暗示它支持的硬件类型.
- 往往我们可以通过Google搜索获得模块的名称, 假设我们寻找芯片组, 而不是硬件的商品名称.
- 最终在产品制造公司的网站寻找, 或许有提供芯片组的驱动程序. 如果我们幸运的话, 这个驱动程序已经是我们内核的一部分, 并且我们不需要再去下载它.
如果我们的驱动程序没有被编译呢?
- 一些驱动程序或许来自于我们内核源代码的一部分, 然而仍然不被编译进发行版的默认内核.
- 我们能够在内核源码树中寻找到驱动程序…
- …或者可以在网上阅读到它的存在, 或者在内核源代码的文档中(
/usr/src/linux/Documentation
). - 为了编译这个驱动, 我们将需要执行一次完整内核的编译, 然后才会编译驱动程序.
- 通常在第二次后, 我们将不再需要重新编译整个内核 - 仅仅是第二个驱动程序.
总结
- 我们涵盖了Linux内核与进程之间, 和Linux内核与硬件之间的交互.
- 我们看到了什么是内核模块, 以及如何管理它们.
- 我们学习了设备文件和它们的用途.
- 我们学习了识别计算机硬件.
第一次翻译文章, 有任何翻译不到位的地方请多多指教.
原文地址 kernel, modules, drivers @guy keren
译文由 @Ray Lee 翻译