Linux内核下不同设备同名sysfs节点修改值互相影响如何修复?
问题根因与解决方案
根因分析
你遇到的同步修改问题核心原因是:两个设备的sysfs属性回调共用了全局静态缓冲区mybuf。
两个设备的mydevice_show/mydevice_store回调读写的都是同一块全局内存,所以不管操作哪一个设备的sysfs节点,修改的都是同一个值,自然会同步变化,和设备节点名称、所属class没有关系。
修复步骤
核心思路是给每个设备绑定独立的私有数据,回调中读写当前设备专属的存储区域:
- 自定义设备私有数据结构体,把每个设备独有的缓冲区、属性变量都放在结构体中
- 创建设备实例后,通过
dev_set_drvdata()接口将私有数据指针和当前struct device绑定 - 在sysfs的show/store回调中,通过入参
struct device *dev调用dev_get_drvdata(),拿到当前设备对应的私有数据指针,再读写专属缓冲区
修正后的示例代码
#include <linux/module.h> #include <linux/init.h> #include <linux/sysfs.h> #include <linux/string.h> #include <linux/device.h> #include <linux/fs.h> #include <linux/slab.h> /* 自定义设备私有数据结构体,每个设备独有一份 */ struct my_dev_priv { char mybuf[100]; }; static int major, major1; static struct class *myclass; static struct device *mydevice, *mydevice1; /* 私有数据指针,每个设备对应一个 */ static struct my_dev_priv *priv0, *priv1; static ssize_t mydevice_show(struct device *dev, struct device_attribute *attr, char *buf) { /* 拿到当前设备对应的私有数据 */ struct my_dev_priv *priv = dev_get_drvdata(dev); return sprintf(buf, "%s\n", priv->mybuf); } static ssize_t mydevice_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct my_dev_priv *priv = dev_get_drvdata(dev); /* 只修改当前设备的私有缓冲区 */ snprintf(priv->mybuf, sizeof(priv->mybuf), "%s", buf); return count; } static DEVICE_ATTR(mydevice, 0644, mydevice_show, mydevice_store); static struct file_operations myfops = { .owner = THIS_MODULE, }; static int __init mydevice_init(void) { int ret; major = register_chrdev(0, "mydevice", &myfops); if (major < 0) { ret = major; return ret; } major1 = register_chrdev(0, "mydevice1", &myfops); if (major1 < 0) { ret = major1; goto unreg_major0; } myclass = class_create(THIS_MODULE, "myclass"); if (IS_ERR(myclass)) { ret = PTR_ERR(myclass); goto unreg_major1; } /* 分配第一个设备的私有数据并初始化 */ priv0 = kzalloc(sizeof(*priv0), GFP_KERNEL); if (!priv0) { ret = -ENOMEM; goto destroy_class; } strscpy(priv0->mybuf, "2", sizeof(priv0->mybuf)); mydevice = device_create(myclass, NULL, MKDEV(major, 0), NULL, "mydevice"); if (IS_ERR(mydevice)) { ret = PTR_ERR(mydevice); goto free_priv0; } /* 绑定私有数据到第一个设备 */ dev_set_drvdata(mydevice, priv0); /* 分配第二个设备的私有数据并初始化 */ priv1 = kzalloc(sizeof(*priv1), GFP_KERNEL); if (!priv1) { ret = -ENOMEM; goto destroy_dev0; } strscpy(priv1->mybuf, "2", sizeof(priv1->mybuf)); mydevice1 = device_create(myclass, NULL, MKDEV(major1, 0), NULL, "mydevice1"); if (IS_ERR(mydevice1)) { ret = PTR_ERR(mydevice1); goto free_priv1; } /* 绑定私有数据到第二个设备 */ dev_set_drvdata(mydevice1, priv1); ret = sysfs_create_file(&mydevice->kobj, &dev_attr_mydevice.attr); if (ret) goto destroy_dev1; ret = sysfs_create_file(&mydevice1->kobj, &dev_attr_mydevice.attr); if (ret) goto remove_file0; return 0; remove_file0: sysfs_remove_file(&mydevice->kobj, &dev_attr_mydevice.attr); destroy_dev1: device_destroy(myclass, MKDEV(major1, 0)); free_priv1: kfree(priv1); destroy_dev0: device_destroy(myclass, MKDEV(major, 0)); free_priv0: kfree(priv0); destroy_class: class_destroy(myclass); unreg_major1: unregister_chrdev(major1, "mydevice1"); unreg_major0: unregister_chrdev(major, "mydevice"); return ret; } static void __exit mydevice_exit(void) { sysfs_remove_file(&mydevice->kobj, &dev_attr_mydevice.attr); sysfs_remove_file(&mydevice1->kobj, &dev_attr_mydevice.attr); device_destroy(myclass, MKDEV(major, 0)); device_destroy(myclass, MKDEV(major1, 0)); kfree(priv0); kfree(priv1); class_destroy(myclass); unregister_chrdev(major, "mydevice"); unregister_chrdev(major1, "mydevice1"); } module_init(mydevice_init); module_exit(mydevice_exit); MODULE_DESCRIPTION("Fixed driver for per-device sysfs attribute"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("HLY");
额外优化建议
如果两个设备属于同类型,不需要两次调用register_chrdev注册两个主设备号,只需要注册一个主设备号,用不同次设备号区分不同设备即可,可减少不必要的资源占用。
内容的提问来源于stack exchange,提问作者Michael Chen
相关产品推荐
相关产品推荐

