内核模块(下)

1.内核模块

Linux内核的插件机制——内核模块
类似于浏览器、eclipse这些软件的插件开发,Linux提供了一种可以向正在运行的内核中插入新的代码段、在代码段不需要继续运行时也可以从内核中移除的机制,这个可以被插入、移除的代码段被称为内核模块。

2.模块传参

module_param(name,type,perm);//将指定的全局变量设置成模块参数
/*
name:全局变量名
type:
    使用符号      实际类型                传参方式
	bool	     bool           insmod xxx.ko  变量名=0 或 1
	invbool      bool           insmod xxx.ko  变量名=0 或 1
	charp        char *         insmod xxx.ko  变量名="字符串内容"
	short        short          insmod xxx.ko  变量名=数值
	int          int            insmod xxx.ko  变量名=数值
	long         long           insmod xxx.ko  变量名=数值
	ushort       unsigned short insmod xxx.ko  变量名=数值
	uint         unsigned int   insmod xxx.ko  变量名=数值
	ulong        unsigned long  insmod xxx.ko  变量名=数值
perm:给对应文件 /sys/module/name/parameters/变量名 指定操作权限
	#define S_IRWXU 00700
	#define S_IRUSR 00400
	#define S_IWUSR 00200
	#define S_IXUSR 00100
	#define S_IRWXG 00070
	#define S_IRGRP 00040
	#define S_IWGRP 00020
	#define S_IXGRP 00010
	#define S_IRWXO 00007
	#define S_IROTH 00004
	#define S_IWOTH 00002  //不要用 编译出错
	#define S_IXOTH 00001
*/
module_param_array(name,type,&num,perm);
/*
name、type、perm同module_param,type指数组中元素的类型
&num:存放数组大小变量的地址,可以填NULL(确保传参个数不越界)
    传参方式 insmod xxx.ko  数组名=元素值0,元素值1,...元素值num-1  
*/

可用MODULE_PARAM_DESC宏对每个参数进行作用描述,用法:

MODULE_PARM_DESC(变量名,字符串常量);

字符串常量的内容用来描述对应参数的作用

modinfo可查看这些参数的描述信息

3.模块依赖

既然内核模块的代码与其它内核代码共用统一的运行环境,也就是说模块只是存在形式上独立,运行上其实和内核其它源码是一个整体,它们隶属于同一个程序,因此一个模块或内核其它部分源码应该可以使用另一个模块的一些全局特性。

一个模块中这些可以被其它地方使用的名称被称为导出符号,所有导出符号被填在同一个表中这个表被称为符号表。

最常用的可导出全局特性为全局变量和函数

查看符号表的命令:nm
nm查看elf格式的可执行文件或目标文件中包含的符号表,用法:

nm 文件名 (可以通过man nm查看一些字母含义)

两个用于导出模块中符号名称的宏:

EXPORT_SYMBOL(函数名或全局变量名)
EXPORT_SYMBOL_GPL(函数名或全局变量名) 需要GPL许可证协议验证

使用导出符号的地方,需要对这些符号进行extern声明后才能使用这些符号

B模块使用了A模块导出的符号,此时称B模块依赖于A模块,则:

  1. 编译次序:先编译模块A,再编译模块B,当两个模块源码在不同目录时,需要:i. 先编译导出符号的模块A ii. 拷贝A模块目录中的Module.symvers到B模块目录 iii. 编译使用符号的模块B。否则编译B模块时有符号未定义错误

  2. 加载次序:先插入A模块,再插入B模块,否则B模块插入失败

  3. 卸载次序:先卸载B模块,在卸载A模块,否则A模块卸载失败

补充说明:
内核符号表(直接当文本文件查看)
/proc/kallsyms运行时 /boot/System.map编译后

4.内核空间与用户空间

为了彻底解决一个应用程序出错不影响系统和其它app的运行,操作系统给每个app一个独立的假想的地址空间,这个假想的地址空间被称为虚拟地址空间(也叫逻辑地址),操作系统也占用其中固定的一部分,32位Linux的虚拟地址空间大小为4G,并将其划分两部分:

0~3G 用户空间 :每个应用程序只能使用自己的这份虚拟地址空间

3G~4G 内核空间:内核使用的虚拟地址空间,应用程序不能直接使用这份地址空间,但可以通过一些系统调用函数与其中的某些空间进行数据通信

实际内存操作时,需要将虚拟地址映射到实际内存的物理地址,然后才进行实际的内存读写

5.执行流

执行流:有开始有结束总体顺序执行的一段独立代码,又被称为代码上下文

计算机系统中的执行流的分类:

执行流:

  1. 任务流–任务上下文(都参与CPU时间片轮转,都有任务五状态:就绪态 运行态 睡眠态 僵死态 暂停态)
    1.1 进程
    1.2 线程
    内核线程:内核创建的线程
    应用线程:应用进程创建的线程

  2. 异常流–异常上下文
    中断
    其它异常

应用编程可能涉及到的执行流:

  1. 进程
  2. 线程

内核编程可能涉及到的执行流:

  1. 应用程序自身代码运行在用户空间,处于用户态 ----------------- 用户态app
  2. 应用程序正在调用系统调用函数,运行在内核空间,处于内核态,即代码是内核代码但处于应用执行流(即属于一个应用进程或应用线程) ---- 内核态app
  3. 一直运行于内核空间,处于内核态,属于内核内的任务上下文 --------- 内核线程
  4. 一直运行于内核空间,处于内核态,专门用来处理各种异常 --------- 异常上下文

6.模块编程与应用编程比较

不同点内核模块应用程序
API来源不能使用任何库函数各种库函数均可以使用
运行空间内核空间用户空间
运行权限特权模式运行非特权模式运行
编译方式静态编译进内核镜像或编译特殊的ko文件elf格式的应用程序可执行文件
运行方式模块中的函数在需要时被动调用从main开始顺序执行
入口函数init_modulemain
退出方式cleanup_modulemain函数返回或调用exit
浮点支持一般不涉及浮点运算,因此printk不支持浮点数据支持浮点运算,printf可以打印浮点数据
并发考虑需要考虑多种执行流并发的竞态情况只需考虑多任务并行的竞态
程序出错可能会导致整个系统崩溃只会让自己崩溃

7.内核接口头文件查询

大部分API函数包含的头文件在include/linux目录下,因此:

  1. 首先在include/linux 查询指定函数:grep 名称 ./ -r -n

  2. 找不到则更大范围的include目录下查询,命令同上

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值