linux内核开发示例

 

[Linux Project] Kernel Modules

http://blog.csdn.net/w4and0er96/article/details/60603152

  408人阅读  评论(0)  收藏  举报
  分类:
linux(7) 
  • 我的环境时 Linux 4.10.0+, Ubuntu 16.04 LTS, Mac OS X 10.12.2, VMware Fusion 8
  • 在这个 project 里, 我们要做的是编写一个 linux 内核模块, 并把它加载到内核里面去.
  • 内核模块的开发过程中, 我们会直接和内核函数打交道, 要提醒自己现在正在编写的是内核代码, 一不小心就可能造成系统崩溃, 这里建议初次尝试时使用虚拟机, 这样即使系统崩溃也可以通过快照的技术快速恢复

Part 1. 创建一个内核模块 
- 在 Linux 下, 我们可以通过lsmod命令来查看当前已经加载的内核模块 
- 接下来我们在任意目录(例如: ~/workspace)下创建 simple.c 文件, 文件内容如下:

#include 
#include 
#include 

/* 加载模块时调用的函数 */
int simple_init(void) {
  printk(KERN_INFO "Loading Module\n");
  return 0;
}

/* 卸载模块时调用的函数 */
void simple_exit(void) {
  printk(KERN_INFO "Removing Module\n");
}

/* 注册模块入口和出口 */
module_init(simple_init);
module_exit(simple_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple Module");
MODULE_AUTHOR("AW");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 函数simple_init是模块的入口(module entry point), 当模块被加载进内核时会被调用; 类似的, 函数simple_exit是模块的出口(module exit point), 当模块从内核中被移除时会被调用.
  • 模块的入口函数必须返回一个整型值(integer value), 0代表加载成功, 其他的值代表加载失败; 模块的出口函数必须返回 void. 以上两个函数都不应该传递任何参数.
  • 下面两个宏用来向内核把函数注册为模块的入口函数和出口函数:module_init() module_exit()
  • 注意到这两个函数都调用了printk()函数, 可以把这个函数看作内核版的printf()(因为在内核编程中默认是没有printf()函数的). 它的输出定向到内核的日志缓冲区里(kernel log buffer). 这个缓冲区可以使用dmesg命令来查看.
  • printf()printk()的不同之处包括printk()支持我们提供一个级别位(priority flag), 它的可选值定义在中, 在上面例子中, 我们使用的是KERN_INFO, 它代表 informational message.
  • 最后的几行以 MODULE_ 开头, 显示了这个模块使用的证书(LICENSE), 模块的描述(DESCRIPTION)和作者(AUTHOR). 这些信息对于模块本身并没有什么作用, 但它是内核模块开发的标准要求之一.
  • 以下是编译这个内核模块源文件所需要的 Makefile:
obj-m += simple.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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5
  • 这里提醒第3行和第5行前面都空白都应该是一个 TAB, 此外我多次在网上复制 Makefile 的时候出现- 这个符号格式不对的情况, 这个 Makefile 建议手写.
  • 我们在把源文件 simple.c 和 Makefile 放到同一个目录下, 在命令行下进入该目录, 输入make指令, 以完成编译. 在我的环境中, 编译结束后当前目录应该有以下文件:
Makefile       Module.symvers  simple.ko     simple.mod.o
modules.order  simple.c        simple.mod.c  simple.o
  • 1
  • 2
  • 1
  • 2

Part 2. 加载和移除内核模块 
- 接着上一步, 确认我们在当前目录下看到了 simple.ko 这个文件. 我们可以通过下面这个指令简单的把刚刚写好的模块加载到内核中sudo insmod simple.ko 
- 现在, 可以来确认我们的成果了! 
- 首先我们输入lsmod命令, 在输出的文本中找到我们刚刚加载进去的 simple 模块. 一般而言它会出现在 title 信息后的第一行. 在我的环境中此时执行lsmod命令, 其前几行输出为:

Module                  Size  Used by
simple                 16384  0
rfcomm                 77824  2
bnep                   20480  2
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
  • 可以看到 simple 模块已经加载成功.
  • 此外我们还可以通过dmesg命令来查看内核日志缓冲区, 我们期望看到加载 simple 模块时应当写进日志的信息 “Loading Module.” 下面是我的输出的最后几行:
[33709.258922] simple: loading out-of-tree module taints kernel.
[33709.262822] simple: module verification failed: signature and/or required key missing - tainting kernel
[33709.311222] Loading Module
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3
  • 看到 module verification failed 的提示信息不用怕, 这不代表我们刚才的步骤有哪一步失败了, 只是表示当前内核在编译的时候选择支持内核签名机制, 而我们目前这个简单的模块并没有处理它的要求而已.
  • 好了, 现在我们把这个演示用模块从内核中移除吧, 这需要我们使用sudo rmmod simple这个命令, 注意参数是的形式, 不像insmod命令的参数是 .ko 文件.
  • 再调用一次dmesg, 在输出文本的末尾应该可以看到:
[33709.311222] Loading Module
[34361.342610] Removing Module
  • 1
  • 2
  • 1
  • 2
  • 表示模块已经被移除(模块出口函数被调用).
  • 使用sudo dmesg -c命令可以清空内核日志缓冲区.

你可能感兴趣的:(linux,驱动)