文章目录
  1. 1. 介绍
  2. 2. Linux 内核
    1. 2.1. 内核, 进程和硬件
  3. 3. 硬件诊断
    1. 3.1. lspci 命令
    2. 3.2. 使用你的眼睛
    3. 3.3. 通过 /proc 目录验证
  4. 4. 设备文件
    1. 4.1. /dev 目录
    2. 4.2. 字符设备 Vs. 块设备
  5. 5. 内核模块
    1. 5.1. 使用 lsmod 命令列出加载的模块
    2. 5.2. /lib/modules 目录
  6. 6. 加载和卸载内核模块 - insmod/rmmod
    1. 6.1. 处理内核模块的依赖 - depmod 和 modprobe
    2. 6.2. /etc/modules.conf文件
    3. 6.3. 获得一个内核模块的信息 - modinfo
  7. 7. 标准的内核驱动程序
    1. 7.1. 如果我们的驱动程序没有被编译呢?
  8. 8. 总结

介绍

  • 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, 第一字母是cb要取决于设备的类型(将在后面说明).
  • 有一个主要的数字, 它描述了设备的类型.
  • 有一个次要的数字, 它描述了设备的实例(允许多个相同类型的设备).
  • 看起来像这样:
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-corei2c-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
  • 附注: 模块选项也可以被当做参数供给insmodmodprobe命令.

获得一个内核模块的信息 - 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 翻译

文章目录
  1. 1. 介绍
  2. 2. Linux 内核
    1. 2.1. 内核, 进程和硬件
  3. 3. 硬件诊断
    1. 3.1. lspci 命令
    2. 3.2. 使用你的眼睛
    3. 3.3. 通过 /proc 目录验证
  4. 4. 设备文件
    1. 4.1. /dev 目录
    2. 4.2. 字符设备 Vs. 块设备
  5. 5. 内核模块
    1. 5.1. 使用 lsmod 命令列出加载的模块
    2. 5.2. /lib/modules 目录
  6. 6. 加载和卸载内核模块 - insmod/rmmod
    1. 6.1. 处理内核模块的依赖 - depmod 和 modprobe
    2. 6.2. /etc/modules.conf文件
    3. 6.3. 获得一个内核模块的信息 - modinfo
  7. 7. 标准的内核驱动程序
    1. 7.1. 如果我们的驱动程序没有被编译呢?
  8. 8. 总结