0%

周谈(54)- sysfs属性文件使用

前言

最近在看华为加速引擎KAE的源码,该库通过sysfs文件将设备驱动的一些信息导出到用户态,在用户态代码中通过访问/sys下的文件,获取或者操作加速引擎,而不是使用ioctl的方式。抽空看看内核文档,了解一下sysfs的系统使用及编程。

sysfs

sysfs是一个导出内核对象的文件系统。sysfs是基于ramfs的一个内存文件系统。为用户态提供了一种访问内核数据和属性的方式。

sysfs始终与kobject的底层结构紧密相关。要深入理解的话,还需要了解kobject。

使用sysfs

内核是否编译了sysfs可以通过是否定义宏CONFIG_SYSFS来确定。
一旦有 kobject 在系统中注册,就会有一个目录在sysfs中被创建。这个目录是作为 kobject 的 parent 下的子目录创建的,以准确的传递内核的对象层次到用户空间。比如我们使用class_create创建了一个class,那么对应/sys/class下就会增加一个文件夹,sysfs中的顶层目录代表着内核对象层次的共同祖先;

kobject 的属性能在文件系统中以普通文件的形式导出,Sysfs 为属性定义了面向文件 I/O 操作的方法,以提供对内核属性的读写。属性应为ASCII 码文本文件,以一个文件只存储一个属性值为宜,也可以存一个数组。

后面主要说一下属性的编程使用,属性文件可以方便内核数据的查看及操作。

属性

驱动子系统定义了设备的属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};

int device_create_file(struct device *, const struct device_attribute *);
void device_remove_file(struct device *, const struct device_attribute *);

#define DEVICE_ATTR_RW(_name) \
struct device_attribute dev_attr_##_name = __ATTR_RW(_name)

#define DEVICE_ATTR_RO(_name) \
struct device_attribute dev_attr_##_name = __ATTR_RO(_name)

#define DEVICE_ATTR_WO(_name) \
struct device_attribute dev_attr_##_name = __ATTR_WO(_name)

通过宏定义可以很方便的设置一个属性,然后实现对应的xxx_show, xxx_store函数,再通过device_create_file创建属性文件,这个文件默认位于/sys/class/xxx_class/xxx/目录下, xxx位设备名。
相应的我们会创建/dev/xxx字符设备。

代码示例

在struct device下有一个groups成员,可以把属性文件聚合成多个组,创建组对应的目录,目录下生成各个属性的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

static ssize_t dev_state_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct test_dev *testdev = dev_get_drvdata(dev);
return sprintf(buf, "dev_state %lu\n", testdev->dev_state);
}

static ssize_t dev_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct test_dev *testdev = dev_get_drvdata(dev);
unsigned long val = 0;

pr_info("dev_state store buf: %s, count %lu\r\n", buf, count);
if (kstrtoul(buf, 0, &val) < 0)
return -EINVAL;

testdev->dev_state = val;

return count;
}

static DEVICE_ATTR_RW(dev_state);
/* 属性数组 */
static struct attribute *test_dev_attrs[] = {
&dev_attr_id.attr,
&dev_attr_dev_state.attr,
&dev_attr_api_ver.attr,
NULL,
};

/*包装后的属性组*/
static const struct attribute_group test_dev_attr_group = {
.name = "attrs",
.attrs = test_dev_attrs,
};

/*多个属性组*/
static const struct attribute_group *test_dev_attr_groups[] = { &test_dev_attr_group, NULL };

...

static int test_create_chrdev(struct test_dev *testdev)
{
int ret;

ret = idr_alloc(&test_idr, testdev, 0, 0, GFP_KERNEL);
if (ret < 0)
return ret;

cdev_init(&testdev->cdev, &testdev_fops);
testdev->dev_id = ret;
testdev->cdev.owner = THIS_MODULE;

device_initialize(&testdev->dev);
testdev->dev.devt = MKDEV(MAJOR(test_devt), testdev->dev_id);
testdev->dev.class = test_class;
testdev->dev.groups = test_dev_attr_groups;
testdev->dev.release = test_dev_release;
dev_set_name(&testdev->dev, "%s", testdev->name);
dev_set_drvdata(&testdev->dev, testdev);
ret = cdev_device_add(&testdev->cdev, &testdev->dev);
if (ret)
goto err_with_idr;

// device_create_file(&testdev->dev, &dev_attr_dev_state);
dev_dbg(&testdev->dev, "create testdev minior=%d\n", testdev->dev_id);
return 0;

err_with_idr:
idr_remove(&test_idr, testdev->dev_id);
return ret;
}


// 驱动安装后,生成对应的结构

root@Ubuntu:# tree /sys/class/test/testdev/
/sys/class/test/testdev/
|-- attrs // 属性组
| |-- api_ver
| |-- dev_state // dev_state属性文件
| `-- id
|-- dev
|-- power
| |-- async
| |-- autosuspend_delay_ms
| |-- control
| |-- runtime_active_kids
| |-- runtime_active_time
| |-- runtime_enabled
| |-- runtime_status
| |-- runtime_suspended_time
| `-- runtime_usage
|-- subsystem -> ../../../../class/test
`-- uevent

root@Ubuntu:/sys/class/test/testdev# cat attrs/dev_state
dev_state 0
root@Ubuntu:/sys/class/test/testdev# echo 10 > attrs/dev_state
root@Ubuntu:/sys/class/test/testdev# [ 6793.317838] dev_state store buf: 10
, count 3

root@Ubuntu:/sys/class/test/testdev# cat attrs/dev_state
dev_state 10
root@Ubuntu:/sys/class/test/testdev#

完整代码见链接文件:

https://gitee.com/fishmwei/blog_code/blob/master/linux-kernel/chrdev/chrdev.c

更多

又是一个充实的一天,早上八点起床就在刷系分的在线视频,上次写了个论文不及格,好好构思准备再写一遍。

下午休息,带娃去看个电影,回来后倒头睡一觉。然后开始看sysfs,写个blog。

清明节假期给自己的安排也是满满的,太多知识空白需要填补了,循序渐进吧!

相信有付出就会有收获!

enroll_success


行动,才不会被动!

欢迎关注个人公众号 微信 -> 搜索 -> fishmwei,沟通交流。

欢迎关注

博客地址: https://fishmwei.github.io

掘金主页: https://juejin.cn/user/2084329776486919