Linux内核模块

Linux内核模块

文章目录

  • Linux内核模块
  • Linux内核模块简介
  • Linux内核模块程序结构
    • 模块加载函数
    • 模块卸载函数
    • 模块许可证声明
    • 模块参数
    • 模块的声明与描述
    • 模块的使用计数
    • 模块的编译

Linux内核模块简介

Linux的内核模块本来就很大,我们添加或者删除某个其中一个模块时,又得重新编译整个模块,导致很多麻烦,那有没有一种机制是将现有的内核并不包含全部功能,当我们需要某个功能时,其对应的代码动态的被加载在内核中

Linux就提供了这种机制叫做模块(module)

  • 本身并不编译进内核的映像
  • 当被加载时和内核其他的部分一样

简单写一个内核的模块函数:

#include 
#include 
#include 

static int __init hello_init(void)
{
	printk("hello world\n");
	return 0;
}

static void __exit hello_exit(void)
{
	printk("hello world exit\n");
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LISENCE("GPL v2");
  1. 查看内核模块:lsmod /proc/modules—————或者是lsmod /sys/module

  2. 加载模块时:可以使用insmod或者modprobe

    insmod /path/to/mydvr.ko
    modprobe mydvr.ko

    modprobe比insmod更高级,加载模块时会同时加载这个模块所依赖的其他模块

  3. 卸载模块:使用rmmod或者modprobe -r

    若使用modprobe -r 来卸载模块时,会同时卸载模块所依赖的其余模块

    而模块的依赖关系被存放在根文件系统 /lib/modules//modules.dep文件中

  4. 查看模块信息,所查看的主要是模块的作者、模块的说明、模块所支持的参数等

    使用modinfo

Linux内核模块程序结构

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

  • 模块卸载函数
  • 模块加载函数
  • 模块许可证说明

模块加载函数

内核的模块加载函数一般

  • 以_ _init标识声明
  • 以module_init(函数名)指定
  • 返回整型,初始化成功则返回0,失败返回错误编码(是一个接近于0的负值),用户可以使用perror等方法把他们转换成有意义的错误信息字符串
  • 可以加载其他的内核,只需要在内核程序中添加:

request_module(module_name);灵活的加载其他内核模块

  • 所有标识为__init的函数如果直接被编译进内核,在连接的时候都会放在.init.text这个区段内
  • 所有的__init函数在区段.initcall.init中还保存了一份函数指针,在初始化的过程中内核会通过这些函数指针来调用__init函数,初始化完成之后,释放init区段(包括.init.text、.initcall.init等)的内存

.init.text 中的代码通常会包含一些初始化函数的调用,而这些初始化函数实际上会被添加到 .initcall.init 中,以便在系统初始化过程中被调用。因此,.init.text 中的代码可以被视为初始化代码的入口点,而 .initcall.init 包含了实际需要在初始化过程中执行的函数。

  • 数据也可以被定义为__initdata,只是对于初始化时期的数据,在初始化完成之后就会被释放

static int hello_data __initdata = 1;

模块卸载函数

  • 使用__exit()来修饰卸载函数
  • 只是退出阶段使用的参数可以用__exitdata来标识

模块许可证声明

许可证(LICENSE)声明描述该内核模块的许可权限

如果不声明LICENSE,模块被加载时将收到内核被污染(Kernel Tainted)的警告

一般的LICENSE可包含:”GPL” “GPL v2” “GPL and additional rights”

模块参数

使用module_param(参数名,参数类型,参数读/写权限)为模块定义一个参数

static char *name = "my name is xiaoming";
module_param(name, charp, S_IRUGO)

static int data = 6;
module_param(data, int, S_IRUGO);

在装载内核模块时可以通过命令行传递参数值,通过

insmod (或者modprobe)模块名 参数名=参数值

如果不传递,参数的值则会使用模块内定义的缺省值

如果模块被内置,无法用insmod去传参数,则使用bootargs给bootloader里面的模块去传递参数值

参数的类型:

byte、short、ushort、int、uint、long、ulong、charp、bool、inbool

模块参数数组:

module_param_array(数组名,数组类型,数组长度,参数的读写权限);

运行insmod或者modprobe时,应该以逗号分隔输入的数组元素

模块被加载成功时,/sys/module下会存在以模块名命名的目录,当参数(这个参数就是你在模块函数中定义的模块参数)的读写权限为0时,表示此参数不存在sysfs文件系统下对应的文件节点;如果存在参数读写权限不为0的命令行参数,此模块的目录下还将出现parameters目录,目录中包含着一系列以参数名命名的文件节点,这些文件的权限值就是传入module_param()的参数读写权限,而文件的内容为参数的值.

举个例子:

假设有一个名为"example_module"的内核模块,该模块定义了一个名为"my_param"的参数,其读写权限为0644(允许读和写)。加载该模块后,会在/sys/module/example_module目录下出现一个名为"parameters"的子目录,其中包含一个名为"my_param"的文件节点,权限为0644,其内容则为该参数的值。通过使用 tail -n /var/log/messages就可以查看内核的输出

| 文件或模块参数的权限宏包括:

S_IRUSR:Owner读权限
S_IWUSR:Owner写权限
S_IXUSR:Owner执行权限
S_IRGRP:Group读权限
S_IWGRP:Group写权限
S_IXGRP:Group执行权限
S_IROTH:Universal读权限
S_IWOTH:Universal写权限
S_IXOTH:Universal执行权限

这些权限宏可以组合使用,以表示不同用户对文件或者模块参数的权限设置。

模块的声明与描述

  • MODULE_AUTHOR 模块的作者
  • MODULE_DESCRIPTION 模块的描述
  • MODULE_VERSION 模块的版本
  • MODULE_DEVICE_TABLE 模块的设备表
  • MODULE_ALIAS 模块的别名

模块的使用计数

在Linux2.4中模块自身通过MOD_INC_USE_COUNT、MOD_DEC_USE_COUNT宏来管理自己被使用的计数

在Linux2.6中模块使用 try_module_get(&module)module_put(&module) 来管理自己被使用的计数

/*该函数用于增加模块使用计数*/
int try_module_get(struct module *module);

/*该函数用于减少模块使用计数*/
void module_put(struct module *module);

struct module *owner//用来指向管理此设备的模块

当某个内核开始使用设备时,内核使用try_module_get去增加管理此设备的owner模块的使用计数;当不再使用的时候,使用函数module_put去减少对管理此设备的管理模块的使用计数;这样当设备在使用的过程中,管理此设备的模块将不会被卸载。只有当设备不再使用时,模块才允许被卸载。

模块的编译

可以先写一个简单的makefile文件

#内核版本号
KVERS = $(shell uname -r)

#内核模块
obj-m += hello.o

#如果hello模块有多个链接文件
#modulename-objs += 1.o 2.o 3.o  这三个文件会被链接进hello.ko中

#定义了总的目标是build,依赖kernel_modules
build: kernel_modules

#定义了目标kernel_modules,内核源码的构建目录为/lib/modules/$(KVERS)/
#M=$(CURDIR)指的是将模块构建在当前的文件夹下
kernel_modules:
	make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules

clean:
	make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean

你可能感兴趣的:(Linux设备驱动开发,linux,Linux内核,内核模块)