1 设备类型
类型 | 特点 | 例子 |
---|---|---|
字符设备 | 一个个字符方式访问 | 键盘 |
块设备 | 以块为单位,对数据随机访问 | flash |
网络设备 | 打破了“一切皆文件”的原则 | 网卡 |
其他设备类型:
杂项设备——简化的字符设备
伪设备——虚拟的,例如/dev/null(空设备), /dev/zero(零设备)
2 模块
(1)构建模块方式
1)放在内核源码树中
2)放在内核代码外
此时,写个Makefile较简单
KVERS = $(shell uname -r)
obj-m += hello.o
build: kernel_modules
EXTRA_CFLAGS=-g -o0
kernel_modules:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules
clean:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean
(2)载入
insmod module.ko
rmmod module
若模块依赖其他的模块,可使用modprobe
命令,会自动加载依赖的模块。
(3)参数
驱动程序声明的参数,允许应用程序在加载模块时指定参数
module_param(name, type, perm) //定义一个模块参数
module_param_named(name, variable, type, perm) //定义一个模块参数,并且参数对内对外的名称不一样
module_param_string(name, string, len, perm) //拷贝字符串到指定的字符数组
module_param_array(name, type, nump, perm) //定义数组类型的模块参数
(4)导出符号表
只有被显示导出后的外部函数,才能被动态库调用。
EXPORT_SYMBOL(函数名)
EXPORT_SYMBOL_GPL(函数名)
3 设备模型
内核最初为了电源管理,提出了设备模型来管理所有的设备,建立了系统中设备的拓扑关系。
sysfs文件系统是一个虚拟文件系统,提供了系统中各种设备的拓扑结构,通过输入“tree /sys”
,可以看到拓扑结构
|--block
|--bus
|--platform
|--serio
|--class
|--dev
|--devices
|--firmware
...
最重要的目录是devices
目录,这就是系统中实际的设备拓扑。
3.1 结构体
(1)kobject
核心部分,一个kobject对应sysfs里的一个目录
struct kobject {
const char *name; /* kobject 名称 */
struct list_head entry; /* kobject 链表 */
struct kobject *parent; /* kobject 的父对象,说明kobject是有层次结构的 */
struct kset *kset; /* kobject 的集合,接下来有详细介绍 */
struct kobj_type *ktype; /* kobject 的类型,接下来有详细介绍 */
struct sysfs_dirent *sd; /* 在sysfs中,这个结构体表示kobject的一个inode结构体,sysfs之后也会介绍 */
struct kref kref; /* 提供 kobject 的引用计数 */
/* 一些标志位 */
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
};
kobject 本身不代表什么实际的内容,一般都是嵌在其他数据结构中来发挥作用。例如cdev
struct cdev {
struct kobject kobj; /* 嵌在 cdev 中的kobject */
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
(2)ktype
ktype是为了描述一族kobject所具有的普遍属性
struct kobj_type {
void (*release)(struct kobject *kobj); /* kobject的引用计数降到0时触发的析构函数,负责释放和清理内存的工作 */
struct sysfs_ops *sysfs_ops; /* sysfs操作相关的函数 */
struct attribute **default_attrs; /* kobject 相关的默认属性 */
};
(3)kset
kset是kobject对象的集合体,可以所有相关的kobject置于一个kset之中
struct kset {
struct list_head list; /* 表示kset中所有kobject的链表 */
spinlock_t list_lock; /* 用于保护 list 的自旋锁*/
struct kobject kobj; /* kset中嵌入的一个kobject,使得kset也可以表现的像一样kobject一样*/
struct kset_uevent_ops *uevent_ops; /* 处理kset中kobject的热插拔事件 提供了与用户空间热插拔进行通信的机制 */
};
(4)kref
kobject的引用计数减为0时,对象便会释放。
struct kref {
atomic_t refcount; /* 只有一个表示引用计数的属性,atomic_t 类型表示对它的访问是原子操作 */
};
操作
void kref_put(struct kref *kref, int num); /* 减少引用计数 -1 */
void kref_get(struct kref *kref); /* 增加引用计数 +1 */
(5)关系
kobject与一个特别的ktype关联,在kobject中ktype字段指向该对象。kobject又归入kset集合,kset提供了两个功能:
1)其中嵌入的kobject作为kobject组的基类
2)kset将相关的kobjcet集合在一起
3.2 程序实例
建立的目录
my_kset/
|-- mykobj1
| |-- name
| `-- val
`-- mykobj2
|-- name
`-- val
代码
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/kobject.h>
#include<linux/sysfs.h>
MODULE_LICENSE("Dual BSD/GPL");
static void myobj_release(struct kobject*);
static ssize_t my_show(struct kobject *, struct attribute *, char *);
static ssize_t my_store(struct kobject *, struct attribute *, const char *, size_t);
/* 自定义的结构体,2个属性,并且嵌入了kobject */
struct my_kobj
{
int ival;
char* cname;
struct kobject kobj;
};
static struct my_kobj *myobj = NULL;
/* my_kobj 的属性 ival 所对应的sysfs中的文件,文件名 val */
static struct attribute val_attr = {
.name = "val",
.owner = NULL,
.mode = 0666,
};
/* my_kobj 的属性 cname 所对应的sysfs中的文件,文件名 name */
static struct attribute name_attr = {
.name = "name",
.owner = NULL,
.mode = 0666,
};
static int test_kobject_default_attr_init(void)
{
struct attribute *myattrs[] = {NULL, NULL, NULL};
struct sysfs_ops *myops = NULL;
struct kobj_type *mytype = NULL;
/* 初始化 myobj */
myobj = kmalloc(sizeof(struct my_kobj), GFP_KERNEL);
if (myobj == NULL)
return -ENOMEM;
/* 配置文件 val 的默认值 */
myobj->ival = 100;
myobj->cname = "test";
/* 初始化 ktype */
mytype = kmalloc(sizeof(struct kobj_type), GFP_KERNEL);
if (mytype == NULL)
return -ENOMEM;
/* 增加2个默认属性文件 */
myattrs[0] = &val_attr;
myattrs[1] = &name_attr;
/* 初始化ktype的默认属性和析构函数 */
mytype->release = myobj_release;
mytype->default_attrs = myattrs;
/* 初始化ktype中的 sysfs */
myops = kmalloc(sizeof(struct sysfs_ops), GFP_KERNEL);
if (myops == NULL)
return -ENOMEM;
myops->show = my_show;
myops->store = my_store;
mytype->sysfs_ops = myops;
/* 初始化kobject,并加入到sysfs中 */
memset(&myobj->kobj, 0, sizeof(struct kobject)); /* 这一步非常重要,没有这一步init kobject会失败 */
if (kobject_init_and_add(&myobj->kobj, mytype, NULL, "test_kobj_default_attr"))
kobject_put(&myobj->kobj);
printk(KERN_ALERT "*************************\n");
printk(KERN_ALERT "test_kobject_default_attr is inited!\n");
printk(KERN_ALERT "*************************\n");
return 0;
}
static void test_kobject_default_attr_exit(void)
{
kobject_del(&myobj->kobj);
kfree(myobj);
/* 退出内核模块 */
printk(KERN_ALERT "*************************\n");
printk(KERN_ALERT "test_kobject_default_attr is exited!\n");
printk(KERN_ALERT "*************************\n");
printk(KERN_ALERT "\n\n\n\n\n");
}
static void myobj_release(struct kobject *kobj)
{
printk(KERN_ALERT, "release kobject");
kobject_del(kobj);
}
/* 读取属性文件 val 或者name时会执行此函数 */
static ssize_t my_show(struct kobject *kboj, struct attribute *attr, char *buf)
{
printk(KERN_ALERT "SHOW -- attr-name: [%s]\n", attr->name);
if (strcmp(attr->name, "val") == 0)
return sprintf(buf, "%d\n", myobj->ival);
else
return sprintf(buf, "%s\n", myobj->cname);
}
/* 写入属性文件 val 或者name时会执行此函数 */
static ssize_t my_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len)
{
printk(KERN_ALERT "STORE -- attr-name: [%s]\n", attr->name);
if (strcmp(attr->name, "val") == 0)
sscanf(buf, "%d\n", &myobj->ival);
else
sscanf(buf, "%s\n", myobj->cname);
return len;
}
module_init(test_kobject_default_attr_init);
module_exit(test_kobject_default_attr_exit);
说明:
(1)添加和删除kobject
int kobject_add(struct kobject *kobj,
struct kobject *parent,
const char *fmt, ...); /* 设置此kobject的parent,将此kobject加入到现有对象层次结构中 */
void kobject_del(struct kobject *kobj); /* 将此kobject从现有对象层次结构中取消 */
(2)属性
默认属性default_attrs
struct attribute {
const char *name; /* sysfs文件树中的文件名 */
struct module *owner; /* x86体系结构中已经不再继续使用了,可能在其他体系结构中还会使用 */
mode_t mode; /* sysfs中该文件的权限 */
};
sys字段描述如何使用它们
struct sysfs_ops {
/* 在读sysfs文件时该方法被调用 */
ssize_t (*show)(struct kobject *kobj, struct attribute *attr,char *buffer);
/* 在写sysfs文件时该方法被调用 */
ssize_t (*store)(struct kobject *kobj,struct attribute *attr,const char *buffer, size_t size);
};
3.3 内核事件
内核事件层实现了内核到用户的消息通知机制,该机制就是建立在kobject基础上的。
内核向用户空间发送信号使用函数
int kobject_uevent(struct kobject *kobj, enum kobject_action action);
参考:
Linux设备模型 (1)