Linux

编写Linux驱动程序 - 1 简单内核模块

2017-10-09  本文已影响93人  louyang

大多数的Linux驱动程序,都以内核模块的形式,运行在Linux内核中。

Linux内核与内核模块

内核模块可以通过insmod/rmmod命令加载/卸载。此过程中不需要重启动任何东西,这使得调试内核模块非常方便。

写一个简单的内核模块

Hello.c:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

static int __init init_hello(void)
{
        pr_info("Hello world\n");
        return 0;
}

static void __exit cleanup_hello(void)
{
        pr_info("Goodbye world\n");
}

module_init(init_hello);
module_exit(cleanup_hello);

保存hello.c到任意目录,然后再同一目录中创建Makefile文件。

Makefile:

obj-m += hello.o

all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

注意,第4和7行中make前的缩进是<tab>。

确认编译内核模块的头文件都已经在当前系统中安装了。

运行:

# ls /usr/src/kernels/$(shell uname -r)

如果此目录不存在,则需要安装或下载相应的内核头文件。
例如,在我的Linux (Fedora Core 26)中,我是通过下面命令安装:

# dnf install kernel-devel

编译:

# make 
make -C /lib/modules/4.11.6-201.fc25.x86_64/build M=/opt4/foo/hello modules
make[1]: Entering directory '/usr/src/kernels/4.11.6-201.fc25.x86_64'
  Building modules, stage 2.
  MODPOST 1 modules
make[1]: Leaving directory '/usr/src/kernels/4.11.6-201.fc25.x86_64'
[root@euca-10-254-112-100 hello]# ls
Makefile        hello.c   hello.mod.c  hello.o
Module.symvers  hello.ko  hello.mod.o  modules.order

运行:

[root@euca-10-254-112-100 hello]# insmod hello.ko
[root@euca-10-254-112-100 hello]# dmesg | tail -1
[ 2467.713120] Hello world
[root@euca-10-254-112-100 hello]# rmmod hello.ko
[root@euca-10-254-112-100 hello]# dmesg | tail -1
[ 2516.974827] Goodbye world

pr_info(...) 等同于printk(KERN_INFO...)。
内核日志也可以通过 journalctl -k 命令查看。

内核模块的额外信息

可以给内核模块加上描述和许可:

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");
MODULE_DESCRIPTION("xxxx");

在hello.c的尾部加上以上3行,执行 make 命令。

使用modinfo命令查看内核模块的额外信息:

[root@euca-10-254-112-100 hello]# modinfo hello.ko
filename:       /opt4/foo/hello/hello.ko
description:    xxxx
author:         xxx
license:        GPL
depends:
vermagic:       4.11.6-201.fc25.x86_64 SMP mod_unload

内核模块的启动参数

Hello.c:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

static char * str = "world";
module_param(str, charp, 0);
MODULE_PARM_DESC(str, "the name to say hello");

static int __init init_hello(void)
{
        pr_info("Hello %s\n", str);
        return 0;
}

static void __exit cleanup_hello(void)
{
        pr_info("Goodbye %s\n", str);
}

module_init(init_hello);
module_exit(cleanup_hello);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");
MODULE_DESCRIPTION("xxxx");

上述代码给内核模块启动时,增加了一个字符串类型的参数。现在,可以跟任何人说 hello 了。

运行:

[root@euca-10-254-112-100 hello]# insmod hello.ko str="mountain"
[root@euca-10-254-112-100 hello]# dmesg | tail -1
[ 5487.679867] Hello mountain
[root@euca-10-254-112-100 hello]# rmmod hello.ko
[root@euca-10-254-112-100 hello]# dmesg | tail -1
[ 5495.265233] Goodbye mountain
上一篇下一篇

猜你喜欢

热点阅读