本章主要讨论与linux的设备驱动和设备管理的相关的4个内核成分,设备类型,模块,内核对象,sysfs。
主要内容:
- 设备类型
- 内核模块
- 内核对象
- sysfs
- 总结
1. 设备类型
linux中主要由3种类型的设备,分别是:
|
设备类型 |
代表设备 |
特点 |
访问方式 |
| 块设备 | 硬盘,光盘 | 随机访问设备中的内容 | 一般都是把设备挂载为文件系统后再访问 |
| 字符设备 | 键盘,打印机 | 只能顺序访问(一个一个字符或者一个一个字节) | 一般不挂载,直接和设备交互 |
| 网络设备 | 网卡 | 打破了Unix "所有东西都是文件" 的设计原则 | 通过套接字API来访问 |
除了以上3种典型的设备之外,其实Linux中还有一些其他的设备类型,其中见的较多的应该算是"伪设备"。
所谓"伪设备",其实就是一些虚拟的设备,仅提供访问内核功能而已,没有物理设备与之关联。
典型的"伪设备"就是 /dev/random(内核随机数发生器), /dev/null(空设备), /dev/zero(零设备), /dev/full(满设备)
2. 内核模块
Linux内核是模块化组成的,内核中的模块可以按需加载,从而保证内核启动时不用加载所有的模块,即减少了内核的大小,也提高了效率。
通过编写内核模块来给内核增加功能或者接口是个很好的方式(既不用重新编译内核,也方便调试和删除)。
2.1 内核模块示例
内核模块可以带参数也可以不带参数,不带参数的内核模块比较简单。
我之前的几篇随笔中用于测试的例子都是用不带参数的内核模块来实验的。
2.1.1. 无参数的内核模块
参考:
《Linux内核设计与实现》读书笔记(六)- 内核数据结构
《Linux内核设计与实现》读书笔记(八)- 中断下半部的处理
《Linux内核设计与实现》读书笔记(十一)- 定时器和时间管理
2.1.2. 带参数的内核模块
构造带参数的内核模块其实也不难,内核中已经提供了简单的框架来给我们声明参数。
1. module_param(name, type, perm) : 定义一个模块参数
+ 参数 name :: 既是用户可见的参数名,也是模块中存放模块参数的变量名
+ 参数 type :: 参数的类型(byte, short, int, uint, long, ulong, charp, bool...) byte型存放在char变量中,bool型存放在int变量中
+ 参数 perm :: 指定模块在 sysfs 文件系统中对应的文件权限(关于 sysfs 的内容后面介绍)
static int stu_id = 0; // 默认id module_param(stu_id, int, 0644);
2. module_param_named(name, variable, type, perm) : 定义一个模块参数,并且参数对内对外的名称不一样
+ 参数 name :: 用户可见的参数名
+ 参数 variable :: 模块中存放模块参数的变量名
+ 参数 type和perm :: 同 module_param 中的 type 和 perm
static char* stu_name_in = "default name"; // 默认名字 module_param_named(stu_name_out, stu_name_in ,charp, 0644); /* stu_name_out 是对用户开放的名称 * stu_name_in 是内核模块内部使用的名称 */
3. module_param_string(name, string, len, perm) : 拷贝字符串到指定的字符数组
+ 参数 name :: 用户可见的参数名
+ 参数 string :: 模块中存放模块参数的变量名
+ 参数 len :: string 参数的缓冲区长度
+ 参数 perm :: 同 module_param 中的 perm
static char str_in[BUF_LEN]; module_param_string(str_out, str_in, BUF_LEN, 0); /* perm=0 表示完全禁止 sysfs 项 */
4. module_param_array(name, type, nump, perm) : 定义数组类型的模块参数
+ 参数 name :: 同 module_param 中的 name
+ 参数 type :: 同 module_param 中的 type
+ 参数 nump :: 整型指针,存放数组的长度
+ 参数 perm :: 同 module_param 中的 perm
#define MAX_ARR_LEN 5 static int arr_len; static int arr_in[MAX_ARR_LEN]; module_param_array(arr_in, int, &arr_len, 0644);
5. module_param_array_named(name, array, type, nump, perm) : 定义数组类型的模块参数,并且数组参数对内对外的名称不一样
+ 参数 name :: 数组参数对外的名称
+ 参数 array :: 数组参数对内的名称
+ 参数 type,nump,perm :: 同 module_param_array 中的 type,nump,perm
#define MAX_ARR_LEN 5 static int arr_len; static int arr_in[MAX_ARR_LEN]; module_param_array_named(arr_out, arr_in, int, &arr_len, 0644);
6. 参数描述宏
可以通过 MODULE_PARM_DESC() 来给内核模块的参数添加一些描述信息。
这些描述信息在编译完内核模块后,可以通过 modinfo 命令查看。
static int stu_id = 0; // 默认id module_param(stu_id, int, 0644); MODULE_PARM_DESC(stu_id, "学生ID,默认为 0"); // 这句就是描述内核模块参数 stu_id 的语句
7. 带参数的内核模块的示例
示例代码:test_paramed_km.c
定义了3个内核模块参数,分别是 int型,char*型,数组型的。
#include<linux/init.h> #include<linux/module.h> #include<linux/kernel.h> MODULE_LICENSE("Dual BSD/GPL"); struct student { int id; char* name; }; static void print_student(struct student*); static int stu_id = 0; // 默认id module_param(stu_id, int, 0644); MODULE_PARM_DESC(stu_id, "学生ID,默认为 0"); static char* stu_name_in = "default name"; // 默认名字 module_param_named(stu_name_out, stu_name_in ,charp, 0644); MODULE_PARM_DESC(stu_name, "学生姓名,默认为 default name"); #define MAX_ARR_LEN 5 static int arr_len; static int arr_in[MAX_ARR_LEN]; module_param_array_named(arr_out, arr_in, int, &arr_len, 0644); MODULE_PARM_DESC(arr_in, "数组参数,默认为空"); static int test_paramed_km_init(void) { struct student* stu1; int i; /* 进入内核模块 */ printk(KERN_ALERT "*************************\n"); printk(KERN_ALERT "test_paramed_km is inited!\n"); printk(KERN_ALERT "*************************\n"); // 根据参数生成 struct student 信息 // 如果没有参数就用默认参数 printk(KERN_ALERT "alloc one student....\n"); stu1 = kmalloc(sizeof(*stu1), GFP_KERNEL); stu1->id = stu_id; stu1->name = stu_name_in; print_student(stu1); // 模块数组 for (i = 0; i < arr_len; ++i) { printk(KERN_ALERT "arr_value[%d]: %d\n", i, arr_in[i]); } return 0; } static void test_paramed_km_exit(void) { /* 退出内核模块 */ printk(KERN_ALERT "*************************\n"); printk(KERN_ALERT "test_paramed_km is exited!\n"); printk(KERN_ALERT "*************************\n"); printk(KERN_ALERT "\n\n\n\n\n"); } static void print_student(struct student *stu) { if (stu != NULL) { printk(KERN_ALERT "**********student info***********\n"); printk(KERN_ALERT "student id is: %d\n", stu->id); printk(KERN_ALERT "student name is: %s\n", stu->name); printk(KERN_ALERT "*********************************\n"); } else printk(KERN_ALERT "the student info is null!!\n"); } module_init(test_paramed_km_init); module_exit(test_paramed_km_exit);