doc@[toc]

模块编写的流程,先在pc上测试

pc也是linux内核。
内核提供的三个宏,module_init,module_exit,MODULE_LICENSE
前俩在include/linux/init.h中定义,MODULE_LICENSE在include/linux/module.h中定义,
在这里插入图片描述
入口出口和GPL协议说明都是宏函数。

0. 入出口函数,GPL声明

在内核源码中找到一个入出口函数,

static int __init omap2_mbox_init(void)
{
	return platform_driver_register(&omap2_mbox_driver);
}

static void __exit omap2_mbox_exit(void)
{
	platform_driver_unregister(&omap2_mbox_driver);
}

module_init(omap2_mbox_init);
module_exit(omap2_mbox_exit);

MODULE_LICENSE("GPL");

大致结构如下:
__init将函数统一放在__init段,入口函数参数为void,返回值为int,用__init修改函数属性,
__exit将函数统一放在__exit段,出口函数参数为void,返回值为void,用__exit修改函数属性,
将入出口函数指针传入

static int __init xxx_init(void)
{
	return platform_driver_register(&omap2_mbox_driver);
}

static void __exit xxx_exit(void)
{
	platform_driver_unregister(&omap2_mbox_driver);
}

module_init(xxx_init);
module_exit(xxx_exit);

MODULE_LICENSE("GPL");

1. 编写内核模块

至少包含那三个宏函数。
查看当前ubuntu系统的linux内核版本号,name -r
ubuntu的linux内核在/lib/modules/5.0.0-37-generic/build,init.h在/lib/modules/5.0.0-37-generic/build/include/linux下。
编写内核模块,就打印个信息:

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

int __init module_demo_init(void)
{
    printk("-----%s-----%s-----%d", __FILE__, __func__, __LINE__);
    return 0;
}
void __exit module_demo_exit(void)
{
    printk("-----%s-----%s-----%d", __FILE__, __func__, __LINE__);
}

module_init(module_demo_init);
module_exit(module_demo_exit);

MODULE_LICENSE("GPL");

2. 编译内核模块

  1. 静态编译
    编译到uImage中
  2. 动态编译
    编译生成动态模块.ko

外部编译内核模块时,在Documentation/kbuild/modules.txt中有说明,

make -C <path-to-kernel> M=`pwd` # -C到指定路径下执行Makefile,内核源码路径,M表示外部模块所处路径,编译目标
...
make -C /lib/modules/`uname -r`/build M=`pwd`

Makefile内容如下:

KERNDIR := /lib/modules/5.0.0-37-generic/build/ # 系统内核的路径
PWD := $(shell pwd)		# 执行pwd命令并把结果赋给PWD

obj-m := module_demo.o  # .ko的生成依赖于.o,.o默认依赖.c

all:
	make -C $(KERNDIR) M=$(PWD) modules # modules为编译目标
clean:
	nake -C $(KERNDIR) M=$(PWD) clean

make后在内核模块路径(当前路径下)生成module_demo.ko文件,这个文件由pc对应的编译器编译生成,所以可以插入到pc内核中运行。

3. 使用内核模块

将内核模块加入到内核中,与内核形成整体,然后运行。
也可以从内核模块中卸载内核 内核模块。

3.1 内核模块信息

查看内核模块信息:modinfo, 如modinfo module_demo.ko
在这里插入图片描述
当然还可以包含作者,模块描述等信息,需要添加其他宏函数,在include/linux/module.h中定义,

#define MODULE_LICENSE(_license) MODULE_INFO(license, _license)

/* Author, ideally of form NAME[, NAME]*[ and NAME] */
#define MODULE_AUTHOR(_author) MODULE_INFO(author, _author)
  
/* What your module does. */
#define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description)
3.2 查看当前系统插入的动态内核模块

lsmod
在这里插入图片描述

3.3 插入内核模块

需要管理员权限
insmod module_demo.ko # 加载函数会被调用

3.4 查看内核日志信息

dmesg
可以先删除之前的内核日志sudo dmesg -c

3.5 卸载内核模块

sudo rmmod module_demo # 卸载函数会被调用
最终可以看到写的module_demo.ko内核模块在pc内核中运行的结果,
在这里插入图片描述内核模块 加载的时候 执行加载函数,只执行一次,
内核模块 卸载的时候 执行卸载函数,只执行一次。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐