文章目录
本期和你们主要分享的是驱动开发内核编译过程中对于模块是怎样设计的,进行了详尽的分享,从模块传参、模块依赖仍然到内核空间用户空间以及模块编程和应用编程的比较,希望诸位男子伴才能把这种基础的知识点把握好,为今后成功进阶为优秀的驱动开发工程师打好基础!
一、模块传参
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可查看那些参数的描述信息
下边给出一个关于模块传参的事例:
#include
#include
int gx = 10;
char *gstr = "hello";
int garr[5] = {1,2,3,4,5};
/* 添加模块参数 */
module_param(gx, int, 0664);
module_param(gstr, charp, 0664);
module_param_array(garr, int, NULL, 0664);
int __init myhello_init(void)
{
int i = 0;
printk("gx = %dn", gx);
printk("gstr = %sn", gstr);
for(i = 0;i < 5;++i)
{
printk("garr[%d] = %dn", i, garr[i]);
}
return 0;
}
void __exit myhello_exit(void)
{
printk("myhello bye bye!!!n");
}
MODULE_LICENSE("GPL");
module_init(myhello_init);
module_exit(myhello_exit);
这么程序运行的结果如下:
这儿还可以进行下述形式的参数传递:
sudoinsmod./testparam.kogx=400gstr=“nihao”garr=4,5,6,7,8
这儿能发觉的推论是当字符串中间出现空格的时侯,会对形参进行打断,最终输入的有效字符串仅为空格上面的部份!
二、模块依赖
既然内核模块的代码与其它内核代码共用统一的运行环境linux内核编程,也就是说模块只是存在方式上独立,运行上虽然和内核其它源码是一个整体,它们隶属于同一个程序,因而一个模块或内核其它部份源码应当可以使用另一个模块的一些全局特点。
一个模块中那些可以被其它地方使用的名称被称为导入符号linux操作系统界面,所有导入符号被填在同一个表中这个表被称为符号表。
最常用的可导入全局特点为全局变量和函数
查看符号表的命令:nm
nm查看elf格式的可执行文件或目标文件中包含的符号表,用法:
nm文件名(可以通过mannm查看一些字母涵义)
两个用于导入模块中符号名称的宏:
EXPORT_SYMBOL(函数名或全局变量名)
EXPORT_SYMBOL_GPL(函数名或全局变量名)须要GPL许可证合同验证
使用导入符号的地方,须要对那些符号进行extern申明后才会使用这种符号
B模块使用了A模块导入的符号,此时称B模块依赖于A模块百度网盘LINUX,则:
编译顺序:先编译模块A,再编译模块B,当两个模块源码在不同目录时,须要:i.先编译导入符号的模块Aii.拷贝A模块目录中的Module.symvers到B模块目录iii.编译使用符号的模块B。否则编译B模块时有符号未定义错误加载顺序:先插入A模块,再插入B模块,否则B模块插入失败卸载顺序:先卸载B模块,在卸载A模块,否则A模块卸载失败
补充说明:
内核符号表(直接当文本文件查看)
/proc/kallsyms运行时/boot/System.map编译后
三、内核空间和用户空间
为了彻底解决一个应用程序出错不影响系统和其它app的运行,操作系统给每位app一个独立的假想的地址空间,这个假想的地址空间被称为虚拟地址空间(也叫逻辑地址),操作系统也占用其中固定的一部份linux内核编程,32位Linux的虚拟地址空间大小为4G,并将其界定两部份:
0~3G用户空间:每位应用程序只能使用自己的这份虚拟地址空间
3G~4G内核空间:内核使用的虚拟地址空间,应用程序不能直接使用这份地址空间,但可以通过一些系统调用函数与其中的个别空间进行数据通讯
实际显存操作时,须要将虚拟地址映射到实际显存的数学地址,之后才进行实际的显存读写
四、执行流
执行流:有开始有结束总体次序执行的一段独立代码,又被称为代码上下文
计算机系统中的执行流的分类:
执行流:
任务流–任务上下文(都参与CPU时间片轮转,都有任务五状态:就绪态运行态睡眠态僵死态暂停态)进程线程内核线程:内核创建的线程应用线程:应用进程创建的线程异常流–异常上下文中断其它异常
应用编程可能涉及到的执行流:
进程线程
内核编程可能涉及到的执行流:
应用程序自身代码运行在用户空间,处于用户态—————--用户态app应用程序正在调用系统调用函数,运行在内核空间,处于内核态,即代码是内核代码但处于应用执行流(即属于一个应用进程或应用线程)—-内核态app仍然运行于内核空间,处于内核态,属于内核内的任务上下文———内核线程仍然运行于内核空间,处于内核态,专门拿来处理各类异常———异常上下文五、模块编程与应用编程的比较不同点内核模块应用程序
API来源
不能使用任何库函数
各类库函数均可以使用
运行空间
内核空间
用户空间
运行权限
特权模式运行
非特权模式运行
编译方法
静态编译进内核镜像或编译特殊的ko文件
elf格式的应用程序可执行文件
运行方法
模块中的函数在须要时被动调用
从main开始次序执行
入口函数
init_module
main
退出形式
cleanup_module
main函数返回或调用exit
浮点支持
通常不涉及浮点运算,因而printk不支持浮点数据
支持浮点运算,printf可以复印浮点数据
并发考虑
须要考虑多种执行流并发的竞态情况
只需考虑多任务并行的竞态
程序出错
可能会造成整个系统崩溃
只会让自己崩溃
六、内核接口头文件查询
大部份API函数包含的头文件在include/linux目录下,因而:
首先在include/linux查询指定函数:grep名称./-r-n找不到则更大范围的include目录下查询,命令同上
给出一个反例:查找atomic_set对应的头文件
表示头文件为总结
本期主要分享的是关于驱动开发过程中一些基础必备的知识点,除过基本的知识点,还有一些常用操作,把握了这种才能帮助我们提高工作效率,希望你们都联练习上去,学因而用!!!
最后,诸位男子伴们假如喜欢我的分享可以点赞收藏哦,大家的认可是我创作的动力,一起加油!