1、设备模型
2.6内核增加了一个引人注目的新特性------统一设备模型
设备模型提供了一个独立的机制专门来表示设备,并描述在系统中的拓扑结构
(1)代码重复最小化
(2)可以列出系统中所有的设备,观察到他们的状态,并且查看他们连接的总线
(3) 可以将设备和其对应的驱动联系起来
(4) 可以按照设备类型进行分类
2、kobject
设备模型的核心部分就是kobject
struct kobject {
const char *name; /*指向kobject的名字*/
struct list_head entry;
struct kobject *parent/*指向父kobject 实现层次结构*/
struct kset *kset;
struct kobj_type *ktype;
struct sysfs_dirent *sd;/*指向sysfs_dirent结构体 这个结构体就表示kobject对象的层次结构*/
struct kref kref;/*提供引用计数 其核心成员是一原子型变量,用来表示内核对象的引用计数 内核通过该成员追踪内核对象生命周期*/
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;/*是否已经加入sysfs*/
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
};
3、ktype
kobject对象被关联到一种特殊的类型 即ktype(kernel object type的缩写) ktype由kobj_type结构体来表示,定义于头文件
struct kobj_type {
void (*release)(struct kobject *kobj);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
};
1)ktype的存在是为了描述一族kobject所具有的普遍特性,实现同类的kobject都能共享相同的特性
2)release指针指向在kobject引用计数减至零时调用析构函数,该函数负责释放所有kobject使用的内存和其他相关的清理工作
3)defaults_attrars 结构体数组定义了所有具有相同类型的kobject对象的属性
4)sysfs文件系统根据对应的kobject属性来创建文件
4、kset
1)kset是kobject对象的集合体,将相关的kobject集合到一起
2)具有相同ktype的kobject可以被分组到不同的kset
5、管理和操作kobject
kobject一般都被嵌入到设备结构体中
(1)kobject的初始化一个kobject对象
void kobject_init(struct kobject *kobj,struct kobj_type *ktype)
(2)定义并初始化
struct kobject *kobject_create(void)
6、引用计数
1)kobject通过引用计数控制对象的有效生命周期
(1)初始化后kobject的引用计数置为1
(2)当引用计数为0时,则表示设备已经卸载,不能在操作对应的设备
2)操作引用计数的接口
(1)增加一个引用计数
struct kobject *kobject_get(struct kobject *kobj)
(2)减少一个引用计数
void kobject_put(struct kobject *kobj)
3)描述引用计数的结构kref
kobject计数是通过kref实现
struct kref{
atomic_t refcount
}
(1)初始化
void kref_init(struct kref *kref)
(2)增加一个计数
void kref_get(struct kref *kref)
(3)减一个计数
void kref_put(struct kref *kref)
代码:
(1)创建一个kobject对象并初始化
(2) 定制自己的kobject对象创建与释放函数
7、sysfs
sysfs文件系统是一个处于内存中的虚拟文件系统,用文件系统的方式提供kobject对象层次结构的视图
bus:提供一个系统总线视图
dev:提供已经注册设备节点的视图
device:系统中设备结构体视图
class:给用户的视图 通过对device实际设备目录的符号链接
kernel:包含内核配置项和状态信息
fs:已经注册文件系统的视图
class与devices 一个是高层概念 给用户的视图 一个底层物理设备 给内核的视图
8、sysfs添加和删除kobject
将kobject对象映射到sysfs文件系统中,产生对应的文件
int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...)
从sysfs文件系统中删除一个kobject对应文件目录,需要使用函数kobject_decl()
void kobject_del(struct kobject *kobj)
int kobject_set_name(struct kobject *kobj, const char *fmt, ...)//设置kobject对象在sysfs中的名字
int sysfs_create_dir(struct kobject * kobj)//在sysfs文件系统中创建目录
void sysfs_remove_dir(struct kobject * kobj)//在sysfs文件系统中删除目录
struct kobject *kobject_get(struct kobject *kobj)//可以用来获取kobj-parent
代码3:将对应的kobject映射到对应sysfs文件系统中,增加my_kobject_add()函数my_kobject_del()函数
代码4:将当前的kobject对象增加parent节点 增加my_kobject_create_and_add()函数
9、向sysfs添加文件
1)默认的文件集合通过kobject和kset中的ktype字段提供
2)具有相同属性的kobject导入到sysfs文件系统中的文件也具有相同的属性
3)默认文件属性由kobj_type结构来描述
struct kobj_type {
void (*release)(struct kobject *kobj);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
};
4)kobj_type结构中包含默认文件集合中所有文件的属性,
default_attrs 默认文件属性结构体数组
struct attribute {
const char *name; 属性名称,在sysfs文件系统中显示的名字
struct module *owner;所属模块 如果存在
mode_t mode;权限
};
sysfs 文件系统根据默认文件属性建立对应的文件
5)增加新的文件
(1)根据新文件属性在sysfs文件系统创建文件
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
{
BUG_ON(!kobj || !kobj->sd || !attr);
return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);
}
6)对sysfs文件系统中的文件进行读写
struct sysfs_ops {
在读sysfs文件时该方法会调用
ssize_t (*show)(struct kobject *, struct attribute *,char *);
在写sysfs文件时该方法被调用
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
};
struct kset *kset_create_and_add(const char *name,struct kset_uevent_ops *uevent_ops,struct kobject *parent_kobj)//创建一个kset集合
10、创建新属性
sysfs_create_link():增加新的链接
sysfs_remove_file():删除新的文件
sysfs_remove_link() 删除链接文件
代码5:在sysfs文件系统中增加文件
代码6:在sysfs文件系统中增加多个文件,并设置新文件的属性
代码7: 在sysfs文件系统中针对每个文件设置相应的的操作方法show()与store()
代码8:底层实现led驱动并关联到kobject对象中
11、uevent
1)kset是属于一组kobject的集合
struct kset { struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
};
kset_uevent_ops : 当kset中的kobject对象发生状态变化需要通知用户空间
struct kset_uevent_ops {
int (* const filter)(struct kset *kset, struct kobject *kobj);
const char *(* const name)(struct kset *kset, struct kobject *kobj);
int (* const uevent)(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env);
};
2)kset相关的API
void kset_init(struct kset *k)
初始化kset对象
int kset_register(struct kset *k)
初始化并向系统注册一个kset对象
static void kobject_init_internal(struct kobject *kobj)
注册kset对象
notes:
.kset对象本身也是一个kobject内核对象,在sysfs文件系统中生成一个新的目录
.注册kset对象,内核编译启用了CONFIG_HOTPLUG,则需要就这一事件通知用户空间(内核配置文件中可以查询)
.事件通知由kobject_uevent完成
.不属于kset的kobject对象不能完成事件通知
.kobject之间通过parent成员实现层次关系,当kobject的parent为NULL时,就会把kobj->kset->kobj作为kobj的parent
struct kset *kset_create_and_add(const char *name, const struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
创建一个kset对象并添加到sysfs文件系统中
3)hotplug(热插拔)
当一个设备动态加入系统时,设备驱动程序可以检查到设备,并通过通知的方式告知用户空间
通知用户空间的方式一般有两种:udev 与 /sbin/hotplug,现在使用更多的是udev
udev 的实现基于内核中的网络机制,通过标准的socket接口来监听来自内核的网络广播包,并对接收的包进行分析处理
4)hotplug相关的API
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
发送一个 event给用户空间,以网络数据包的形式发送给用户空间应用程序
@action : 发送event类型
enum kobject_action {
KOBJ_ADD,
KOBJ_REMOVE,
KOBJ_CHANGE,
KOBJ_MOVE,
KOBJ_ONLINE,
KOBJ_OFFLINE,
KOBJ_MAX
};
kobject_uevent_env()
sprintf(scratch, "%s@%s", action_string, devpath); @273L
传递给用户空间event数据的内容
"事件类型@设备路径"
example:
"add@/kset-test/kobject-test/"
uevent一般与应用程序结合起来使用,一般可以接收并处理uevent的应用程序有udev or mdev
udev工具通过netlink获取内核发出的uevent消息,并且处理,加载相应的驱动或者在/dev/目录下生成对应的设备结点
udev服务启动后,会扫描/sys目录下所有具有uevent属性文件,在进行相应的处理
uevent的触发被封装到设备模型的操作中,当添加设备的时则会发送一个uevent(user event)事件,
udev 工具会通过netlink获取uevent消息 ,然后进行加载驱动或者在/dev/目录下生成对应的设备结点
int device_add()
{
......
kobject_uevent(&dev->kobj, KOBJ_ADD);
......
}
5)netlink机制
(1)netlink机制的特点
.netlink是一种特殊的socket
.netlink可以实现内核与应用程序进行双向传输通讯,并且使用socket()API进行交互
.netlink是一种异步通讯方式,在内核与用户态进行传递的消息保存在socket缓冲区数据队列中,发送消息只是把消息保存在接收者的socket的接收队列中,而不需要等待接收者收到消息
(2)使用socket()创建netlink套接字
int socket(int domain, int type, int protocol);
@domain:协议族 AF_NETLINK or PF_NETLINK
@type :socket类型 SOCK_DGRAM or SOCK_RAW
@protocol : 协议类型 NETLINK_KOBJECT_UEVENT
(3)netlink地址结构
struct sockaddr_nl {
__kernel_sa_family_t nl_family; /* AF_NETLINK */
unsigned short nl_pad; /* zero */
__u32 nl_pid; /* port ID */
__u32 nl_groups; /* multicast groups mask */
};
@nl_family : AF_NETLINK 协议族
@nl_pad : 填0
@nl_pid : 进程pid ,可以通过getpid()获取
(3)使用setsockopt()设置套接字选项
setsockopt(sd, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));
@SO_RCVBUFFORCE:设置或者获取缓冲区的大小
(4)使用bind()绑定地址结构
retval= bind(sd, (void*)&snl, sizeof(struct sockaddr_nl))
(5)读取netlink接收缓冲区的内容
read(sd, buf, sizeof(buf));
Copyright © 2004-2024 华清远见教育科技集团 版权所有
京ICP备16055225号-5,京公海网安备11010802025203号