内核模块–参数和依赖
1.模块传参
在insmod xxx.ko加载模块时,可以在后面添加参数,类似于应用编程中的主函数传参。只是这里的入口函数没有参数列表,因此参数需要用对应的函数来设置参数,即module_param(接收的全局变量名,传递的参数类型,权限一般为0664)用来传递单个数据,module_param_array(接收的全局变量名,传递的参数类型,存放数组变量大小的地址即&num(当确定传参个数不越界可以写NULL),权限一般为0664)。代码例子如下:
#include <linux/module.h>
#include <linux/kernel.h>
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 testparam_init(void)
{
int i = 0;
printk("gx=%d\n", gx);
printk("gstr=%s\n", gstr);
for(i = 0; i < 5; i++)
{
printk("%d ", garr[i]);
}
printk("\n");
return 0;
}
void __exit testparam_exit(void)
{
printk("testparam will exit\n");
}
MODULE_LICENSE("GPL");
module_init(testparam_init);
module_exit(testparam_exit);
然后编译生成.ko文件后,即可使用sudo insmod xxx.ko gx=100 gstr=“hi” garr=5,6,7,8,9来执行
2.模块依赖
2.1 一个模块的全局变量或函数要提供给其他模块使用时,需要在提供方里面添加一个宏为EXPORT_SYMBOL(函数名或全局变量名),然后在另一个模块就可以通过extern xxx来使用了。前提是这些模块在同一个目录下。
2.2当模块不在同一个目录下时,提供方编译出来的Module.symvers要拷贝到使用方的目录下才可以。这是因为Module.symvers里面包含了提供方的符号表,这样使用方才可以找到。
3.内核空间和用户空间
内核中为了防止任务出错后不影响其他任务。内存这一块使用了虚拟地址空间,32系统中,0-3G为用户空间,3-4G为内核空间。即应用程序编写好的查看到对应的地址都是虚拟空间的地址,然后在通过内存管理单元映射到实际的物理地址。系统启动后有一段程序是在物理地址的,当把内存管理单元(MMU)初始化之后,内核后面的地址都是指的虚拟空间地址。
4.执行流
执行流:一段从开始到结束的一段独立代码,又被称为代码上下文
计算机系统中执行流分为两类:
1.任务流–任务上下文(都参与时间片轮转,都有任务的五种状态:就绪态,执行态,睡眠态,僵尸态,暂停态)
1.1进程
1.2线程
1.2.1内核线程:内核创建的线程
1.2.2应用线程:应用进程创建的线程
2.异常流–异常上下文
2.1中断
2.2其他异常
应用编程里面不需要考虑异常流,只有任务流,而内核编程需要考虑异常流,因此要求更高。并且异常流发生后,去执行异常处理程序,此时时间片轮转机制会停止,直到异常流处理完后,时间片轮转机制才会重新开始。
内核编程可能涉及到的执行流:
1.应用程序自身代码一直运行在用户空间,处于用户态------用户态app
2.应用程序在调用系统调用的函数,正在执行系统调用函数,而系统调用函数是在内核里面实现的即此时代码是内核代码而处于应用执行流(即属于一个应用进程或应用线程)------内核态app
3.一直运行在内核空间,处于内核态,属于内核内的任务上下文-----内核线程,此时也参与时间片轮转。
4.一直运行在内核空间,处于内核态,专门用来处理各种异常-----异常上下文
内核编程和应用编程区别
1.API:内核模块不支持库函数调用,应用编程支持库函数
2.并发考虑:内核模块需要考虑多种情况的竞态,应用编程只需要考虑多任务并行的竞态
3.程序出错:内核模块可能会导致整个系统崩溃,应用编程只会让自己崩溃