构建自己的Kernel–Module 定制
构建自己的内核最困难的事情是确定让计算机正常工作需要哪些驱动和配置选项。
使用发行版的内核
要确定哪些模块是必须得,最简单的方法是从发行版的内核开始。这是因为,发行版内核已经将正确的驱动和硬件绑定,所以,在运行的系统上确定所需的驱动将变得简单。
几乎所有的发行版都在其内核包中包含内核配置文件,一般都在/usr/src/linux目录树下。
如RHEL5.7,安装完其 kernel-2.6.18-194.el5.src.rpm, 会在/usr/src/redhat/SOURCES下找到kernel的config 文件。
kernel-2.6.18-i586.config kernel-2.6.18-i686.config kernel-2.6.18-i686-debug.config kernel-2.6.18-i686-PAE.config kernel-2.6.18-i686-xen.config kernel-2.6.18-ia64.config kernel-2.6.18-ia64-debug.config kernel-2.6.18-ia64-xen.config kernel-2.6.18-ppc64.config kernel-2.6.18-ppc64-debug.config kernel-2.6.18-ppc64-kdump.config kernel-2.6.18-ppc.config kernel-2.6.18-ppc-smp.config
如果找不到内核配置文件,可以查看当前运行内核是否包含这些配置选项。如果正在运行的内核的CONFIG_IKCONFIG 和 CONFIG_IKCONFIG_PROC选项是打开的,则在/proc/config.gz文件中包含当前正在运行内核的配置选项。
1: cp /proc/config.gz ~/linux
2: cd ~/linux
3: gzip -dv config.gz
通过上面的命令可以得到当前使用的内核配置选项。接下来,你就可以根据该配置文件,来定制你自己的内核。
这种方式有一个缺点,这是因为通过这个配置文件产生的内核镜像会包含内核源码树中的所有内核模块和驱动。而这对于单独一台机器是不需要的,所以你可以关闭一些不需要的驱动和选项。
查找哪些模块是需要的 (设备和驱动已经绑定)
要剪裁驱动,需要确定驱动硬件需要哪些模块。系统中有个几个文件存放了设备和驱动的映射信息,其中最重要的是sysfs 虚拟文件系统,其被初始化脚本挂载到/sys挂载点上。
例子一:确定网络设备驱动
1: # ls /sys/class/net
2: eth0 eth1 eth2 eth3 lo sit0
# ifconfig eth0 Link encap:Ethernet HWaddr 00:19:BB:25:2B:36
inet addr:10.101.2.2 Bcast:10.255.255.255 Mask:255.0.0.0 inet6 addr: fe80::219:bbff:fe25:2b36/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:4130304 errors:0 dropped:0 overruns:0 frame:0 TX packets:31156 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:554668333 (528.9 MiB) TX bytes:8029978 (7.6 MiB) Interrupt:201 Memory:f7cf0000-f7d00000
从如上命令的结果可以得知,eth0是活动并且是处于工作状态的网络设备,所以我们接下来查找控制它的驱动程序。
1: # basename `readlink /sys/class/net/eth0/device/driver/module`
2: tg3
可以看到,其驱动的名称是tg3。所以,根据tg3我们来查找对应于kernel config选项名称。在内核源码根目录下:
1: # find -type f -name Makefile | xargs grep tg3
2: ./drivers/net/Makefile:obj-$(
CONFIG_TIGON3
) += tg3.o
可以看到,对应于tg3 模块的内核配置选项为CONFIG_TIGON3.
接下来,我就可以根据CONFIG_TIGON3在查找在哪里可以对其进行配置。
1: make menuconfig
结果为:
设备查找一般步骤小结:
1 在sysfs的class文件夹下找到设备设备对应的文件,网络设备在/sys/class/net下,tty设备在/sys/class/tty目录下。其他设备根据其种类不同罗列在/sys/class其它目录下。
2 跟踪sysfs找到控制这个设备的模块名称,通常会在/sys/class/class_name/device_name/devic/module 中,并且可以使用readlink和basename程序显示出来。
3 使用find加grep命令在内核的Makefile中查找用于构建该模块的以CONFIG_为前缀的配置项。
find –type f –name Makefile | xargs grep module_name
4 在内核配置系统中搜索这个值并到相应的菜单中相应的位置启动这个驱动(build-in or module)
从零开始确定正确的模块
有时候,不能使用先前所使用的方法来确定驱动硬件需要加载哪些模块。或者系统新增加了新的硬件,你需要找出使他正常工作的内核配置选项。
【1】 PCI设备
为新增加的PCI设备匹配驱动
PCI设备是依靠供应商ID(Vendor ID)和设备ID(Device ID)来识别,每个供应商和设备ID的组合就能唯一确定一个驱动程序。
首先,找到系统中不能使用的PCI设备。要得到所有PCI设备的列表,通过lspci。假设我们仅关注ethernet 设备,所以:
1: # lspci | grep -i ethernet
2: 02:02.0 Ethernet controller: Broadcom Corporation NetXtreme BCM5704 Gigabit Ethernet (rev 10)
3: 02:02.1 Ethernet controller: Broadcom Corporation NetXtreme BCM5704 Gigabit Ethernet (rev 10)
4: 05:01.0 Ethernet controller: Broadcom Corporation NetXtreme BCM5703 Gigabit Ethernet (rev 10)
5: 05:02.0 Ethernet controller: Broadcom Corporation NetXtreme BCM5703 Gigabit Ethernet (rev 10)
lspci输出结果中得第一个字段是该设备的PCI总线ID,02:02.0。 这就是我们在sysfs中查找此设备时所使用的关键字。
cd /sys/bus/pci/devices ls 0000:00:03.0 0000:00:04.3 0000:00:08.0 0000:00:18.1 0000:00:19.0 0000:00:19.3 0000:01:03.0
0000:02:02.0
0000:04:09.0 0000:04:0a.1 0000:06:01.0 0000:00:04.0 0000:00:07.0 0000:00:08.1 0000:00:18.2 0000:00:19.1 0000:01:00.0 0000:01:04.0 0000:02:02.1 0000:04:09.1 0000:05:01.0 0000:06:01.1 0000:00:04.1 0000:00:07.1 0000:00:18.0 0000:00:18.3 0000:00:19.2 0000:01:00.1
0000:02:02.0 就是我们要找的PCI设备。
1: cd 0000\:02\:02.0
2: ls
3: broken_parity_status class device enable local_cpus net:eth0 resource subsystem subsystem_vendor vendor
4: bus config driver irq modalias power resource0 subsystem_device uevent
5:
6: cat vendor
7: 0x14e4
8: # cat device
9: 0x1648
如上,可以得到vendor ID 为0x14e4和device ID为0x1648。
接下来,根据vendor ID和device ID在内核的源代码树里寻找支持该PCI设备的驱动。
PCI ID在内核源码树里最常见的位置是include/linux/pci_ids.h
1: grep -i 0x14e4 include/linux/pci_ids.h
2: #define PCI_VENDOR_ID_BROADCOM 0x14e4
3: # grep -i 0x1648 include/linux/pci_ids.h
4: #define PCI_DEVICE_ID_TIGON3_5704 0x1648
可以得到对应的供应商的宏定义为 PCI_VENDOR_ID_BROADCOM ,而设备对应的宏为PCI_DEVICE_ID_TIGON3_5704 。
接下来寻找使用供应商宏的源文件:
# grep -Rl PCI_VENDOR_ID_BROADCOM *
drivers/net/b44.c
drivers/net/wireless/bcm43xx/bcm43xx_main.c
drivers/net/wireless/bcm43xx/bcm43xx_phy.c
drivers/net/wireless/bcm43xx/bcm43xx_radio.c
drivers/net/bnx2.c
drivers/net/tg3.c
include/linux/pci_ids.h
打开drivers/net/tg3.c ,搜索PCI_VENDOR_ID_BROADCOM, 可以找到:
static struct pci_device_id tg3_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5700)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5701)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5703)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5704
)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5702FE)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705_2)},
所有,tg3.c是对应的驱动程序。
接下来,可以根据该module的名称tg3,通过前面介绍的方法,来找到对应的kernel配置选项。
附: 查找对应设备的驱动和模块的脚步
1: #!/bin/sh
2:
3: #
4: # find all modules and drivers for a given class device
5: #
6: if [ $# != "1" ]; then
7: echo
8: echo "Script to display the drivers and moduels for a specified sysfs class device"
9: echo "Usage: $0 <CLASS_NAME>"
10: echo ""
11: echo "example usage:"
12: echo " $0 sda"
13: echo "Will show all drivers and modules for the sda block device"
14: exit 1
15: fi
16:
17: DEV=$1
18: if test -e "$1"; then
19: DEVPATH=$1
20: else
21: DEVPATH=$(find /sys/class -name "$1" | head -1)
22: test -z "$DEVPATH" && DEVPATH=$(find /sys/block -name "$1" | head -1)
23: test -z "$DEVPATH" && DEVPATH=$(find /sys/bus -name "$1" | head -1)
24: if ! test -e "$DEVPATH"; then
25: echo "no device found."
26: exit 1
27: fi
28: fi
29:
30: echo "looking at sysfs device: $DEVPATH"
31: if test -L "$DEVPATH" ; then
32: DEVPATH=$(readlink -f $DEVPATH)
33: echo "resolve link to $DEVPATH"
34: fi
35:
36: if test -d "$DEVPATH"; then
37: #resolve old-style device link to the parent device
38: PARENT="$DEVPATH";
39: while test "$PARENT" != "/"; do
40: if test -L "$PARENT/device"; then
41: DEVPATH=$(readlink -f $PARENT/device)
42: echo "follow device link to parent: $DEVPATH"
43: break;
44: fi
45: PARENT=$(dirname $PARENT)
46: done
47: fi
48: while test "$DEVPATH" != "/" ; do
49: DRIVERPATH=
50: DRIVER=
51: MODULEPATH=
52: MODULE=
53: if test -e $DEVPATH/driver; then
54: DRIVERPATH=$(readlink -f $DEVPATH/driver)
55: DRIVER=$(basename $DRIVERPATH)
56: echo -n "found driver: $DRIVER"
57: if test -e $DRIVERPATH/module; then
58: MODULEPATH=$(readlink -f $DRIVERPATH/module)
59: MODULE=$(basename $MODULEPATH)
60: echo -n " from module: $MODULE"
61: fi
62: echo
63: fi
64: DEVPATH=$(dirname $DEVPATH)
65: done