引言:最近上班特无聊,发发牢骚,特地对内核对象、内核集合进行了了解,写此文章,方便日后一目了然
设备模型构成:①内核对象②内核集合
一:内核对象
定义:内核对象是设备模型中最基本的数据类型,内核对象与sysfs(伪文件系统,通常挂载/sys目录下)文件系统中的目录一一对应,其父子关系对应着目录的层次关系,属性对应着目录中的文件(文件名、以及文件属性)
内核对象的数据类型
structkobject{
constchar*name; //内核对象的名称
structlist_headentry;//把属于同一个集合的多个内核对象组成链表
structkobject*parent;//维护内核对象的父子关系
structkset*kset; //指向所属内核集合
structkobj_type*ktype; //指向一个用于描述内核对象内型的结构体(包括:文件名,文件属性,以及文件的读写)
structkrefkref; //引用计数
unsignedintuevent_suppress:1;//内核用户态事件是否被抑制
//...
};
内核对象的类型
structkobj_type{
void(*release)(structkobject*kobj); //内核对象的释放函数
structsysfs_ops*sysfs_ops; //sysfs文件操作(读、写)
structattribute**default_attrs; //内核对象的默认属性(文件的名称、文件的访问权限)
};
内核对象的属性
structattribute{
constchar*name;
mode_tmode;
};
内核对象的操作
structsysfs_ops{
ssize_t(*show)(structkobject*kobj,structattribute*attr,char*buf);
ssize_t(*store)(structkobject*kobj,structattribute*attr,constchar*buf,size_tsize));
};
内核对象的名称内核对象的名称属性(即文件名)intkobject_set_name(structkobject*kobj,constchar*fmt,...);内核对象的改名函数intkobject_rname(structkobject*kobj,contchar*name);内核对象的名称获取intkobject_name(conststructkobject*kobj);内核对象的初始化与注册初始化voidkobject_init(structkobject*kobj,structkobj_type*ktype);注册intkobject_add(structkobject*kobj,structkobject*parent,constchar*fmt,...);初始化与注册(以上两者合并)intkobject_init_and_add(structkobject*kobj,structkobj_type*ktype,structkobject*parent,constchar*fmt,...);动态创建structkobject*kobject_create(void);动态创建与注册structkobject*kobject_create_and_add(constchar*name,structkobject*parent);释放voidkobject_del(structkobject*kobj);
动态创建属性(属性即名字,包括文件名、访问权限)
intsysfs_create_file(structkobject*kobj,conststructattribute*attr);
动态创建的属性被删除
voidsysfs_remove_file(structkobject*kobj,conststructattribute*attr);
解释:动态创建属性,相当于在内核目录(/sys)中动态创建一个文件,应用程序读写这个文件时,内核仍然会调用内核对象时提供的show、store函数
考虑到动态创建属性
structkobj_attribute{
structattributeattr;
ssize_t(*show)(structkobject*,structkobj_attribute*attr);
ssize_t(*store)(structkobject*kobj,structkobj_attribute*attr,constchar*buf,size_tcount);
};
然后定义一个通用的show和stroe函数(以下两个函数是内核特有的,可以仿照) staticssize_tkobj_attr_show(structkobject*kobj,structattribute*attr,char*buf);
staticssize_tkobj_attr_store(structkobject*kobj,structattribute*attr,constchar*buf,size_tcount);
structsysfs_opskobj_sysfs_ops={
.show=kobj_attr_show,
.store=kobj_attr_store,
};
内核还定义了一个宏来初始化structkobj_attribute内核对象的属性
__ATTR(name,mode,show,store)
例:structkobj_attributeattr_xxx=__ATTR(XXX,S_IRUG0,XXX_show,xxx_store);
二.内核集合
定义:内核集合是基于内核对象而设计的,它可以收纳多个内核对象,并且可以管理内核对象所发送的用户态事件
内核集合数据内型
structkset{
structlist_headlist; //链表头
spinlock_tlist_lock; //自旋锁
structkobjectkobj; //内嵌的内核对象
structkset_uevent_ops*uevent_ops;//指向用户态事件操作,uenent_ops指向一个结构体,其中包含各种用户态事件的操作函数指针
};
内核集合的注册和注销
intkset_register(structkset*kset);
voidkset_ungister(structkset*kset);
动态创建并注册
structkset*kset_create_and_add(constchar*name,structkset_uevent_ops*u,structkobject*parent_kobj);
用户态事件
intkobject_uevent_env(structkobject*kobj,enumkobject_actionaction,char*envp[]);
其中第二个参数“枚举”,表示事件类型的参数
enumkobject_action{
KOBJ_ADD, //注册
KOBJ_REMOVE, //注销
KOBJ_CHANGE, //修改
KOBJ_MOVE, //移动
KOBJ_ONLINE, //在线
KOBJ_OFFLINE, //离线
KOBJ_MAX //不是一种动作,仅用来表示可选取的个数
};
用户态事件将以两种方式告知应用程序: ①用户态帮手的方式“/sbin/hotplug”(具体细节,找度娘);
②NETLINK套接字方法,在应用程序内可以直接用netlink_socket=socket(PF_NETLINK,SOCK_DGRAM,NETLINK_KOBJECT_UEVENT);
内核集合与内核对象之关系
总结:内核对象在注册前如果设置了kset指针,则注册时它就会被加入到kset指针所指向的内核集合中,它所发送的用户态事件将接收这个内核集合的过滤操作
内核集合与对象例程在加载时,首先创建并注册一个顶级子系统test,然后在这个子系统内注册若干个内核对象,每个对象有两个属性;name属性为可读,string属性为可写目录树形图:/sys|||---test(kset)(内核集合)|||-------kobj1(内核对象)| || |------struct sys_ops(读写文件)| || |------struct attribute(属性名字、访问权限)||-------kobj2(内核对象)| || |------struct sys_ops(读写文件)| |------struct attribute(属性名字、访问权限)
源码如下:
#include<linux/module.h>#defineSZ_STRING 30#defineNR_KOBJS 3staticssize_ttest_attr_show(structkobject*kobj,structattribute*attr,char*buf){structkobj_attribute*kattr;ssize_tret=-EIO;kattr=container_of(attr,structkobj_attribute,attr);if(kattr->show)ret=kattr->show(kobj,kattr,buf);returnret;}staticssize_ttest_attr_restore(structkobject*kobj,structattribute*attr,constchar*buf,size_tcount){structkobj_attribute*kattr;ssize_tret=-EIO;kattr=container_of(attr,structkobj_attribute,attr);if(kattr->store)ret=kattr->store(kobj,kattr,buf,count);returnret;}//仿照内核的kobj_sysfs_opsstaticstructsysfs_opstest_sysfs_ops = {.show=test_attr_show,.store=test_attr_restore,};staticvoidtest_release(structkobject*kobj){kfree(kobj);}//属性name的show、store函数staticssize_ttest_name_show(structkobject*kobj,structkobj_attribute*attr,char*buf){returnsprintf(buf,"%s\n",kobject_name(kobj));}staticssize_ttest_name_restore(structkobject*kobj,structkobj_attribute*attr,constchar*buf,size_tlen){return-EACCES;}//属性name的定义staticstructkobj_attributetest_attr_name=__ATTR(name,S_IRUGO,test_name_show,test_name_restore);//用于保存属性stirng对应的字符串staticchartest_string[SZ_STRING]="test\n";//属性name的show、store函数staticssize_ttest_string_show(structkobject*kobj,structkobj_attribute*attr,char*buf){returnsprintf(buf,test_string);}staticssize_ttest_string_restore(structkobject*kobj,structkobj_attribute*attr,constchar*buf,size_tlen){if(len>=SZ_STRING)len=SZ_STRING-1;memcpy(test_string,buf,len);test_string[len]='\0';returnlen;}//属性string的定义staticstructkobj_attributetest_attr_string=__ATTR(name,S_IWUGO,test_string_show,test_string_restore);//对象的默认属性,将string、name属性加入到structattribute结构中staticstructattribute*test_default_attr[]={&test_attr_name,&test_attr_string,NULL,};//对象的类型staticstructkobj_typetest_kobj_type={.release=test_release,.sysfs_ops=&test_sysfs_ops, //属性操作.default_attrs=test_default_attr //默认属性};//用于保存动态创建的内核集合的指针staticstructkset*kset;//模块初始化static__initintsys_test_init(void){inti;interr;structkobject*kobj,*next;pr_debug("sysfs_test_init:becalled.\n");//创建“内核集合”,由于没有父对象,它将成为顶级子系统,即/sys下的目录if((kset=kset_create_and_add("test",NULL,NULL))==NULL){pr_debug("sysfs_test_init:kset_create_and_addERR\n");err=-ENOMEM;gotokset_fail;}//为kset集合创建一个属性,使用name属性的定义if(err=sysfs_create_file((&kset->kobj),&test_attr_name.attr)){pr_debug("sysfs_test_init:sysfs_create_fileERR\n");gotosysfs_fail;}//注册NR_KOBJS个内核对象for(i=0;i<NR_KOBJS;i++){kobj=kzalloc(sizeof(structkobject),GFP_KERNEL);if(!kobj){pr_debug("sysfs_test_init:kzallocERR\n");err=-ENOMEM;gotokobject_fail;}//设置所属集合kobj->kset=kset;//初始化并注册err=kobject_init_and_add(kobj,&test_kobj_type,NULL,"obj%d",i);if(err){pr_debug("sysfs_test_init:kobject_init_and_addERR\n");kfree(kobj);gotokobject_fail;}kobject_uevent(kobj,KOBJ_ADD);}return0;kobject_fail:list_for_each_entry_safe(kobj,next,&kset->list,entry)kobject_put(kobj);sysfs_fail:kset_unregister(kset);kset_fail:returnerr;}static__exitvoidsysfs_test_exit(void){structkobject*kobj,*next;pr_debug("sysfs_test_exit:becalled.\n");list_for_each_entry_safe(kobj,next,&kset->list,entry)kobject_put(kobj);kset_unregister(kset);}MODULE_LICENSE("GPL");module_init(sys_test_init);module_exit(sysfs_test_exit);