linux内核开发示例

来源:互联网 发布:linux下移动文件夹命令 编辑:程序博客网 时间:2024/06/05 15:34
 

[Linux Project] Kernel Modules

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

 408人阅读 评论(0) 收藏 举报
 分类:
  • 我的环境时 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 <linux/init.h>#include <linux/kernel.h>#include <linux/module.h>/* 加载模块时调用的函数 */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), 它的可选值定义在<linux/printk.h>中, 在上面例子中, 我们使用的是KERN_INFO, 它代表 informational message.
  • 最后的几行以 MODULE_ 开头, 显示了这个模块使用的证书(LICENSE), 模块的描述(DESCRIPTION)和作者(AUTHOR). 这些信息对于模块本身并没有什么作用, 但它是内核模块开发的标准要求之一.
  • 以下是编译这个内核模块源文件所需要的 Makefile:
obj-m += simple.oall:  make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modulesclean:  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.omodules.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 bysimple                 16384  0rfcomm                 77824  2bnep                   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这个命令, 注意参数是<module name>的形式, 不像insmod命令的参数是 .ko 文件.
  • 再调用一次dmesg, 在输出文本的末尾应该可以看到:
[33709.311222] Loading Module[34361.342610] Removing Module
  • 1
  • 2
  • 1
  • 2
  • 表示模块已经被移除(模块出口函数被调用).
  • 使用sudo dmesg -c命令可以清空内核日志缓冲区.