linux sysfs使用
概述
Linux 2.6以后的内核引入了sysfs文件系统, sysfs被看成是与proc、 devfs和devpty同类别的文件系统,该文件系统是一个虚拟的文件系统, 它可以产生一个包括所有系统硬件的层级视图, 与提供进程和状态信
息的proc文件系统十分类似。
sysfs把连接在系统上的设备和总线组织成为一个分级的文件, 它们可以由用户空间存取, 向用户空间导出内核数据结构以及它们的属性。 sysfs的一个目的就是展示设备驱动模型中各组件的层次关系, 其顶级目录包括block、 bus、 dev、 devices、 class、 fs、 kernel、 power和firmware等。
(出自《Linux设备驱动开发详解:基于最新的Linux4.0内核》)
目录结构
/sys下子目录 | 目录内容 |
---|---|
/sys/devices | 内核对系统中所有设备的分层次表达模型,也是/sys文件系统管理设备的最重要的目录结构; |
/sys/dev | 分为字符设备和块设备,都是以主次设备号的形式链接到/sys/devices的真实设备 |
/sys/bus | 按照总线类型将/sys/devices下的真实设备进行分类 |
/sys/class | 按照功能分类将/sys/devices下的真实设备进行分类 |
/sys/block | 系统中所有的块设备 |
/sys/firmware | 路径中是系统使用的devicetree的各个描述符 |
/sys/module | 模块中引出来的参数在这个路径下 |
/sys/power | 系统中的电源选项,写入控制指令可以suspend/reboot等 |
创建sysfs节点
device_attribute
创建读写字符串的节点:
#函数原型
int device_create_file(struct device *device, const struct device_attribute *entry);
这里涉及一个结构体:
device_attribute
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);
};
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
#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)
以一个可以进行字符串读写的AA节点为例:
在代码中需要如下定义:
DEVICE_ATTR_RW(AA)
#这样就已经定义好了名为 dev_attr_AA 的结构体对象
#同时也定义好了两个方法名称:AA_show()和AA_store()
在probe函数中调用创建函数:
device_create_file(&client->dev, &dev_attr_AA);
然后实现AA_show()和AA_store()实体
static ssize_t vmute_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
unsigned int value;
......
......
return scnprintf(buf, PAGE_SIZE, "%d\n", value);
}
static ssize_t vmute_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
......
......
return count;
}
有几点注意项:
1.show()方法应该总是使用snprintf 进行返回
2.store()方法应该返回实际使用的字节数
3.show()和store()方法出错时返回相应的错误码
4.show()和store()方法都是同步通信
5.节点的缓冲区总是为1页,为PAGE_SIZE个字节,PAGE_SIZE=4096
bin_attribute
创建读写bin类型的节点:
#函数原型
int __must_check device_create_bin_file(struct device *dev, const struct bin_attribute *attr);
#因为是__must_check,所以该函数的返回值必须进行判定
这里涉及一个结构体:
bin_attribute
struct bin_attribute {
struct attribute attr;
size_t size;
void *private;
ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *, char *, loff_t, size_t);
ssize_t (*write)(struct file *, struct kobject *, struct bin_attribute *, char *, loff_t, size_t);
int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr, struct vm_area_struct *vma);
};
#define BIN_ATTR(_name, _mode, _read, _write, _size) \
struct bin_attribute bin_attr_##_name = __BIN_ATTR(_name, _mode, _read, \
_write, _size)
#define BIN_ATTR_RO(_name, _size) \
struct bin_attribute bin_attr_##_name = __BIN_ATTR_RO(_name, _size)
#define BIN_ATTR_WO(_name, _size) \
struct bin_attribute bin_attr_##_name = __BIN_ATTR_WO(_name, _size)
#define BIN_ATTR_RW(_name, _size) \
struct bin_attribute bin_attr_##_name = __BIN_ATTR_RW(_name, _size)
以一个可以进行二进制读写的BB节点为例:
在代码中需要如下定义:
BIN_ATTR_RW(BB, 0x400)
#这样就已经定义好了名为 bin_attr_BB 的结构体对象
#同时也定义好了两个方法名称:BB_read()和BB_write()
在probe函数中调用创建函数:
err = device_create_bin_file(&client->dev, &bin_attr_BB);
if (err)
......
然后实现BB_read()和BB_write()实体
static ssize_t BB_read(struct file *filp,
struct kobject *kobj, struct bin_attribute *attr,
char *buf, loff_t offset, size_t count)
{
......
......
return count;
}
static ssize_t BB_write(struct file *filp,
struct kobject *kobj, struct bin_attribute *attr,
char *buf, loff_t offset, size_t count)
{
......
......
return count;
}
sysfs_notify
如果内核想主动上报状态,可以使用sysfs_notify方法
#函数原型
void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr);
直接唤醒该字符串
sysfs_notify(&dev->client->dev.kobj, NULL, "AA");
/sys/module中的parameter
可以用如下方式向模块中传入参数,例:
static bool xx;
#将模块外传进来的参数yy映射到代码中的xx变量,代码中使用xx进行操作
module_param_named(yy, xx, bool, 0644);
#这句是对yy参数的描述,在#:modinfo mm.ko时,会打印出来,用来描述参数的作用等
MODULE_PARM_DESC(yy, "Force Off");
用户空间使用sysfs节点
比如:
#第一种
cat AA #会调用 AA_show() 方法
echo "xxx" > AA #会调用 AA_store()方法
#第二种
int fd = open("BB", O_RDWR);
# offset = 1, size = 1
ret = pread(fd, readbuf, 1, 1); #会调用 BB_read()方法,ret返回读取到的个数,readbuf中是读取到的值
# offset = 1, size = 1
ret = pwrite(fd, writebuf, 1, 1); #会调用 BB_write()方法,ret返回写入的个数,writebuf中是待写入的值
#第三种
while(1) {
err = epoll_wait(epfd, pEvent, 1, -1); #sysfs_notify会唤醒epoll
if(err) {
ret = read(fd, read_value, 1); #会调用 AA_show()
write(fd, writebuf, sizeof(writebuf)); #会调用 AA_store()
}
}
sysfs节点创建原理
总结
以上只是我在项目中用到的基本内容,还有很多内容有待研究。
如果以上内容有哪些错误,请毫不犹豫的指正。