为什么在 sysfs 设备属性文件上对 `poll` 的调用没有正确阻塞? [英] Why doesn't this call to `poll` block correctly on a sysfs device attribute file?

查看:51
本文介绍了为什么在 sysfs 设备属性文件上对 `poll` 的调用没有正确阻塞?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的 sysfs 设备属性,它显示在我的 sysfs 目录下,并在调用 read 时返回一个内核空间变量.我想在这个属性上调用 poll 以允许我的用户空间线程阻塞,直到属性显示的值发生变化.

我的问题是 poll 似乎没有阻止我的属性——它一直返回 POLLPRI,即使属性显示的值没有改变.事实上,我在内核模块中根本没有调用 sysfs_notify,但用户空间调用 poll 仍然没有阻塞.

也许我应该检查 POLLPRI 以外的返回值——但是 根据Linux 内核中的文档sysfs_poll 应该返回POLLERR|POLLPRI:

/* ... 当内容改变时(假设* kobject 的管理器支持通知),poll 将* 返回 POLLERR|POLLPRI ...*/

有什么我忘记用 poll 做的事了吗?


  1. 设备属性位于:/sys/class/vilhelm/foo/blah.

  2. 我加载了一个名为 foo 的内核模块,它注册了一个设备,并创建了一个类和这个设备属性.

  3. 名为 bar 的用户空间应用程序生成一个线程,该线程在设备属性上调用 poll,检查 POLLPRI.

    • 如果poll返回一个正数,POLLPRI已经返回.
    • 使用 fopenfscan 从设备属性文件中读取值.
    • 如果值为42,则打印FROM THREAD!!!.

问题是当我期望对 poll 的调用无限期地阻塞时,消息会不间断地打印.问题一定出在poll(其他调用成功地从设备属性中获取了42的正确值).


用户空间应用 - bar.c:

#include #include #include #include #include static void handle_val(unsigned val, FILE *fp);void * start_val_service(void *arg);int main(void){pthread_t val_serv;pthread_create(&val_serv, NULL, &start_val_service, NULL);pthread_exit(NULL);返回0;}static void handle_val(unsigned val, FILE *fp){开关(值){案例42:{printf("从线程!!!
");休息;}默认:休息;}}void * start_val_service(void *arg){结构pollfd fds;fds.fd = open("/sys/class/vilhelm/foo/blah", O_RDONLY);fds.events = POLLPRI;做{int ret = poll(&fds, 1, -1);如果(ret > 0){文件 *fp = fopen("/sys/class/vilhelm/foo/blah", "r");无符号值;fscanf(fp, %u", &val);handle_val(val, fp);fclose(fp);}}同时(1);关闭(fds.fd);pthread_exit(NULL);}

内核模块 - foo.c:

#include #include #include #include 静态 dev_t foo_dev;静态结构类 *vilhelm;静态无符号 myvar = 42;静态 ssize_t unsigned_dev_attr_show(struct device *dev, struct device_attribute *attr, char *buf);struct unsigned_device_attribute{struct device_attribute dev_attr;无符号 *ptr;};静态结构 unsigned_device_attribute unsigned_dev_attr_blah = {.dev_attr = __ATTR(blah, S_IRUGO, unsigned_dev_attr_show, NULL)};静态 int __init foo_init(void){int retval = 0;printk(KERN_INFO "HELLO FROM MODULE 1");if(alloc_chrdev_region(&foo_dev, 0, 1, "vilhelm") <0){printk(KERN_ERR "foo: 无法注册设备");retval = -1;转到 out_alloc_chrdev_region;}vilhelm = class_create(THIS_MODULE, vilhelm");如果(IS_ERR(vilhelm)){printk(KERN_ERR "foo: 无法创建设备类");retval = PTR_ERR(vilhelm);转到 out_class_create;}struct device *foo_device = device_create(vilhelm, NULL, foo_dev, NULL, foo");如果(IS_ERR(foo_device)){printk(KERN_ERR "foo: 无法创建设备文件");retval = PTR_ERR(foo_device);转到 out_device_create;}unsigned_dev_attr_blah.ptr = &myvar;retval = device_create_file(foo_device, &unsigned_dev_attr_blah.dev_attr);如果(返回){printk(KERN_ERR "foo: 无法创建设备属性文件");转到 out_create_foo_dev_attr_files;}返回0;out_create_foo_dev_attr_files:device_destroy(vilhelm,foo_dev);out_device_create:class_destroy(vilhelm);out_class_create:unregister_chrdev_region(foo_dev, 1);out_alloc_chrdev_region:返回 retval;}静态无效 __exit foo_exit(void){printk(KERN_INFO "BYE FROM MODULE 1");device_destroy(vilhelm,foo_dev);class_destroy(vilhelm);unregister_chrdev_region(foo_dev, 1);}静态 ssize_t unsigned_dev_attr_show(struct device *dev, struct device_attribute *attr, char *buf){struct unsigned_device_attribute *tmp = container_of(attr, struct unsigned_device_attribute, dev_attr);无符号值 = *(tmp->ptr);返回 scnprintf(buf, PAGE_SIZE, "%u
", value);}模块初始化(foo_init);模块退出(foo_exit);MODULE_LICENSE(GPL");


另见

<块引用>

使用 Linux sysfs_notify 调用

解决方案

从您引用的评论中引用更多内容:

<块引用>

一旦 poll/select 指示值已更改,您需要关闭并重新打开文件,或寻求0并重新阅读.

但是你对 fds.fd 什么也没做.

另外,在调用poll()之前做一个虚拟的read();任何新打开的文件都被视为已更改.

I have a simple sysfs device attribute which shows up under my sysfs directory, and on a call to read returns the value of a kernelspace variable. I want to call poll on this attribute to allow my userspace thread to block until the value shown by the attribute changes.

My problem is that poll doesn't seem to block on my attribute -- it keeps returning POLLPRI even though the value shown by the attribute does not change. In fact, I have no calls at all to sysfs_notify in the kernel module, yet the userspace call poll still does not block.

Perhaps I should be checking for a return value of something other than POLLPRI -- but according to the documentation in the Linux kernel, sysfs_poll should return POLLERR|POLLPRI:

/* ... When the content changes (assuming the
 * manager for the kobject supports notification), poll will
 * return POLLERR|POLLPRI ...
 */

Is there something I'm forgetting to do with poll?


  1. The device attribute is located at: /sys/class/vilhelm/foo/blah.

  2. I load a kernel module called foo which registers a device, and creates a class and this device attribute.

  3. The userspace application called bar spawns a thread that calls poll on the device attribute, checking for POLLPRI.

    • If poll returns a positive number, POLLPRI has been returned.
    • Use fopen and fscan to read the value from the device attribute file.
    • If the value is 42, print FROM THREAD!!!.

The problem is that the message is printed nonstop when I'm expecting the call to poll to block indefinitely. The problem must lie with poll (the other calls successfully acquire the correct value of 42 from the device attribute).


userspace app - bar.c:

#include <stdio.h>

#include <fcntl.h>
#include <poll.h>
#include <pthread.h>
#include <unistd.h>

static void handle_val(unsigned val, FILE *fp);
void * start_val_service(void *arg);

int main(void){
        pthread_t val_serv;
        pthread_create(&val_serv, NULL, &start_val_service, NULL);

        pthread_exit(NULL);
        return 0;

}

static void handle_val(unsigned val, FILE *fp){
        switch(val){
                case 42:
                {
                        printf("FROM THREAD!!!
");
                        break;
                }

                default:
                        break;
        }
}


void * start_val_service(void *arg){
        struct pollfd fds;
        fds.fd = open("/sys/class/vilhelm/foo/blah", O_RDONLY);
        fds.events = POLLPRI;

        do{
                int ret = poll(&fds, 1, -1);
                if(ret > 0){
                        FILE *fp = fopen("/sys/class/vilhelm/foo/blah", "r");

                        unsigned val;
                        fscanf(fp, "%u", &val);
                        
                        handle_val(val, fp);

                        fclose(fp);
                }
        }while(1);

        close(fds.fd);

        pthread_exit(NULL);
}

kernel module - foo.c:

#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>

static dev_t foo_dev;
static struct class *vilhelm;

static unsigned myvar = 42;

static ssize_t unsigned_dev_attr_show(struct device *dev, struct device_attribute *attr, char *buf);

struct unsigned_device_attribute{
        struct device_attribute dev_attr;
        unsigned *ptr;
};

static struct unsigned_device_attribute unsigned_dev_attr_blah = {
        .dev_attr = __ATTR(blah, S_IRUGO, unsigned_dev_attr_show, NULL)
};

static int __init foo_init(void){
        int retval = 0;

        printk(KERN_INFO "HELLO FROM MODULE 1");
        
        if(alloc_chrdev_region(&foo_dev, 0, 1, "vilhelm") < 0){
                printk(KERN_ERR "foo: unable to register device");
                retval = -1;
                goto out_alloc_chrdev_region;
        }
        
        vilhelm = class_create(THIS_MODULE, "vilhelm");
        if(IS_ERR(vilhelm)){
                printk(KERN_ERR "foo: unable to create device class");
                retval = PTR_ERR(vilhelm);
                goto out_class_create;
        }
        
        struct device *foo_device = device_create(vilhelm, NULL, foo_dev, NULL, "foo");
        if(IS_ERR(foo_device)){
                printk(KERN_ERR "foo: unable to create device file");
                retval = PTR_ERR(foo_device);
                goto out_device_create;
        }

        unsigned_dev_attr_blah.ptr = &myvar;
        retval = device_create_file(foo_device, &unsigned_dev_attr_blah.dev_attr);
        if(retval){
                printk(KERN_ERR "foo: unable to create device attribute files");
                goto out_create_foo_dev_attr_files;
        }

        return 0;

        out_create_foo_dev_attr_files:
                device_destroy(vilhelm, foo_dev);
        out_device_create:
                class_destroy(vilhelm);
        out_class_create:
                unregister_chrdev_region(foo_dev, 1);
        out_alloc_chrdev_region:
                return retval;
}

static void __exit foo_exit(void){
        printk(KERN_INFO "BYE FROM MODULE 1");

        device_destroy(vilhelm, foo_dev);
        class_destroy(vilhelm);
        unregister_chrdev_region(foo_dev, 1);
}

static ssize_t unsigned_dev_attr_show(struct device *dev, struct device_attribute *attr, char *buf){
        struct unsigned_device_attribute *tmp = container_of(attr, struct unsigned_device_attribute, dev_attr);

        unsigned value = *(tmp->ptr);

        return scnprintf(buf, PAGE_SIZE, "%u
", value);
}

module_init(foo_init);
module_exit(foo_exit);

MODULE_LICENSE("GPL");


See also

Using the Linux sysfs_notify call

解决方案

To quote some more from the comment you quoted:

Once poll/select indicates that the value has changed, you need to close and re-open the file, or seek to 0 and read again.

But you do nothing with fds.fd.

Also, do a dummy read() before calling poll(); any newly opened file is considered changed.

这篇关于为什么在 sysfs 设备属性文件上对 `poll` 的调用没有正确阻塞?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆