《Linux内核动态加载模块.pdf》由会员分享,可在线阅读,更多相关《Linux内核动态加载模块.pdf(7页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、Linux 内核动态加载模块 一、安装内核模块:一般步骤:(1)在/usr/src/linux/下运行 make menuconfig 把需要编译成模块的项打上(M),保存并退出。(2)运行 make modules,这一步将在/usr/src/linux/下生成*.o 或*.ko 文件。(3)运 行 make modeules_install 来 安 装,这 步 会 把 生 成 的.o 或 ko 文 件 拷 贝 到/lib/modules/uname-r/下。如果你只要编译某一个或几个模块,就可以用下面这个快速的方法:(1)找到编译内核所需要的.config 文件。在/usr/src/lin
2、ux/arch 目录下有若干编译内核所用的配置。选择我们想要的配置,将它复制到/usr/src/linux 目录下,改名为.config。cp/usr/src/linux/arch/x86/xxconfig/usr/src/linux/.config (2)修改.config 文件,去掉不用的模块,加上自己想要的模块。打开.config,有许多 XXXX=m 的项,这些都是要被编译 为模块的项,因为我们不希望编译这些模块,所以要把 XXXX=m 的项统统去掉。然后再加上我们想要的模块,例如将#CONFIG_NTFS_FS is not set 改为 CONFIG_NTFS_FS=m 当然,可以
3、用你熟悉各种工具来做这件事。(3)编译 NTFS 模块。在/usr/src/linux 目录下运行命令 make modules 来编译我们想要的模块。(4)安装模块。编译后得到的.o 文件在/usr/src/linux/目录下,手动将它复制到正确的目录下。例如 cp/usr/src/linux/fs/ntfs/ntfs.o/lib/modules/2.2.16-22/fs/注意:千万不能运行命令 make modules_install,否则将带来严重的后果,它会删除你系统中的所有模块,只安装刚刚编译的模块(ntfs.o)。二:安装完成以后,我们就可以加载模块了:和 linux 中加载模块有
4、关的几个命令分别如下:depmod,modprobe,lsmod 先来看看 depmod 命令:depmod 是一个用来产生 modules.dep 和 map 文件的程序。在 modules.dep 文件中空白行和以#开头的行将被忽 略.depmod 通过读取/lib/modules/version 目录下的每一个模块来创建一个记录模块相依性的列表。这个列表就是/lib/modules/version 目录下的 modules.dep。depmod也会在/lib/modules/version目录下创建许多map文件,例如 modules.dep,modules.isapnpmap,modu
5、les.pcimap,modules.alias 这些文件将会被 hotplug 用到。OPTIONS:-a-all Probe all modules.This option is enabled by default if no file names are given in the command-line.检查所有的模块,这个命令是默认的如果你没有指定模块名字的话。-A-quick This option scans to see if any modules are newer than the modules.dep file before any work is done%3 再
6、来看看 modprobe 命令:modprobe 命令是根据 depmod-a 的输出/lib/modules/version/modules.dep 来加载全部的所需要模块。可以通过 modprobe-l 来显示可以当前可以加载的模块。modprobe 在挂载模块是不用指定模块文件的路径,也不用带文件的后缀.o 或.ko,而 insmod 需要的是模块的所在目录的绝对路径,并且一定要带有模块文件名后缀的(modulefile.o 或 modulesfile.ko)。insmod比较重要的用途是用来测试模块的正确性,加载一般都是依靠 modprobe。用法:modprobe xxx.ko#加载
7、某个模块 modprobe-r xxx.ko#卸载某个模块 lsmod:lsmod 显示当前加载的所有 模块,相当于 cat/proc/modules,假设你没有设定开机加载某个模块,比如 ntfs,那么开机后执行 lsmod,列表里不会有 ntfs 这个模块的,这时你再执行 mount-t ntfs xxx 后,执行 lsmod 后列表里就会有 ntfs 这个模块了。还要注意的是 lsmod 显示的是模块名,而不是别名(alias)。三、在内核中有一个“Automatic kernel module loading功能被编译到了内核中。当用户尝试打开某类型的文件时,内核会根据需要尝试加载相应
8、的模块。我们来看看驱动程序自动加载是怎么实现的:每一个设备都有 Verdon ID,Device ID,SubVendor ID 等信息。而每一个设备驱动程序,必须说明自己能够为哪些 Verdon ID,Deviece ID,SubVendor ID 的设备提供服务。以 PCI 设备为例,它是通过一个 pci_device_id 的数据结构来实现这个功能的。例如:RTL8139 的 pci_device_id 定义为:static struct pci_device_id rtl8139_pci_tbl=0 x10ec,0 x8139,PCI_ANY_ID,PCI_ANY_ID,0,0,RTL
9、8139,0 x10ec,0 x8138,PCI_ANY_ID,PCI_ANY_ID,0,0,RTL8139,.在模块安装的时候,depmod 会根据模块中的 rtl8139_pci_tbl 的信息,生成下面的信息,保存到/lib/modules/uname-r/modules.alias 文件中,其内容如下:alias pci:v000010ECd00008138sv*sd*bc*sc*i*8139too alias pci:v000010ECd00008139sv*sd*bc*sc*i*8139too .另外在/lib/modules/uname-r/modules.dep 文件中还保存这
10、模块之间的依赖关系,其内容如下:(这里省去了路径信息。)8139too.ko:mii.ko 在内核启动过程中,总线驱动程序会会总线协议进行总线枚举(总线驱动程序总是集成在内核之中,不能够按模块方式加载,你可以通过 make menuconfig 进入 Bus options,这里面的各种总线,你只能够选择 Y 或 N,而不能选择 M.),并且为每一个设备建立一个设备对象。每一个总线对象有一个 kset 对象,每一 个设备对象嵌入了一个 kobject 对象,kobject 连接在 kset 对象上,这样总线和总线之间,总线和设备设备之间就组织成一颗树状结构。当总线驱 动程序为扫描到的设备建立设
11、备对象时,会初始化 kobject 对象,并把它连接到设备树中,同时会调用 kobject_uevent()把这个(添加新设 备的)事件,以及相关信息(包括设备的 VendorID,DeviceID 等信息。)通过 netlink 发送到用户态中。在用户态的 udevd 检测到这个 事件,就可以根据这些信息,打开/lib/modules/uname-r/modules.alias 文件,根据 alias pci:v000010ECd00008138sv*sd*bc*sc*i*8139too 得知这个新扫描到的设备驱动模块为 8139too。于是 modprobe 就知道要加载 8139too
12、这个模块了,同时 modprobe 根据 modules.dep 文件发现,8139too 依赖于 mii.ko,如果 mii.ko 没有加载,modprobe 就先加载mii.ko,接着再加载 8139too.ko。试验 在你的 shell 中,运行:#ps aux|grep udevd#kill-9 25063 然后跟踪 udevd,在 shell 中运行:#strace-f/sbin/udevd-daemon 这时,我们看到 udevd 的输出如下:.close(8)=0 munmap(0 xb7f8c000,4096)=0 select(7,3 4 5 6,NULL,NULL,NULL
13、 我们发现 udevd 在这里被阻塞在 select()函数中。select 函数原型如下:int select(int nfds,fd_set*readfds,fd_set*writefds,fd_set*exceptfds,struct timeval*timeout);第一个参数:nfds 表示最大的文件描述符号,这里为 7(明明是 6?)。第二个参数:readfds 为读文件描述符集合,这里为 3,4,5,6.第三个参数:writefds 为写文件描述符集合,这里为 NULL。第四个参数:exceptfds 为异常文件描述符集合,这里为 NULL。第五个参数:timeout 指定超时时
14、间,这里为 NULL。select 函数的作用是:如果 readfds 中的任何一个文件有数据可读,或者 witefds 中的任何一个文件可以写入,或者 exceptfds 中的任 何一个文件出现异常时,就返回。否则阻塞当前进程,直到上诉条件满足,或者因阻塞时间超过了 timeout 指定的时间,当前进程被唤醒,select 返 回。所以,在这里 udevd 等待 3,4,5,6 这几个文件有数据可读,才会被唤醒。现在,到 shell中运行:#ps aux|grep udevd root 27615 .strace-o/tmp/udevd.debug-f/sbin/udevd-daemon r
15、oot 27617 ./sbin/udevd-daemon udevd 的进程 id 为 27617,现在我们来看看 select 等待的几个文件:#cd/proc/27615/fd#ls-l udevd 的标准输入,标准输出,标准错误全部为/dev/null.0-/dev/null 1-/dev/null 2-/dev/null udevd 在下面这几个文件上等待。3-/inotify 4-socket:331468 5-socket:331469 6-pipe:331470 7-pipe:331470由于不方便在运行中插入一块 8139 的网卡,因此现在我们以一个 U 盘来做试验,当你插入
16、一个 U 盘后,你将会看到 strace 的输出,从它的输出可以看到 udevd 在 select 返回后,调用了 modprobe 加载驱动模块,并调用了 sys_mknod,在 dev 目录下建立了相应的节点。execve(/sbin/modprobe,/sbin/modprobe,-Q,usb:v05ACp1301d0100dc00dsc00dp00.mknod(/dev/sdb,S_IFBLK|0660,makedev(8,16)=0.这里 modprobe 的参数usb:v05AC.对应 modules.alias 中的某个模块。可以通过 udevmonitor 来查看内核通过 ne
17、tlink 发送给 udevd 的消息,在 shell 中运行:#udevmonitor-env 然后再插入 U 盘,就会看到相关的发送给 udevd 的消息。四、内核模块加载的配置:有时候需要一次性加载许多模块,需要在一个地方统一配置 modprobe 的选项等,有一个比较重要的文件:/etc/modprobe.conf,在 openSUSE 中,和它有关的还有 modprobe.d/文件夹下的许多文件和 modprobe.conf.local 文件,在/etc/modprobe.conf里会 include 其它所说的文件,一般建议在 modprobe.conf.local 中修改自己的配
18、置。/etc/modprobe.conf 其实就是用于 写入模块的加载命令或模块的别名的定义等。man modprobe.conf:alias my-mod really_long_modulename 为模块定义一个便于使用的别名 options modulename option.在加载模块时添加选项 install modulename command.使用自己定义的命令去加载指定的模块,如 install fred/sbin/modprobe barney;/sbin/modprobe -ignore-install fred 每次加载 fred 模块的时候用的是“/sbin/modp
19、robe barney;/sbin/modprobe -ignore-install fred”命令 remove modulename command.用自己定义的命令删除指定的模块。include filename 引入其它文件 blacklist modulename 不再加载某个模块 五、内核模块开机自动挂载:对于开机自动挂载模块,在 RedHat 系 统里,网上说在内核启动的过程中,init 执行/etc/rc.d/rc.sysinit 后,启动内核外挂模块 时会读取/etc/modprobe.conf 这个文件。在 2.4 的内核中,只要直接修改/etc/modprobe.conf
20、 加入 install xxx 即可。2.6 内核则需修改/etc/rc.d/rc.sysinit 文件。具体的过程可以看:http:/ 而在 SUSE 里,可以在 root 权限编辑/etc/sysconfig/kernel 文件,添加需要启动的模块。Linux 如何在系统启动时自动加载模块 为搞清楚如何在系统启动时自动加载模块,搜索了好久,网上有很多人提出这个问题,但都没有正确的答案,无论是中文社区还是英文社区,大家的回答都没 有讲到点子上,无非是围绕 modprobe.conf、modprobe 讲来讲去的,要不就是针对特定问题尝试不同的方法。有的还建议把 modprobe module
21、name 写入 rc.local,却不曾想,rc.local 的执行被放在整个启动顺序的很后面,而启动 init.d 下面定义的服务却在 rc.local 前面,那么如果某个服务要用这个模块,就不行了。在测试 LVS 时,因为我的 Fedora7 的 Kernel(2.6.21-1)缺省没有加载 ip_vs 模块,而内核中已经 包 含 编 译 好 的IPVS相 关 的 模 块 了,放 在:/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/下面,有:/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/
22、ipvs/ip_vs.ko/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_dh.ko/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_ftp.ko/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_lblc.ko/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_lblcr.ko/lib/modules/2.6.21-1.3194.fc
23、7/kernel/net/ipv4/ipvs/ip_vs_lc.ko/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_nq.ko/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_rr.ko/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_sed.ko/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_sh.ko/lib/modules/
24、2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_wlc.ko/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/ip_vs_wrr.ko 其中 ip_vs.ko 是 IPVS 的基本模块,不加载 IPVS 就不能工作(运行 ipvsadm 会报错的),而其他的都是 IPVS 的调度算法或特定协议的辅助模块,需要时则须加载。如果系统运行时手动加载则需:modprobe ip_vs 和 modprobe ip_vs_sh 等。要了解如何在系统启动时自动加载模块(Automatically load ker
25、nel modules),就得先了解系统是如阿启动的,启动的过程中按什么顺序做了什么,怎么做的,这些启动操作都有那些文件和脚本控制。由于 Google 和 Baidu 出来的东西都解决不了问题,而且 man modprobe 和 man modprobe.conf 发现并不是需要修改的文件。于是温习鸟哥的 urlhttp:/linux.vbird.org/url“开机关机流程与 Loader”:1.整个开机流程是(1)载入 BIOS 的硬件信息,并取得第一个开机装置的代号(2)读取第一个开机装置的 MBR 的 boot Loader(grub)的开机信息(3)载入 OS Kernel 信息,解
26、压 Kernel,尝试驱动硬件(4)Kernel 执行 init 程序并获得 run-lebel 信息(如 3 或 5)(5)init 执行/etc/rc.d/rc.sysinit(6)启动内核外挂模块(/etc/modprobe.conf)(7)init 执行 run-level 的各种 Scripts,启动服务(8)init 执行/etc/rc.d/rc.local(9)执行/bin/login,等待用户 Login(10)Login 后进入 Shell 看来正确的方式是把需要加载的模块放在(5)或(6),但正如网络上很多人的尝试,修改modprobe.conf 都没有成功(例如在 mod
27、probe.conf 中增加 install ip_vs.)。于是我修改了/etc/rc.d/rc.sysinit 就成功加载了。初步尝试在 rc.sysinit 最后增加 modprobe.conf ip_vs,重启后 lsmod|grep ip_vs,发现成功自动加载了。于是仿效 rc.sysinit 中其他模块的加载方法,扩展改脚本文件,在最后增加下来一段:#load LVS IPVS modules if -d/lib/modules/$unamer/kernel/net/ipv4/ipvs;then for module in/lib/modules/$unamer/kernel/n
28、et/ipv4/ipvs/*;do module=$module#*/module=$module%.ko modprobe$module/dev/null 2&1 done fi 就把/lib/modules/2.6.21-1.3194.fc7/kernel/net/ipv4/ipvs/下的所有模块都自动加载了。其中:if 语句检查 ipvs 模块的目录是否存在 for 循环遍历该目录下面的所有文件 module=$module#*/:其中#表示从前面删除字符,*/表示删除到最后一个/,如果一个#就表示只删除到第一个/。如果变量后面接#,表示在#后面的字符串取最长的(一直到最后面),如果接#
29、,表示取最小的一段。module=$module%.ko:表示从后面删除.ko。如果变量后面接%,表示在%后面的字符串取最长的(一直到最前面),如果接%,表示取最小的一段。这样多 module 的两次修改就得到了模块名,就是文件名不带路径和.ko 后缀。modprobe$module/dev/null 2&1:加载模块,输出都指向空设备 这样重启后 lsmod|grep ip_vs 就会得到:ip_vs_wrr 6977 0 ip_vs_wlc 6081 0 ip_vs_sh 6593 0 ip_vs_sed 6081 0 ip_vs_rr 6081 0 ip_vs_nq 5953 0 ip_vs_lc 5953 0 ip_vs_lblcr 10565 0 ip_vs_lblc 9797 0 ip_vs_ftp 10053 0 ip_vs_dh 6593 0 ip_vs 79425 22 ip_vs_wrr,ip_vs_wlc,ip_vs_sh,ip_vs_sed,ip_vs_rr,ip_vs_nq,ip_vs_lc,ip_vs_lblcr,ip_vs_lblc,ip_vs_ftp,ip_vs
限制150内