手机网站尺寸大小,3免费做网站,企业管理10大系统,怎么做网站认证一、内核模块基础代码解析 一个内核模块代码错误仍然会导致的内核崩溃。
GPL协议#xff1a;开源规定#xff0c;使用内核一些函数需要
1、单内核的缺点
单内核扩展性差的缺点减小内核镜像文件体积#xff0c;一定程度上节省内存资源提高开发效率不能彻底解决稳定性低的缺…一、内核模块基础代码解析 一个内核模块代码错误仍然会导致的内核崩溃。
GPL协议开源规定使用内核一些函数需要
1、单内核的缺点
单内核扩展性差的缺点减小内核镜像文件体积一定程度上节省内存资源提高开发效率不能彻底解决稳定性低的缺点内核模块代码出错可能会导致整个系统崩溃
内核模块的本质一段隶属于内核的“动态”代码与其它内核代码是同一个运行实体共用同一套运行资源只是存在形式上是独立的。
#include linux/module.h //包含内核编程最常用的函数声明如printk
#include linux/kernel.h //包含模块编程相关的宏定义如MODULE_LICENSE/*该函数在模块被插入进内核时调用主要作用为新功能做好预备工作被称为模块的入口函数__init的作用 :
1. 一个宏展开后为__attribute__ ((__section__ (.init.text))) 实际是gcc的一个特殊链接标记
2. 指示链接器将该函数放置在 .init.text区段
3. 在模块插入时方便内核从ko文件指定位置读取入口函数的指令到特定内存位置
*/
int __init myhello_init(void)
{/*内核是裸机程序不可以调用C库中printf函数来打印程序信息Linux内核源码自身实现了一个用法与printf差不多的函数命名为printk k-kernelprintk不支持浮点数打印*/printk(#####################################################\n);printk(#####################################################\n);printk(#####################################################\n);printk(#####################################################\n);printk(myhello is running\n);printk(#####################################################\n);printk(#####################################################\n);printk(#####################################################\n);printk(#####################################################\n);return 0;
}/*该函数在模块从内核中被移除时调用主要作用做些init函数的反操作被称为模块的出口函数__exit的作用
1.一个宏展开后为__attribute__ ((__section__ (.exit.text))) 实际也是gcc的一个特殊链接标记
2.指示链接器将该函数放置在 .exit.text区段
3.在模块插入时方便内核从ko文件指定位置读取出口函数的指令到另一个特定内存位置
*/
void __exit myhello_exit(void)
{printk(myhello will exit\n);
}/*
MODULE_LICENSE(字符串常量);
字符串常量内容为源码的许可证协议 可以是GPL GPL v2 GPL and additional rights Dual BSD/GPL Dual MIT/GPL Dual MPL/GPL等, GPL最常用其本质也是一个宏宏体也是一个特殊链接标记指示链接器在ko文件指定位置说明本模块源码遵循的许可证
在模块插入到内核时内核会检查新模块的许可证是不是也遵循GPL协议如果发现不遵循GPL则在插入模块时打印抱怨信息myhellomodule license unspecified taints kernelDisabling lock debugging due to kernel taint
也会导致新模块没法使用一些内核其它模块提供的高级功能
*/
MODULE_LICENSE(GPL);/*
module_init 宏
1. 用法module_init(模块入口函数名)
2. 动态加载模块对应函数被调用
3. 静态加载模块内核启动过程中对应函数被调用
4. 对于静态加载的模块其本质是定义一个全局函数指针并将其赋值为指定函数链接时将地址放到特殊区段.initcall段方便系统初始化统一调用。
5. 对于动态加载的模块由于内核模块的默认入口函数名是init_module,用该宏可以给对应模块入口函数起别名
*/
module_init(myhello_init);/*
module_exit宏
1.用法module_exit(模块出口函数名)
2.动态加载的模块在卸载时对应函数被调用
3.静态加载的模块可以认为在系统退出时对应函数被调用实际上对应函数被忽略
4.对于静态加载的模块其本质是定义一个全局函数指针并将其赋值为指定函数链接时将地址放到特殊区段.exitcall段方便系统必要时统一调用实际上该宏在静态加载时没有意义因为静态编译的驱动无法卸载。
5.对于动态加载的模块由于内核模块的默认出口函数名是cleanup_module,用该宏可以给对应模块出口函数起别名
*/
module_exit(myhello_exit);
myhello.c:内核模块函数代码
二内核模块的三要素 模块三要素入口函数 出口函数 MODULE__LICENSE
二、内核模块多源文件
如果一个内核模块太大多个文件编译成一个.ko文件
ifeq ($(KERNELRELEASE),)ifeq ($(ARCH),arm)
KERNELDIR ? 目标板linux内核源码顶层目录的绝对路径
ROOTFS ? 目标板根文件系统顶层目录的绝对路径
else
KERNELDIR ? /lib/modules/$(shell uname -r)/build
endif
PWD : $(shell pwd)modules:$(MAKE) -C $(KERNELDIR) M$(PWD) modulesmodules_install:$(MAKE) -C $(KERNELDIR) M$(PWD) INSTALL_MOD_PATH$(ROOTFS) modules_installclean:rm -rf *.o *.ko .*.cmd *.mod.* modules.order Module.symvers .tmp_versionselse
obj-m hello.o
hello-objs f1.o f2.o ... ... ... ... ...endif
Makefile中
obj-m用来指定模块名注意模块名加.o而不是.ko
可以用 模块名-objs 变量来指定编译到ko中的所有.o文件名每个同名的.c文件对应的.o目标文件
一个目录下的Makefile可以编译多个模块
添加obj-m 下一个模块名.o makefile: 三、内核模块信息宏
MODULE_AUTHOR(字符串常量); //字符串常量内容为模块作者说明MODULE_DESCRIPTION(字符串常量); //字符串常量内容为模块功能说明MODULE_ALIAS(字符串常量); //字符串常量内容为模块别名
四、模块传参 perm权限0664
6用户可读可写
6主用户可读可写
4其他用户可读
可执行一般不用 module_param(name,type,perm);//将指定的全局变量设置成模块参数
/*
name:全局变量名
type使用符号 实际类型 传参方式bool bool insmod xxx.ko 变量名0 或 1invbool bool insmod xxx.ko 变量名0 或 1charp 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_paramtype指数组中元素的类型
num存放数组大小变量的地址可以填NULL确保传参个数不越界传参方式 insmod xxx.ko 数组名元素值0,元素值1,...元素值num-1
*/
可用MODULE_PARAM_DESC宏对每个参数进行作用描述用法
MODULE_PARM_DESC(变量名,字符串常量);
字符串常量的内容用来描述对应参数的作用
modinfo可查看这些参数的描述信息
五、模块依赖
一个模块用到另外一个模块的参数
B模块依赖于A模块B模块要extern变量A模块内也需要用宏声明可以被外部调用的全局变量。
编译顺序一定要先A后B插入顺序也要先A后B。卸载顺序先B后A。 modulea.c: moduleb: 既然内核模块的代码与其它内核代码共用统一的运行环境也就是说模块只是存在形式上独立运行上其实和内核其它源码是一个整体它们隶属于同一个程序因此一个模块或内核其它部分源码应该可以使用另一个模块的一些全局特性。
一个模块中这些可以被其它地方使用的名称被称为导出符号所有导出符号被填在同一个表中这个表被称为符号表。
最常用的可导出全局特性为全局变量和函数
查看符号表的命令nm nm查看elf格式的可执行文件或目标文件中包含的符号表用法
nm 文件名 可以通过man nm查看一些字母含义
两个用于导出模块中符号名称的宏
EXPORT_SYMBOL(函数名或全局变量名) EXPORT_SYMBOL_GPL(函数名或全局变量名) 需要GPL许可证协议验证
使用导出符号的地方,需要对这些符号进行extern声明后才能使用这些符号
B模块使用了A模块导出的符号此时称B模块依赖于A模块则
编译次序先编译模块A再编译模块B当两个模块源码在不同目录时需要i. 先编译导出符号的模块A ii. 拷贝A模块目录中的Module.symvers到B模块目录 iii. 编译使用符号的模块B。否则编译B模块时有符号未定义错误加载次序先插入A模块再插入B模块否则B模块插入失败卸载次序先卸载B模块在卸载A模块否则A模块卸载失败
补充说明
乌班图内核符号表 运行前 /proc/kallsyms运行时 /boot/System.map编译后
运行后
开发板 六、内核空间和用户空间
为了彻底解决一个应用程序出错不影响系统和其它app的运行操作系统给每个app一个独立的假想的地址空间这个假想的地址空间被称为虚拟地址空间也叫逻辑地址操作系统也占用其中固定的一部分32位Linux的虚拟地址空间大小为4G并将其划分两部分 0~3G 用户空间 每个应用程序只能使用自己的这份虚拟地址空间 3G~4G 内核空间内核使用的虚拟地址空间应用程序不能直接使用这份地址空间但可以通过一些系统调用函数与其中的某些空间进行数据通信
七、执行流
内核态使用内核空间用户态使用用户空间。
执行流有开始有结束总体顺序执行的一段独立代码又被称为代码上下文
计算机系统中的执行流的分类
执行流
任务流--任务上下文都参与CPU时间片轮转都有任务五状态就绪态 运行态 睡眠态 僵死态 暂停态 进程线程 内核线程内核创建的线程应用线程应用进程创建的线程异常流--异常上下文 中断其它异常
应用编程可能涉及到的执行流
进程线程
内核编程可能涉及到的执行流
应用程序自身代码运行在用户空间处于用户态 ----------------- 用户态app应用程序正在调用系统调用函数运行在内核空间处于内核态即代码是内核代码但处于应用执行流即属于一个应用进程或应用线程 ---- 内核态app一直运行于内核空间处于内核态属于内核内的任务上下文 --------- 内核线程一直运行于内核空间处于内核态专门用来处理各种异常 --------- 异常上下文
八、模块编程和应用编程的比较 1、API内核不能使用任何库函数应用程序可以使用库函数。
2、并发考虑内核考虑多种执行流并发手段丰富应用层需要考虑多任务异常上下文。
3、程序出错
九、内核接口头文件查询
大部分API函数包含的头文件在include/linux目录下因此
首先在include/linux 查询指定函数grep 名称 ./ -r -n找不到则更大范围的include目录下查询命令同上
定位函数/结构体对应的头文件