Linux 内核模块

来源:互联网 发布:淘宝最新开店流程 编辑:程序博客网 时间:2024/05/22 06:26

Linux 内核模块

Linux 设备驱动会以内核模块的形式出现,内核模块本身不被编译入内核镜像,从而控制了内核的大小,这样的工作机制也更加灵活.

内核模块程序结构

一个Linux内核模块主要由如下几部分组成

  • 模块加载函数
  • 模块卸载函数
  • 模块许可证声明
  • 模块参数(可选)
  • 模块导出符号(可选)
  • 模块作者等信息声明(可选)

内核模块基础示例

【代码仓库】

hello world

#include <linux/init.h>#include <linux/module.h>static int __init hello_init(void){    printk(KERN_INFO "Hello world enter");    return 0;}module_init(hello_init);static int __exit hello_exit(void){    printk(KERN_INFO "Hello world exit");    return 0;}module_exit(hello_exit);MODULE_LICENSE("Dual BSD/GPL");

Makefile

KERNELDIR = /lib/modules/`uname -r`/build#Kernel modulesobj-m += hello.o#EXTRA_CFLAGS=-g -o0all:    $(MAKE) -C $(KERNELDIR) M=$(PWD) modulesclean:    $(MAKE) -C $(KERNELDIR) M=$(PWD) clean

验证

$ make$ insmod hello.ko$ modinfo hello.kofilename:       /root/share/linux_driver_demo/hello_world/hello.kolicense:        Dual BSD/GPLsrcversion:     5A29A82E48CA197252C923Ddepends:vermagic:       4.4.0-31-generic SMP mod_unload modversions$ dmesg | tail -1[22017.025568] Hello world enter$ rmmod hello$ dmesg | tail -1[22225.742975] Hello world exit

相关说明

示例内核模块中只包含了加载函数、卸载函数和对GPL许可权限的声明.

  • insmod hello.ko 加载模块
  • rmmod hello 卸载模块
  • printk 内核打印函数
  • modprobe 加载/卸载模块(包括依赖关系)
  • modinfo hello.ko 查看模块信息

内核模块符号导出

Linux 的“/proc/kallsyms”文件对应着内核符号表,记录了符号以及符号所在的内存地址.模块可以使用如下宏导出符号到内核符号表中:

  • EXPORT_SYMBOL(符号名)
  • EXPORT_SYMBOL_GPL(符号名)

导出的符号可以被其他模块使用,只需要在使用前声明一下即可.

符号导出基础示例

export symbol

#include <linux/init.h>#include <linux/module.h>MODULE_LICENSE("Dual BSD/GPL");int add(int a, int b){    return a+b;}int sub(int a, int b){    return a-b;}/* 导出符号 */EXPORT_SYMBOL_GPL(add);EXPORT_SYMBOL_GPL(sub);

Makefile

KERNELDIR = /lib/modules/`uname -r`/build#Kernel modulesobj-m += export.o#EXTRA_CFLAGS=-g -o0all:    $(MAKE) -C $(KERNELDIR) M=$(PWD) modulesclean:    $(MAKE) -C $(KERNELDIR) M=$(PWD) clean

import symbol

#include <linux/init.h>#include <linux/module.h>/* 使用导出符号函数 */extern int add(int, int);extern int sub(int, int);static int __init import_init(void){    int rc = add(5, 6);    printk(KERN_INFO "[import symbol] -> add(5, 6) = %d\n", rc);    return 0;}module_init(import_init);static int __exit import_exit(void){    printk(KERN_INFO "import symbol exit\n");    return 0;}module_exit(import_exit);MODULE_LICENSE("Dual BSD/GPL");

Makefile

KERNELDIR = /lib/modules/`uname -r`/build#Kernel modulesobj-m += import.o#EXTRA_CFLAGS=-g -o0all:    $(MAKE) -C $(KERNELDIR) M=$(PWD) modulesclean:    $(MAKE) -C $(KERNELDIR) M=$(PWD) clean

验证

$ cd export_symbol && make$ cp Module.symvers ../import_symbol$ insmod export.ko$ cd ../import_symbol && make$ insmod import.ko$ dmesg | tail -1[23665.048569] [import symbol] -> add(5, 6) = 11

学习资源

  • 《Linux C API 参考手册》
  • 《Linux 设备驱动开发详解》
原创粉丝点击