【问题标题】:Why doesn't this call to `poll` block correctly on a sysfs device attribute file?为什么这个对 `poll` 的调用不能在 sysfs 设备属性文件上正确阻塞?
【发布时间】:2013-05-02 19:36:16
【问题描述】:

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

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

也许我应该检查POLLPRI以外的返回值——但是在Linux内核中according to the documentationsysfs_poll应该返回POLLERR|POLLPRI

/* ... When the content changes (assuming the
 * manager for the kobject supports notification), poll will
 * return 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 <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!!!\n");
                        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);
}

内核模块 - 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\n", value);
}

module_init(foo_init);
module_exit(foo_exit);

MODULE_LICENSE("GPL");

另见

Using the Linux sysfs_notify call

【问题讨论】:

    标签: linux linux-kernel linux-device-driver blocking polling


    【解决方案1】:

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

    一旦 poll/select 表明值发生了变化,你 需要关闭并重新打开文件,或者寻找到 0 并重新读取。

    但你对fds.fd 无能为力。

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

    【讨论】:

    • 我在fopen 行上方添加了close(fds.fd),并在fclose 调用后再次调用open,但没有任何变化。使用lseek(fds.fd, 0, SEEK_SET) 也没有产生任何结果。
    • 在调用poll之前必须从被轮询的fd中读取当前值。
    • 虚拟读取解决了它。我创建了一个名为dummybufchar 变量,并在open 之后立即插入以下行,在fclose 之后插入:read(fds.fd, &amp;dummybuf, 1);
    • 虽然我确实有后续问题:打开文件时返回POLLPRI 是否正常(因此需要一个虚拟的read)?
    • 对于sysfs 文件,这是正常的。 (POLLIN 表示该文件是可读的,总是如此。)
    猜你喜欢
    • 2011-10-16
    • 1970-01-01
    • 1970-01-01
    • 2014-09-22
    • 2013-05-22
    • 2018-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多