HACK #3 如何编写内核模块

本节将介绍向Linux内核中动态添加功能的结构—内核模块的编写方法。
内核模块
Linux内核是单内核(monolithic kernel),也就是所有的内核功能都集成在一个内核空间内。但是内核具有模块功能,可以将磁盘驱动程序、文件系统等独立的内核功能制作成模块,并动态添加到内核空间或者删除。
内核模块是可以动态添加到Linux内核空间的二进制文件,文件扩展名为ko。
内核模块的编写方法大致有两种。一种是将内核源码树带有的功能编写为模块的方法(参考Hack #2),另一种是将内核源码树中所没有的特有功能编写为模块的方法。
通过内核配置编写模块
把内核源代码文件中CONFIG_*=m的项目所对应的驱动程序编写为模块。编写生成的模块一般安装在/lib/modules/内核版本/kernel下。
以RHEL6为例

# ls /lib/modules/2.6.32-71.29.1.el6.x86_64/kernel/
arch crypto drivers fs kernel lib mm net sound

编写特有的内核模块
下面将介绍如何编写内核源码树中所没有的特有内核模块。
以mymod模块为例说明,请将下面的代码以mymod.c为文件名保存。

#include <linux/module.h>
#include <linux/timer.h>
#include <linux/errno.h> static int sec = 5;
module_param(sec, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(sec, "Set the interval."); static void mymod_timer(unsigned long data); static DEFINE_TIMER(timer, mymod_timer, 0, 0); static void mymod_timer(unsigned long data)
{
printk(KERN_INFO "mymod: timer\n");
mod_timer(&timer, jiffies + sec * HZ);
} static int mymod_init(void)
{
printk(KERN_INFO "mymod: init\n"); if (sec <= 0) {
printk(KERN_INFO "Invalid interval sec=%d\n", sec);
return -EINVAL;
} mod_timer(&timer, jiffies + sec * HZ); return 0;
} static void mymod_exit(void)
{
del_timer(&timer);
printk(KERN_INFO "mymod: exit\n");
} module_init(mymod_init);
module_exit(mymod_exit); MODULE_AUTHOR("Hiroshi Shimamoto");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("My module");

在模块的源代码中包含(include)头文件linux/module.h。
名为module_int()和module_exit()的宏,可以调用回调(callback)函数来进行初始化和终止模块的处理。在模块的源文件中进行如下描述,就可以在添加模块时调用初始化函数,在删除模块时调用终止函数。
module_init(初始化函数名);
module_exit(终止函数名);
在这个例子模块的情形下调用的分别是mymod_init()和mymod_exit()。
初始化函数为了表示初始化已正常完成,需要返回0。按照Linux内核中的写法,发生错误(error)时将返回一个值为负数的错误代码。在这个例子中,如果设定值出错,则处理为-EINVAL(非法值)。
下面先用3个宏对模块进行定义,但在模块编写中并不是必需的。

这个例子模块还用到了模块参数。模块参数可以使用module_param()宏来生成。
module_param(参数名,参数类型,权限(permission));
在例子模块中,sec定义为int类型的模块参数。
另外,还可以使用MODULE_PARM_DESC()宏来对模块参数进行说明。
先简单介绍一下这个例子的运行过程。当添加模块时,会调用指定为初始化函数的mymod_init()。在mymod_init()中首先通过printk()输出:
mymod: init
然后确认模块参数sec是否正常。在模块参数sec的值为0以下的异常情形时,会返回EINVAL错误代码并终止程序。在判断模块参数sec正常后,将内核计时器设置为sec秒后启动超时(timeout)函数mymod_timer()。在每隔sec秒启动的mymod_timer()中,首先使用printk()输出:

mymod: timer

再次设置sec秒的内核计时器,然后终止。当删除模块时,会调用mymod_exit()函数,删除内核计时器,通过printk()输出:

mymod: exit

于是模块终止。
接下来需要准备编写模块所需的Makefile。由于是使用内核的创建框架来生成,因此Makefile的内容非常简单。

obj-m :=mymod.o

最后执行下列make命令,通过当前目录(current directory)的源代码和Makefile生成模块mymod.ko。

# make -C /lib/modules/'uname 杛'/build M='pwd'

通过使用modinfo命令,可以看到所生成模块mymod.ko的信息。从这里可以看到使用MODULE_*宏所指定的内容。

# modinfo mymod.ko
filename: mymod.ko
description: My module
license: GPL
author: Hiroshi Shimamoto
srcversion: 61A3BB7CFC0C89B8344F5A5
depends:
vermagic: 2.6.32-71.29.1.el6.x86_64 SMP mod_unload modversions
parm: sec:Set the interval. (int)

添加内核模块
添加内核模块需要用到insmod命令或modprobe命令。
通过执行insmod命令把生成的mymod.ko模块添加进来。

# insmod mymod.ko

使用dmesg命令,可以看到例子模块mymod.ko的输出内容。

# dmesg | tail
:
mymod: init

作为模块初始化函数mymod_init()所调用的printk()的输出内容会在最后一行显示。
使用lsmod可以显示目前添加到内核中的模块列表。

# lsmod
Module Size Used by
mymod 1482 0
   :

可以看到,mymod行存在,模块已添加。
要将已添加的模块从内核空间删除时,可以使用rmmod命令。

# rmmod mymod

执行rmmod命令后,模块将从内核空间内删除,使用lsmod命令就不会再输出mymod行。
此外,使用dmesg命令还可以看到终止模块的处理中printk()输出的信息mymod: exit。

# dmesg | tail
:
mymod: exit

下面针对模块参数作一些介绍。在添加模块后,就会在/sys/module下生成对应的目录和文件。

# ls /sys/module/mymod/

holders initstate notes parameters refcnt sections srcversion
可以确认在parameters下生成的模块mymod中所定义的参数sec。

# ls -l /sys/module/mymod/parameters/sec

-rw-r--r--. 1 root root 4096 May 15 06:34 /sys/module/mymod/parameters/sec
其内容应当是初始值5。

# cat /sys/module/mymod/parameters/sec

5
模块参数可以在使用insmod添加模块时对值进行指定。

# insmod mymod.ko sec=10

进行上述操作后,添加mymod.ko时模块参数sec就为10,默认间隔5秒的超时变成间隔10秒。
小结
本节介绍了内核模块的编写方法。编写特有内核模块是Kernel构建的入门级操作,你也可以尝试一下。
参考文献
Documentation/kbuild/modules.txt
—Hiroshi Shimamoto

《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #3 如何编写内核模块的更多相关文章

  1. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #1 如何获取Linux内核

    HACK #1 如何获取Linux内核 本节介绍获取Linux内核源代码的各种方法.“获取内核”这个说法看似简单,其实Linux内核有很多种衍生版本.要找出自己想要的源代码到底是哪一个,必须首先理解各 ...

  2. 《Linux内核精髓:精通Linux内核必会的75个绝技》目录

    1章 内核入门HACK #1 如何获取Linux内核HACK #2 如何编译Linux内核HACK #3 如何编写内核模块HACK #4 如何使用GitHACK #5 使用checkpatch.pl检 ...

  3. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #15 ramzswap

    HACK #15 ramzswap 本节介绍将一部分内存作为交换设备使用的ramzswap.ramzswap是将一部分内存空间作为交换设备使用的基于RAM的块设备.对要换出(swapout)的页面进行 ...

  4. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #21FUSE

    HACK #21FUSE 本节将介绍使用用户进程的文件系统框架—FUSE.FUSE概要FUSE(Filesystem in Userspace,用户空间文件系统),是用来生成用户空间的一般进程的框架. ...

  5. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #20 使用fio进行I/O的基准测试

    HACK #20 使用fio进行I/O的基准测试 本节介绍使用fio进行模拟各种情况的I/O基准测试的操作方法.I/O的基准测试中有无数需要考虑的因素.是I/O依次访问还是随机访问?是通过read/w ...

  6. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #17 如何使用ext4

    HACK #17 如何使用ext4 本节介绍ext4的编写和挂载方法.开发版ext4的使用方法.ext4是ext3的后续文件系统,从Linux 2.6.19开始使用.现在主要的发布版中多数都是采用ex ...

  7. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #16 OOM Killer的运行与结构

    HACK #16 OOM Killer的运行与结构(1) 本节介绍OOM Killer的运行与结构. Linux中的Out Of Memory(OOM) Killer功能作为确保内存的最终手段,可以在 ...

  8. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #14 虚拟存储子系统的调整

    HACK #14 虚拟存储子系统的调整 本节介绍如何使用/proc进行虚拟存储子系统的调整.虚拟空间存储方式在Linux上向应用程序分配内存时,是通过以页面为单位的虚拟存储方式进行的.采用虚拟存储方式 ...

  9. 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #13 使用Block I/O控制器设置I/O优先级

    HACK #13 使用Block I/O控制器设置I/O优先级 本节介绍使用Block I/O控制器的功能设置I/O优先级的方法.Block I/O控制器可以将任意进程分组,并对该分组设置I/O的优先 ...

随机推荐

  1. (CLR via C#学习笔记)任务和并行操作

    一 任务 可以调用ThreadPool的QueueUserWorkItem方法发起一次异步的计算限制操作.但这个技术有很多限制.最大的问题是没有内建的机制让你知道操作在什么时候完成和操作完成时的返回值 ...

  2. Apache-commons-io包的使用及常用方法

    首先,我们要下载FileUtils相关的Apache-commons-io jar包以及api文档.FileUtils类库的下载页面在: http://commons.apache.org/prope ...

  3. Spring4 MVC RESTFul WebServices CRUD实例+RestTemplate

    简单介绍REST REST 表示状态传输.这是一个体系结构样式,可用于设计网络服务,可以被各种客户端消耗.核心思想是,不使用如CORBA,RPC或SOAP复杂的机制在机器之间进行连接,简单的 HTTP ...

  4. ViewPager实现引导页(添加导航点,判断是否第一次进入主界面)

    1.引导页的4个界面布局,里面加载一张背景图片 插入到guide的界面布局中(这里不用fragment) guide_background_fragment1.xml <?xml version ...

  5. Xilinx SDK使用教程

    本文参考 Xilinx SDK软件内置的教程,打开方法:打开SDK->Help->Cheet Sheets...->Xilinx SDK Tutorials,这里有6篇文档.本文详细 ...

  6. 由于BOM头导致的Json解析出错

    上周五改完一些BUG后,测试通过就安心在家过了个周末.结果周一回来一看,整个安卓APP所有的接口都挂掉了1.查找bug 首先想到的是客户端代码有问题,然后想起来上周五还能运行得好好的手机也是同样的错误 ...

  7. L201

    The American public’s obsession with dieting has led to one of the most dangerous healthmisconceptio ...

  8. 2018.11.23 Cypress BLE module test

    CYx63BPA BLE module IQC test guide Test Jig setting:1.  Connect  USB1 and USB2 with computer serial ...

  9. 新转移注意(caffe):ImportError: libcudart.so.7.0: cannot open shared object file: No such file or directory

    https://github.com/NVIDIA/DIGITS/issues/8 For this error ImportError: libcudart.so.7.0: cannot open ...

  10. [QT_QML]qml假如调试信息 qDebug console.debug

    WinSys: win7 Qt Version: 5.8.0 使用Console调试 console.log 打印日志信息console.debug 打印调试信息console.info 打印普通信息 ...