【问题标题】:poll() not blocking, returns immediatelypoll() 不阻塞,立即返回
【发布时间】:2016-06-03 17:46:53
【问题描述】:

我正在尝试使用poll() 通过sysfs 捕获GPIO 上的中断。我在第三个位置有-1,所以它可以阻止,但它似乎总是返回。我已经在 SO 上查看了一些类似的帖子。尤其是this (1)this (2)this (3)

在 (1) 中,通过在调用 poll() 之前放置一个虚拟 read() 来解决此问题。如果我这样做(请参阅代码中注释的read())。我的代码在循环中运行一次,第二次在poll() 上永远阻塞。

在 (2) 中,这可能是一种解释,但并不能真正解决我的问题。

在 (3) 中,我的 read() 之前已经有一个 lseek()

当 gpio 的 value 发生变化时,我怎样才能让这个 poll() 阻塞并仅在中断中返回?

这里是sn-p的代码:

int read_gpio(char *path, void (*callback)(int)){
    int fd;
    char buf[11];
    int res = 0;
    char c;
    int off;
    struct pollfd gpio_poll_fd = {
        .fd = fd,
        .events = POLLPRI,
        .revents = 0
    };
    for(;;){

        gpio_poll_fd.fd = open(path, O_RDONLY);
        if(fd == -1){
            perror("error opening file");
            return -1;
        }
//        char c;
//        read(fd,&c,1);
        LOGD("for begins");
        res = poll(&gpio_poll_fd,1,-1);
        LOGD("polling ended");
        if(res == -1){
            perror("error polling");
            break;
        }

        if((gpio_poll_fd.revents & POLLPRI)  == POLLPRI){
            LOGD("POLLPRI");
            off = lseek(fd, 0, SEEK_SET);
            if(off == -1) break;
            memset(&buf[0], 0, 11);
            size_t num = read(fd, &buf[0], 10*sizeof(char));
            LOGD("Before callback");
            callback(atoi(buf));
            LOGD("After Callback");
        }
        if((gpio_poll_fd.revents & POLLERR) == POLLERR) {
            //seems always to be true ..
            //LOGD("POLLERR");
        }
        close(fd);
        LOGD("for ends");

    }
    LOGD("for exits");

    return 0;
}

注意:当我在 Android JNI 上执行此操作时,我一直在从 LOGD() 获取调试信息

更新: 按照 jxh 评论中的建议,我已经像这样安排了结构,尽管现在它无限期地阻塞在 poll() 上。当 value 的内容从外部施加的电压改变时,POLLPRI 不会变高,并且 poll() 不会返回:

int read_gpio(char *path, void (*callback)(int)){
    int fd = open(path, O_RDONLY);
    if(fd == -1){
        perror("error opening file");
        return -1;
    }
    char buf[11];
    int res, off;
    char c;
    struct pollfd pfd = {
            .fd = fd,
            .events = POLLPRI,
            .revents = 0
    };
    for(;;){
        LOGD("for begins");
//        dummy read causes poll never to run
//        lseek() alone here cause poll never to run
//        read(fd, &buf[],1);
//        lseek(fd, 0, SEEK_SET);
        res = poll(&pfd,1,-1);
        LOGD("polling ended");
        if(res == -1){
            perror("error polling");
            break;
        }

        if((pfd.revents & POLLPRI)  == POLLPRI){
            LOGD("POLLPRI");
            off = lseek(fd, 0, SEEK_SET);
            if(off == -1) break;
            memset(&buf[0], 0, 11);
            read(fd, &buf[0], 10*sizeof(char));
//            These two lines will cause it to poll constantly
//            close(fd);
//            fd = open(path, O_RDONLY);
            LOGD("Before callback");
            callback(atoi(buf));
            LOGD("After Callback");
        }
        LOGD("for ends");
    }
    close(fd);
    LOGD("for exits");

    return 0;
}

【问题讨论】:

  • 您的open() 确定成功了吗?您似乎打算测试的代码正在查看错误的变量。
  • @JohnBollinger 当然,我得到了正确的价值。感谢您对测试的提醒。
  • 不清楚为什么您认为参考文献 (1) 中给出的答案可以忽略。它似乎解释了您报告的案例的行为,您在打开文件和轮询文件之间没有读取文件。也许您真的想问为什么poll() 在初始读取后执行时会无限期阻塞?
  • @JohnBollinger 我没有忽略它,而是尝试在这里使用他们的解决方案。我尝试实施对他们有用的解决方案(请参阅注释掉的 read() 以了解我放置虚拟读取的位置)。因此,虽然 (1) 解释了这种行为,但该解决方案在我的实现中并没有奏效。也许我做错了什么,但我不确定是什么。
  • 既然代码在你使用dummy read的时候可以工作一次,你有没有考虑过那个版本的代码是ok的?您确定文件在第一次通过后发生更改而不会导致poll() 返回吗?

标签: c linux blocking polling sysfs


【解决方案1】:

在您的代码中,fd 未初始化。

当您打开文件时,您直接分配给gpio_poll_fd.fd,而不使用fd,因此fd 保持未初始化状态。

试试:

gpio_poll_fd.fd = fd = open(path, O_RDONLY);

正如cmets中所指出的,根据GPIO manual(我在仔细阅读这些cmets之后才阅读),GPIOsysfs接口有点特殊:

如果引脚可以配置为产生中断的中断 如果它已被配置为产生中断(见 “边缘”的描述),您可以在该文件上poll(2) 和 只要触发中断,poll(2) 就会返回。如果 您使用poll(2),设置事件POLLPRIPOLLERR。如果你 使用select(2),在exceptfds中设置文件描述符。后 poll(2)lseek(2) 返回到 sysfs 的开头 文件并读取新值或关闭文件并重新打开它 读取值。

因此,虽然它不是典型的poll() 成语,但您的关闭和重新打开构造是正确的。但是,我会选择让文件描述符保持打开状态。所以,这就是我将如何构建您的代码:

int read_gpio(char *path, void (*callback)(int)){
    char buf[11];
    int fd, res, off;
    struct pollfd pfd;
    if((pfd.fd = fd = open(path, O_RDONLY)) == -1){
        perror("path");
        return -1;
    }
    LOGD("First read");
    res = read(fd, buf, 10);
    assert(res == 10);
    LOGD("Before callback");
    callback(atoi(buf));
    LOGD("After Callback");
    pfd.events = POLLPRI|POLLERR;  // poll(2) says setting POLLERR is
                                   // unnecessary, but GPIO may be
                                   // special.
    for(;;){
        LOGD("for begins");
        if((res = poll(&pfd,1,-1)) == -1){
            perror("poll");
            break;
        }
        LOGD("polling ended");
        if((pfd.revents & POLLPRI) == POLLPRI){
            LOGD("POLLPRI");
            off = lseek(fd, 0, SEEK_SET);
            if(off == -1) break;
            memset(buf, 0, 11);
            res = read(fd, buf, 10);
            assert(res == 10);
            LOGD("Before callback");
            callback(atoi(buf));
            LOGD("After Callback");
        } else {
            // POLLERR, POLLHUP, or POLLNVAL
            break;
        }
        LOGD("for ends");
    }
    close(fd);
    LOGD("for exits");

    return 0;
}

【讨论】:

  • 我在 for(;;) 中这样做了。应该在外面做吗?
  • 因为您在每次迭代中都 close(fd),在 for 内。
  • 是的,这完全解释了问题。 sysfs FD 是被轮询的,但几乎在其他任何地方都是未初始化的 fd 被操纵。
  • 我将gpio_poll_fd.fd = open(path, O_RDONLY); 替换为gpio_poll_fd.fd = fd = open(path, O_RDONLY); 作为for 的第一行,它的行为与以前完全相同。这是我应该放的地方吗?
  • 该展示位置应该为您提供一个初始化的fd,以便在lseekread 中使用。
猜你喜欢
  • 2015-04-30
  • 2011-01-26
  • 1970-01-01
  • 2017-01-03
  • 2016-10-07
  • 2021-09-06
  • 2011-02-21
  • 1970-01-01
  • 2023-03-04
相关资源
最近更新 更多