epoll_wait未触发的小Bug

上次看了一下epoll监听的原理,在Android Jni里使用epoll,来监听Gpio口的变化事件,具体代码如下:

  1. 打开 GPIO 文件描述符,因为该文件是内核虚拟出来的,不是实际文件,所以无法使用FileObserver来监听,应该是不会触发Inotify
cpp 复制代码
int *fds = malloc(num_gpios * sizeof(int));
    LOGD("Allocating memory for %d GPIOs", num_gpios);
    for (int i = 0; i < num_gpios; i++) {
        jstring gpioPin = (jstring) (*env)->GetObjectArrayElement(env, gpioPins, i);
        const char *pin = (*env)->GetStringUTFChars(env, gpioPin, NULL);
        char gpio_value_path[50];
        snprintf(gpio_value_path, sizeof(gpio_value_path), "/sys/class/gpio/gpio%s/value", pin);
        LOGD("Opening GPIO path: %s", gpio_value_path);
        fds[i] = open(gpio_value_path, O_RDONLY);
        if (fds[i] < 0) {
            LOGD("Failed to open GPIO %s", pin);
            (*env)->ReleaseStringUTFChars(env, gpioPin, pin);
            free(fds);
            return;
        }
  1. 创建 epoll 实例,epoll_create1这个函数看着强迫症都犯了。。。
cpp 复制代码
    int epfd = epoll_create1(0);
    if (epfd < 0) {
        LOGD("Failed to create epoll instance");
        free(fds);
        return;
    }

    struct epoll_event ev;
    for (int i = 0; i < num_gpios; i++) {
        ev.events = EPOLLPRI | EPOLLERR;  // 等待优先事件和错误事件
        ev.data.fd = fds[i];
        if (epoll_ctl(epfd, EPOLL_CTL_ADD, fds[i], &ev) < 0) {
            LOGD("Failed to add GPIO %d to epoll", i);
            free(fds);
            return;
        }
    }
  1. 阻塞监听 GPIO 变化,默认会触发一次,可先执行一次 lseek + read 吸掉脏数据。
    这是 epoll 的通用惯例:在 add 到 epoll 前就要清空一次触发状态,否则可能误触发一次后就永远不再触发了。
cpp 复制代码
    while (1) {
        struct epoll_event events[num_gpios];
        LOGD("epoll_wai...");
        int n = epoll_wait(epfd, events, num_gpios, -1);  // 阻塞直到事件发生
        if (n > 0) {
            for (int i = 0; i < n; i++) {
                if (events[i].events & EPOLLPRI) {
                    char gpio_value;
                    lseek(events[i].data.fd, 0, SEEK_SET);
                    read(events[i].data.fd, &gpio_value, 1);
                    LOGD("GPIO fd %d changed! New value: %c", events[i].data.fd, gpio_value);

                }
            }

4.监听是否成功触发,和这些几个点相关:

  • /sys/class/gpio/gpioX/edge 的设置(none、rising、falling、both)
  • 内核中对应 gpio_irq_handler
  • 中断触发机制(边沿触发的行为)
  • 用户态 epoll 等待的数据准备逻辑(f_op->poll())

5. 写入 /sys/class/gpio/gpioX/edge,本质上调用了如下路径

bash 复制代码
gpio_sysfs_set_edge()
 -> gpio_setup_irq()
   -> irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING / IRQ_TYPE_EDGE_FALLING / BOTH)

实际代码如下:

  1. 中断触发 → 唤醒等待的进程
bash 复制代码
static irqreturn_t gpio_irq_handler(int irq, void *dev_id) {
    struct gpio_desc *desc = dev_id;
    ...
    // 设置状态标志
    desc->irq_data.triggered = true;
    // 唤醒 epoll/poll 的 waitqueue
    wake_up_interruptible(&desc->wait);
    return IRQ_HANDLED;
}
相关推荐
woxihuan1234561 小时前
SQL删除数据时存在依赖关系_设置外键级联删除ON DELETE
jvm·数据库·python
东风破1371 小时前
DM8达梦共享存储集群DSC搭建步骤
数据库·学习·dm达梦数据库
雪碧聊技术2 小时前
当数据库字段数大于Java实体类属性数时,MyBatis还能映射成功吗?一文详解
数据库·自动映射·mybatis映射机制·java实体类·宽容映射机制
Jetev2 小时前
如何确定SQL字段是否为空_使用IS NULL与IS NOT NULL
jvm·数据库·python
m0_702036532 小时前
mysql如何处理不走索引的OR查询_使用UNION ALL优化重写
jvm·数据库·python
代钦塔拉3 小时前
Qt4 vs Qt5 带参数信号槽的连接方式详解
开发语言·数据库·qt
2401_846339563 小时前
MySQL在云环境如何选择存储类型_SSD与高性能云盘配置建议
jvm·数据库·python
zhaoyong2224 小时前
SQL如何统计每个用户的首次行为时间_MIN聚合与分组
jvm·数据库·python
2501_901006474 小时前
C#怎么实现配置热更新 C#如何在运行时动态刷新配置文件不需要重启程序【技巧】
jvm·数据库·python
m0_470857644 小时前
HTML怎么创建响应式图片备选方案_HTML srcset与sizes结构【详解】
jvm·数据库·python