这段代码可以分作三个部分。第一部分是分配obj1和obj2并赋值;第二部分是初始化kobj_type变量my_type;第三部分是调用kobject_init_and_add函数来初始化kobject并把它加入到设备模型的体系架构(也就是上文中提到的内核中的那棵树)中。kobject_init_and_add是简化的写法,这个函数也可以分两步完成:kobject_init和kobject_add。
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
struct kobject *parent, const char *fmt, ...);
void kobject_init(struct kobject *kobj, struct kobj_type *ktype);
int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...);
kobject_init用来初始化kobject结构,kobject_add用来把kobj加入到设备模型之中。在实作中,我们先对obj1进行初始化和添加的动作,调用参数里,parent被赋为NULL,表示obj1没有父对象,反映到sysfs里,my_kobj1的目录会出现在/sys下,obj2的父对象设定为obj1,那么my_kobj2的目录会出现在/sys/my_kobj1下面。
前面提到,kobject也提供了引用计数的功能,虽然本质上是利用kref,但也提供了另外的接口供用户使用。
struct kobject *kobject_get(struct kobject *kobj);
void kobject_put(struct kobject *kobj);
kobject_init_and_add和kobject_init这两个函数被调用后,kobj的引用计数会初始化为1,所以在module_exit时要记得用kobject_put来释放引用计数。
我们再回到实作中,看看如何使用ktype。代码里,my_attrs是这样定义的:
struct attribute name_attr = {
.name = "name",
.mode = 0444,
};
struct attribute val_attr = {
.name = "val",
.mode = 0666,
};
struct attribute *my_attrs[] = {
&name_attr,
&val_attr,
NULL,
};
结构体struct attribute里的name变量用来指定文件名,mode变量用来指定文件的访问权限。这里需要着重指出的是,数组my_attrs的最后一项一定要赋为NULL,否则会造成内核oops。
sysfs_ops的代码如下:
ssize_t my_show(struct kobject *kobj, struct attribute *attr, char *buffer)
{
struct my_kobj *obj = container_of(kobj, struct my_kobj, kobj);
ssize_t count = 0;
if (strcmp(attr->name, "name") == 0) {
count = sprintf(buffer, "%s\n", kobject_name(kobj));
} else if (strcmp(attr->name, "val") == 0) {
count = sprintf(buffer, "%d\n", obj->val);
}
return count;
}
ssize_t my_store(struct kobject *kobj, struct attribute *attr, const char *buffer, size_t size)
{
struct my_kobj *obj = container_of(kobj, struct my_kobj, kobj);
if (strcmp(attr->name, "val") == 0) {
sscanf(buffer, "%d", &obj->val);
}
return size;
}
struct sysfs_ops my_sysfsops = {
.show = my_show,
.store = my_store,
};
【福利】填问卷送精选测试礼包+接口测试课程!为测试行业做点事!