购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

4.1
Linux内核模块简介

Linux内核的整体架构本就非常庞大,其包含的组件也非常多。而我们怎样把需要的部分都包含在内核中呢?

一种方法是把所有需要的功能都编译到Linux内核中。这会导致两个问题,一是生成的内核会很大,二是如果我们要在现有的内核中新增或删除功能,将不得不重新编译内核。

有没有另一种机制可使得编译出的内核本身并不需要包含所有功能,而在这些功能需要被使用的时候,其对应的代码被动态地加载到内核中呢?

Linux提供了这样的机制,这种机制被称为模块(Module)。模块具有这样的特点。

·模块本身不被编译入内核映像,从而控制了内核的大小。

·模块一旦被加载,它就和内核中的其他部分完全一样。

为了使读者初步建立对模块的感性认识,我们先来看一个最简单的内核模块“Hello World”,如代码清单4.1所示。

代码清单4.1 一个最简单的Linux内核模块

 1 /*
 2  * a simple kernel module: hello
 3  *
 4  * Copyright (C) 2014 Barry Song  (baohua@kernel.org)
 5  *
 6  * Licensed under GPLv2 or later.
 7  */
 8
 9 #include <linux/init.h>
10 #include <linux/module.h>
11 
12 static int __init hello_init(void)
13 {
14   printk(KERN_INFO "Hello World enter\n");
15   return 0;
16 }
17 module_init(hello_init);
18 
19 static void __exit hello_exit(void)
20 {
21   printk(KERN_INFO "Hello World exit\n ");
22 }
23 module_exit(hello_exit);
24 
25 MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
26 MODULE_LICENSE("GPL v2");
27 MODULE_DESCRIPTION("A simple Hello World Module");
28 MODULE_ALIAS("a simplest module");

这个最简单的内核模块只包含内核模块加载函数、卸载函数和对GPL v2许可权限的声明以及一些描述信息,位于本书配套Ubuntu的/home/baohua/develop/training/kernel/drivers/hello目录。编译它会产生hello.ko目标文件,通过“insmod./hello.ko”命令可以加载它,通过“rmmod hello”命令可以卸载它,加载时输出“Hello World enter”,卸载时输出“Hello World exit”。

内核模块中用于输出的函数是内核空间的printk()而不是用户空间的printf(),printk()的用法和printf()基本相似,但前者可定义输出级别。printk()可作为一种最基本的内核调试手段,在Linux驱动的调试章节中将会详细讲解。

在Linux中,使用lsmod命令可以获得系统中已加载的所有模块以及模块间的依赖关系,例如:

Module          Size Used by
hello           9 472 0
nls_iso8859_1   12 032 1
nls_cp437       13 696 1
vfat            18 816 1
fat             57 376 1 vfat
...

lsmod命令实际上是读取并分析“/proc/modules”文件,与上述lsmod命令结果对应的“/proc/modules”文件如下:

$ cat /proc/modules
hello 12393 0 - Live 0xe67a2000 (OF)
nls_utf8 12493 1 - Live 0xe678e000
isofs 39596 1 - Live 0xe677f000
vboxsf 42561 2 - Live 0xe6767000 (OF)
…

内核中已加载模块的信息也存在于/sys/module目录下,加载hello.ko后,内核中将包含/sys/module/hello目录,该目录下又有一个refcnt文件和一个sections目录,在/sys/module/hello目录下运行“tree–a”可得到如下目录树:

root@barry-VirtualBox:/sys/module/hello# tree -a
.
├── coresize
├── holders
├── initsize
├── initstate
├── notes
│ └── .note.gnu.build-id
├── refcnt
├── sections
│ ├── .exit.text
│ ├── .gnu.linkonce.this_module
│ ├── .init.text
│ ├── .note.gnu.build-id
│ ├── .rodata.str1.1
│ ├── .strtab
│ └── .symtab
├── srcversion
├── taint
└── uevent
3 directories, 15 f iles

modprobe命令比insmod命令要强大,它在加载某模块时,会同时加载该模块所依赖的其他模块。使用modprobe命令加载的模块若以“modprobe-r filename”的方式卸载,将同时卸载其依赖的模块。模块之间的依赖关系存放在根文件系统的/lib/modules/<kernel-version>/modules.dep文件中,实际上是在整体编译内核的时候由depmod工具生成的,它的格式非常简单:

kernel/lib/cpu-notifier-error-inject.ko: kernel/lib/notifier-error-inject.ko
kernel/lib/pm-notifier-error-inject.ko: kernel/lib/notifier-error-inject.ko
kernel/lib/lru_cache.ko:
kernel/lib/cordic.ko:
kernel/lib/rbtree_test.ko:
kernel/lib/interval_tree_test.ko:
updates/dkms/vboxvideo.ko: kernel/drivers/gpu/drm/drm.ko

使用modinfo<模块名>命令可以获得模块的信息,包括模块作者、模块的说明、模块所支持的参数以及vermagic: B147QSauVgGAcJRXaiSe6mvhxfo9VMwFzpFWijgDK8nHy0eJ7Piqwaq14lqvKebp

# modinfo hello.ko
filename:       /home/baohua/develop/training/kernel/drivers/hello/hello.ko
alias:          a simplest module
description:    A simple Hello World Module
license:        GPL v2
author:         Barry Song <21cnbao@gmail.com>
depends:        
vermagic:       4.0.0-rc1 SMP mod_unload 686
点击中间区域
呼出菜单
上一章
目录
下一章
×