Linux Kernel in a Nutshell - 7
标签: 设备 id linux 00 内核 kernel 0000 PCI Nutshell
Customizing a Kernel 原文链接 我的博客 以问题做关键字搜索,还有问题 构建你自己的 Linux 内核版本最困难的部分,应该就是确定哪一个驱动以及配置选项是你的设备需要的。本章将会手把手带你查找选择合适的驱动。 Using a Distribution Kernel 一个最简单确定需要模块的…
Customizing a Kernel
原文链接
我的博客
以·问题·做关键字搜索,还有问题
构建你自己的 Linux
内核版本最困难的部分,应该就是确定哪一个驱动以及配置选项是你的设备需要的。本章将会手把手带你查找选择合适的驱动。
Using a Distribution Kernel
一个最简单确定需要模块的方法之一,是从发行版内核包附带的内核配置开始。这一方法也能更简单地确定在一个运行的系统上需要哪些驱动,哪些驱动已经与硬件绑定。
如果你的设备上尚且没有安装一个 Linux
发行版本,可以使用 LiveCD
来安装一个。这允许你在你的设备上启动 Linux
,并确定哪些内核配置选项是必须的。
Where Is the Kernel Configuration?
几乎所有的发布版本都提供内核配置文件作为内核发布包的一部分。读取发布版本指定文档,来获取找到这些配置的信息。通常是位于 /usr/src/linux/
目录树下。
如果内核配置难以查找到,查看内核自身。大部分发布版本编译的内核,都将配置信息保存在 /proc
文件系统中。使用下面的命令,来确定是否有相关信息:
1 |
|
如果存在 /proc/config.gz
文件,可以将它复制到内核源码树下并解压:
1 2 3 |
|
将这个配置文件复制到内核目录中,并将其重命名为 .config
。之后使用它作为基础内核配置文件,来编译内核。
使用这个配置文件,总是能够生成能够在你的设备上正常工作的内核。这个方法的缺点是,你需要编译内核源码树中的所有内核模块以及驱动。而对于单个设备而言,很多都是不需要的,因此你需要开始关闭那些不需要的驱动与选项。建议你关闭那些你确定不需要的内容,因为系统的有些功能可能会依赖于某些选项,需要将其使能。
Finding Which Module is Needed
对于来自一个发布版本的配置文件配置的工程,需要编译很长时间,因为所有驱动都会被编译。你希望只编译你有的那些硬件驱动,这会节省编译内核的时间,并允许你选择是将部分驱动还是全部驱动编译到内核中,这可以节省内存,并在某些架构中,令系统的运行速度变快。想要裁剪驱动,就先要确定你的硬件需要哪些模块。我们将要介绍两个例子,来讲解如何查找到所控制硬件的驱动。
在你的系统的某些位置,存储着运行的内核确定设备绑定的对应驱动的信息。最重要的位置是称作 sysfs
的虚拟文件系统。sysfs
总是被 Linux
的初始化脚本挂载在文件系统的 /sys
路径下。sysfs
提供了一个视角,让我们看到不同区块的内核是如何使用指向整个文件系统的符号链接钩到一起的。
在下面所有例子中,展示了硬件类型以及 sysfs
路径。你的设备可能不同,但是信息的相对位置还是相同的。如果你的设备上的 sysfs
文件名不太一样,不要惊讶。
需要另外说明的是,sysfs
文件系统的内部结构总是变来变去,以向重新识别设备并以最佳方式向用户空间展示内核信息。因此,随着时间的推移,本章中提到的东西可能都不存在了。不过,信息依旧是存在的,只是修改了一点点。
Example: Determining the network driver
系统中最常见也是最重要的设备是网络接口控制卡。它用来确定哪一个驱动控制这个设备,并在内核配置中使能这个驱动,保证网络能够正常工作。
首先,回退到网络链接名找到是什么 PCI
设备控制着它。使用下面的命令查看不同的网络名:
1 2 |
|
其中 lo
目录为网络回环设备,并不会真正连接到真实的网络设备。eth0
、eth1
、eth2
目录是我们需要注意的内容,它们代表着真实的网络设备。
更深入的查看网络设备,来确定你关心的内容,使用 ifconfig
工具:
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 27 28 29 30 |
|
显示详细信息
从这个列表中列出的信息,可以看出 eth0
设备是活跃并处于工作中的网络设备,这可以从下面的信息中看到:
1 2 |
|
这个输出信息展示了它是一个以太网设备,具有有效的 IP
地址。
现在,我们希望确保 eth0
设备能够在我们的新内核上正常工作,因此我们需要找到控制它的驱动。可以通过下面的命令来查看:
1 2 |
|
这个输出信息表示,名为 e1000
的模块控制着 eth0
网络设备。basename
命令将下面的命令压缩成单行命令:
跟着
/sys/class/net/eth0/device
符号链接,进入到/sys/device/
目录下,它包含着控制eth0
的设备信息。注意到,/sys/class/net/eth0
在新版本的内核中可能也是一个符号链接在这个目录中,描述了
sysfs
的设备,具有一个绑定这个设备的符号链接,这个符号链接称作driver
,因此我们去向这个链接在
sysfs
描述驱动的目录中,具有包含驱动的模块的符号链接,这个符号链接称作module
,我们需要的是符号链接的目标。为了获取到这个目标,我们使用readlink
命令,产生类似下面的输出:1
2
$ readlink /sys/class/net/eth0/device/driver/module
../../../../module/e1000
因为我们只关心模块的名称,我们希望跳过
readlink
输出的那些路径信息,直取最右侧的信息,这就是basename
命令做的操作,对这个路径执行下面的命令会产生想要的效果了:1
2
$ basename ../../../../module/e1000
e1000
现在我们有了模块命了,我们需要找到内核配置选项,并控制它。可以查看不同的网络设备配置菜单或查看内核源码来确定具有正确的选项:
1 2 3 4 5 |
|
需要注意的是,将 e1000
替换为你自己查看到的模块信息。上面这个命令查找到的信息中,我们关注的信息为带有 CONFIG_
的那个内容。这是内核需要的使能编译模块的配置选项,在上面的例子中,CONFIG_E1000
是我们要查找的配置选项。
现在,我们有了要配置的内核选项,运行配置菜单:
1 |
|
进入配置菜单之后,按下 /
按键 (初始化查找),之后键入配置选项,省略字串中的掉 CONFIG_
。
内核配置系统会告知你去哪里选择使能这个模块。
1 2 3 4 5 6 7 8 9 10 |
|
location
信息表示如果希望将 E1000
模块编译进入内核,需要将下面的配置使能:
1 2 3 4 5 |
|
上面的步骤适用于内核的任何设备。
Example: A USB device
作为一个例子,让我们查看一个 USB-to-serial
转换器是如何在我们系统中工作的。它现在被连接到 /dev/ttyUSB0
端口,因此,需要查看 sysfs
的 tty
部分内容:
1 2 |
|
我们可以追踪 sysfs
中这个设备使用的控制模块:
1 2 |
|
之后再内核源码树种查找配置选项:
1 2 3 |
|
使用内核配置工具,查找合适的选项来使能 CONFIG_USB_SERIAL_PL2303
选项。
Summary of device discovery
作为总结,下面是我们依据一个已经可以正常工作的驱动查找到它在内核源码的选项的方法:
何理地查找设备绑定的
sysfs
类设备。网络设备在/sys/class/net
中tty
设备在/sys/class/tty
中,其他类型的设备都列在/sys/class
中循着
sysfs
的脉络,查找到控制这个设备的模块名称,可以在/sys/class/class_name/device_name/device/driver/module
中查找到,使用readlink
以及basename
工具来将模块提取出来:1
$ basename `readlink /sys/class/class_name/device_name/device/driver/module`
在内核源码的
Makefile
中查找带有CONFIG_
的规则:1
$ find -type f -name Makefile | xargs grep module_name
在内核配置文件中查找模块配置选项所处的定位以及需要进行的配置
Let the kernel tell us what we need
我们已经使用 sysfs
以及符号链接来确定模块名,下面是一个简单的脚本实现这个工作:
1 2 3 4 5 6 7 |
|
这个脚本将会遍历 sysfs
并查找所有称为 modalias
的文件。modalias
文件包含模块别名,告知 modprobe
命令那个模块要被加载来控制这个设备。模块别名是设备的设备制造商、ID
、类型以及其他唯一辨识号组成的字串。所有的内核驱动模块内部具有一个设备支持列表,告知内核它支持的设备。modprobe
程序查看这个设备列表,如果查找到匹配项,它会加载模块。
脚本在 modprobe
加载模块之前停止它,只是打印它需要做的行动。这会给到我们控制系统设备的模块列表。输出类似下面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
显示详细信息
这是一系列控制系统上硬件设备的模块。
脚本也可能会打印下面的一些错误信息:
1 2 3 |
|
这表示它不能找到控制这个设备的模块。无需关注这一内容,因为有些设备没有适用于它们的内核驱动。
Determining the Correct Module from Scratch
有时候我们没有机会获取到能够在系统上正常运行的内核,并依此来获取某个硬件设备需要的这却模块。或者你在系统上添加了一个新的硬件,需要确定要配置的内核配置选项来令硬件能够正确工作。本节将会帮助你去确定如何查找到那个配置选项,来令硬件工作起来。
最简单确定哪一个驱动控制这个新设备的方法就是编译源码树中的所有驱动作为模块,并令 udev
启动进程为设备匹配驱动。一旦完成,就可以使用前面介绍过的方法来配置内核选项了。
如果你不想编译所有驱动,或者某种原因这个方法不能使用,就需要更多的工作来确定这个设备的合适驱动了。下面的步骤是负载的,并需要深挖驱动源码。不要害怕,这会帮助你更好的了解你的硬件以及内核源码。
涉及的匹配步骤将会因你使用的设备的不同而不同。我们将会讨论两个最常用的设备: PCI
以及 USB
设备。其中描述的方法也适用于其他类型的设备。
有一个十分重要的点是,内核系统要能够查找到系统上所有的文件系统,最重要的一个为根文件系统。我们将在后面详细讨论。
PCI Devices
PCI
设备按照供应商 ID
以及设备 ID
区分;每一个供应商 ID
与设备 ID
的组合将需要一个唯一驱动。
对于本例,让我们使用一张 PCI
网卡,不能在当前的内核上正常工作。本例列举的内容可能与你的情况不同,使用不同的 PCI
设备与总线 ID
,但是涉及的步骤总是相似的。
首先,查找系统上没有正常工作的的 PCI
设备。使用 lspci
命令查看系统上所有的 PCI
设备。因为我们只关注以太网 PCI
设备,所以我们会限定在以太网相关的 PCI
设备:
1 2 |
|
这是我们希望能正常工作的设备。上面命令的打印信息的投几个位展示了这个 PCI
的总线 ID
,06:04.0
。这个值是我们将要在 sysfs
中查找关于这个设备更多信息时使用的数据。
进入到 sysfs
的 PCI
设备列表中,查看它们的名称:
1 2 3 4 5 6 |
|
PCI
设备的内核编号以 0000:
开头,这不会展示在 lspci
程序打印的信息中。因此要向 06:04.0
前添加上 0000:
并进入到这个目录中:
1 |
|
在这个目录中,我们希望获取到供应商与设备文件名:
1 2 3 4 |
|
这两个分别是这个 PCI
设备的供应商 ID
以及设备 ID
。内核使用这些值来完成设备与驱动的匹配。PCI
驱动告知内核它们所支持的供应商与设备 ID
,这样内核就直到如何将驱动与设备正确绑定。
现在我们知道了这个 PCI
设备的供应商与产品 ID
了,我们需要查找支持这个设备的合适的内核驱动。回到内核源码目录:
1 |
|
最常见的 PCI
的 ID
位置在内核源码的 include/linux/pci_ids.h
头文件中。在其中查找是否有这个内容:
1 2 |
|
上面的定义信息中,PCI_VENDOR_ID_REALTEK
可能就是内核驱动用来支持这个设备厂商的关键字。
为了保险起见,我们也查看一下设备 ID
,它也在其中做了定义:
1 2 |
|
这个定义在后面会用到。
现在,让我们查找涉及到这个供应商的驱动源文件:
1 2 3 4 5 |
|
我们需要看的就是 drivers/net
目录下的这几个源文件。
在编辑器中打开这些文件,查找 PCI_VENDOR_ID_REALTEK
关键字。在 drivers/net/r8169.c
中具有如下代码:
1 2 3 4 5 6 7 8 |
|
所有的 PCI
驱动包含系列支持的设备。这个列表被保存在 pci_device_id
结构体值中,就像上面这个例子中展示的一样。这是我们需要查看的内容,用以确定我们的设备是否被驱动支持。我们的设备值为 0x8139
但是匹配到的值确实 0x8169
和 0x8129
。因此这个驱动不能支持我们的设备。
继续查看下面的文件,/drivers/net/8139too.c
我们查看到 PCI_VENDOR_ID_REALTEK
字串出现在下面的代码中:
1 2 3 4 5 |
|
可以看到这个源文件中的源码提示我们可以使用 PCI_DEVICE_ID_REALTEK_8139
这个关键字作为线索。
我们接着看 drivers/net/8139cp.c
源文件:
1 2 3 4 5 6 7 8 |
|
这里有使用我们的供应商与设备 ID
的组合在 pci_device_id
中出现。这个驱动应该会支持我们的设备。
现在,我们有了驱动名,我们可以参照本章前面小节介绍的内容,来查找要使能的合适的内核配置选项。
作为总结,下面列出了找到某个 PCI
设备驱动的步骤:
使用
lspci
命令,查找到这个设备所处的PCI
总线进入到
/sys/bus/pci/devices/0000:bus_id
目录中读取
PCI
设备的供应商与设备ID
进入到内核源码中,去
include/linux/pci_ids.h
中去查找供应商与设备ID
的信息查找在
pci_device_id
结构体中共同定义有供应商与设备ID
的驱动查看
Makefile
中带有CONFIG_
的信息1
$ find -type f -name Makefile | xargs grep DRIVER_NAME
在内核配置系统中查找对应的配置信息,并进行配置
USB Devices
为 USB
设备查找驱动类似于为 PCI
设备查找驱动。只在查找总线号时有所区别。
在本例中,让我们为一个无线 USB
设备查找驱动。就像在 PCI
设备例子中的一样,本例中的细节会与你遇到的情况有所不同,但是涉及的步骤是相似的。
就像在 PCI
设备中描述的一样,首先要找到 USB
设备的总线号。可以使用 usbutils
包中提供的 lsusb
程序来完成。
lsusb
程序展示所有连接到系统上的 USB
设备:
1 2 3 4 5 6 7 8 9 10 |
|
具有 ID
为 0000:0000
的设备可以忽略,它们是 USB
主控制器,用来驱动总线自身的。将它们剔除之后,剩下四台设备:
1 2 3 4 5 |
|
因为 USB
设备移除起来是十分方便的,因此,我们插拔我们的 USB
设备,再来查找一下:
1 2 3 4 |
|
这样我们就确定了我们的 USB
设备了:
1 |
|
如果我们重新插上设备再次使用 lsusb
,可以看到这个设备号发生了变化:
1 2 |
|
这是因为 USB
设备号并不固定,而是在设备插入时候变化。稳定的内容是供应商与产品 ID
,这里供应商 ID
为 157e
,设备 ID
为 300d
。
就像 PCI
例中阐述的一样,我们将会在内核源码中查找 USB
供应商与产品 ID
,并以此来查找控制这个设备的合适驱动。不幸的是,没有单个文件像 PCI
设备中那样包含所有的 USB
供应商 ID
,因此我们只能搜索所有的内核文件了:
1 2 3 4 5 6 7 8 |
|
我们知道我们的 USB
设备是一个无线设备,不是一个 ATM
或 SCSI
设备,因此,我们可以忽略在 ztm
以及 scsi
目录中的内容。那么就剩下 drivers/net/wireless/zd1211rw/zd_usb.c
文件了。
在 zd_usb.c
源文件中字串 157e
包含在下面一簇代码中:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
像 PCI
驱动一样,USB
驱动也告知内核它们支持的设备有哪些,来让内核将驱动与设备进行绑定。这是通过 usb_device_id
结构体变量来实现的。在这个列表中有类似下面这样驱动支持的供应商与产品 ID
:
1 |
|
这一行展示了我们使用的 USB
设备的供应商与产品 ID
,表示本驱动支持这个 USB
设备。
一旦确定了这个设备需要的驱动名,后面的工作就是前面介绍过的内容了。
总结一下,对于 USB
设备的驱动选项配置过程如下:
使用
lsusb
来查看USB
供应商以及产品ID
在内核源码中查找包含供应商与产品
ID
的驱动usb_device_id
在内核顶层
Makefile
中查找驱动对应的选项1
$ find -type f -name Makefile | xargs grep DRIVER_NAME
配置选项
Root Filesystem
根文件系统是运行系统启动的主文件系统分区。它包含发布版本的所有初始化程序,并包含设备的整个系统配置。简言之,它十分重要,必须能够在启动时被内核查找到,来使内核正常运行。
如果你的新配置内核在启动时挂掉,并有下面的报错:
1 2 3 |
|
那么就是没能成功找到根文件系统。如果你在启动时没有使用一个虚拟盘 (ramdisk) 镜像,那么建议你将文件系统的根分区,盘控制器到内核中,而不是让它们作为模块。如果你在启动时使用虚拟盘 (ramdisk) 镜像,你需要将这些分区编译为模块。
如何确定你在启动时使用的是否是虚拟盘呢?在第五章中我们提到了使用安装脚本来安装内核,或者自己手动安装内核。如果你使用发布版本的安装脚本,那么就可能是使用的虚拟盘。如果你自己手动安装,那么可能就不是。
下面的小节会介绍如何在启动时让内核找到根文件系统。
Filesystem type
首先,根分区使用的文件系统类型是需要确定的。可以查看 mount
命令的输出来确定:
1 2 |
|
我们感兴趣的是文件系统的类型,它位于 type
关键字后面。在本例中,类型为 ext3
。这是根分区使用的文件系统类型。进入到内核配置系统,确保这个文件系统类型已使能。
Disk controller
前面我们使用了 mount
命令输出了一些信息,输出行的第一个信息展示了根文件系统是挂载在那个块设备上的。在本例中,是在 /dev/sda2
上。现在文件系统已经在内核中做了配置,现在需要保证这个块设备能够正常的工作。为了查找到这个块设备需要的驱动,需要再次查看 sysfs
。
所有 sysfs
中的块设备,要么是在 /sys/block
要么在 /sys/class/block
中,这依赖于你使用的内核版本。无论在哪个位置,块设备都是树状结构,不同的分区作为主设备的子节点:
1 2 3 4 5 6 7 |
|
基于我们之前使用 mount
看到的信息,你需要确保 sd2
设备做了正确配置。因为这是一个分区 (盘分区是有编号的,主块设备是没有编号的),完整的 sda
设备需要被配置 (没有主块设备,显然对块设备的分区是没有意义的)。
sda
块设备就像我们之前看到的网络设备一样。在设备的目录中具有符号链接,指向控制这个块设备的逻辑设备:
1 2 3 4 |
|
现在需要查看 sysfs
中的信息来确定哪个驱动控制着这个设备:
1 2 3 4 |
|
我们看到,SCSI
盘控制器驱动着这个设备工作。因此我们需要配置 SCSI
配置选项。
继续在 sysfs
目录链中尝试查找哪个驱动控制着这个硬件:
1 2 |
|
在本级目录中没有称为 driver
的链接,因此去向上一层:
1 2 |
|
依旧没有 driver
,我们继续向上:
1 2 3 4 |
|
终于,这就是我们需要在内核配置中需要使能的盘控制器。
因此我们为了让根文件系统能够正常工作,我们需要使能 ext3
、sd
、以及 ata_piix
驱动能够在内核中正常工作。
Helper Script
在本章开始时,我们提到,sysfs
随着内核版本的改变在不断变化。这里有一个脚本来确定系统中的设备节点需要的内核驱动于模块的模块名。
比如,它可以帮你完成 sda
块设备的需要的驱动查找:
1 2 3 4 5 |
|
我们也可以用它来查找更加复杂的设备的合适的驱动,比如 USB-to-serial
设备:
1 2 3 4 5 6 7 8 9 |
|
你可以在本书的 How to Contact Us
小节中提供的网站上下载这个脚本。
原文链接:https://www.cnblogs.com/arvin-blog/p/16634276.html
Linux Kernel in a Nutshell - 7 - Linux教程 - 找一找教程网 (zyiz.net)
Linux Kernel in a Nutshell - 7相关推荐
- linux 内核调试信息在哪里,Linux kernel debug技巧----开启DEBUG选项
Linux kernel debug技巧----开启DEBUG选项 作者:wowo 发布于:2016-11-1 19:39 分类:Linux应用技巧 kernel的source code中有很多使用p ...
- linux kernel内存回收机制
http://www.wowotech.net/linux_kenrel/233.html 无论计算机上有多少内存都是不够的,因而linux kernel需要回收一些很少使用的内存页面来保证系统持续有 ...
- Linux kernel futex.c的bug导致JVM不可用
JVM死锁导致线程不可用,然后会瞬间起N个线程,当然也是不可用的,因为需要的对象死锁,然后耗尽文件句柄导致外部TCP无法建议拒绝服务,jstack之后就会恢复. 解决办法:替换中间件类库 ,比如htt ...
- linux命令注入,Linux kernel本地命令注入漏洞
Linux kernel本地命令注入漏洞 2005-10-19 eNet&Ciweek Linux kernel 2.2.6 Linux kernel 2.2.5 Linux kernel 2 ...
- 编译Linux Kernel(linux-4.19.178)并制作成rpm文件
目录 一.安装依赖项 二.下载.解压缩.制作.config文件 三.编译内核及打包 四.升级内核 首次尝试编译Linux内核,记录过程,提供Linux Kernel(linux-4.19.178)下载 ...
- linux读取nand的文件,Linux Kernel 之AP读写Nand Flash上的Yaffs2文件的全过程浅析
1.1 用top-down的方法分析AP读一个Nand Flash上的file的全过程 我先简单看一个例子,看User Application如何打开一个Yaffs2 file并读写之: int ma ...
- linux 内核 call,在Linux Kernel內新增一个System Call(转)
在Linux Kernel內新增一个System Call(转)[@more@]本文作者: gpmoney 使用 system call 去呼叫系統的函式是非常好玩的,但是要如何寫出一個自己的syst ...
- g++ linux 编译开栈_使用 linux kernel +busybox 定制linux系统
写在开头: 本来是想使用linux kernel +busybox 制作一个教程的,后来快要结束的时候,死活找不到硬盘,我了解很多文章都有类似的,但是没有谈到硬盘找不到问题,最后历经艰辛,终于把问题解 ...
- linux kernel内存映射实例分析
作者:JHJ(jianghuijun211@gmail.com) 日期:2012/08/24 欢迎转载,请注明出处 引子 现在android智能手机市场异常火热,硬件升级非常迅猛,arm cortex ...
最新文章
- Exchange笔记之使用OWA加密访问邮箱
- 高可用 Redis 服务架构分析与搭建
- 给大家分享微信小说域名防封最新的解决方案
- 从古希腊神话说起,讲讲英语里的偏旁部首
- 用 Ubuntu 重置 Windows 密码
- java sessionstorage_sessionStorage的使用
- pythonrgb高精度浮点运算类型_python实现RGB字符串,按24位对齐后输出对应Integer行数字...
- Ubuntu14.04如何使用root登录
- 开发者在对项目失去信心后,该做什么?
- C语言?看女程序员是怎么往死里坑师兄的
- Qt4_IconEditor窗口部件
- 傅立叶变换,时域,频域一
- 我正在运行哪个版本的PostgreSQL?
- 在rhel6 64位环境下部署LNMP环境
- 【PDF合并】滴滴出行电子发票及行程报销单【一页打印】
- python的认识从唯物主义_中国大学mooc用Python玩转数据章节答案
- 针对三层别墅的两种无线组网方案
- 符冉迪 计算机 培训,采用多模糊支持向量机决策融合的积雨云检测.pdf
- 国内主流云服务器价格概览
- 来吧 带你玩转 Excel VBA