epoll_wait未触发的小Bug

一给  论坛元老 | 2025-5-25 18:30:33 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 2057|帖子 2057|积分 6171

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

  • 打开 GPIO 文件描述符,由于该文件是内核虚拟出来的,不是现实文件,所以无法使用FileObserver来监听,应该是不会触发Inotify
  1. int *fds = malloc(num_gpios * sizeof(int));
  2.     LOGD("Allocating memory for %d GPIOs", num_gpios);
  3.     for (int i = 0; i < num_gpios; i++) {
  4.         jstring gpioPin = (jstring) (*env)->GetObjectArrayElement(env, gpioPins, i);
  5.         const char *pin = (*env)->GetStringUTFChars(env, gpioPin, NULL);
  6.         char gpio_value_path[50];
  7.         snprintf(gpio_value_path, sizeof(gpio_value_path), "/sys/class/gpio/gpio%s/value", pin);
  8.         LOGD("Opening GPIO path: %s", gpio_value_path);
  9.         fds[i] = open(gpio_value_path, O_RDONLY);
  10.         if (fds[i] < 0) {
  11.             LOGD("Failed to open GPIO %s", pin);
  12.             (*env)->ReleaseStringUTFChars(env, gpioPin, pin);
  13.             free(fds);
  14.             return;
  15.         }
复制代码

  • 创建 epoll 实例,epoll_create1这个函数看着强迫症都犯了。。。
  1.     int epfd = epoll_create1(0);
  2.     if (epfd < 0) {
  3.         LOGD("Failed to create epoll instance");
  4.         free(fds);
  5.         return;
  6.     }
  7.     struct epoll_event ev;
  8.     for (int i = 0; i < num_gpios; i++) {
  9.         ev.events = EPOLLPRI | EPOLLERR;  // 等待优先事件和错误事件
  10.         ev.data.fd = fds[i];
  11.         if (epoll_ctl(epfd, EPOLL_CTL_ADD, fds[i], &ev) < 0) {
  12.             LOGD("Failed to add GPIO %d to epoll", i);
  13.             free(fds);
  14.             return;
  15.         }
  16.     }
复制代码

  • 壅闭监听 GPIO 变化,默认会触发一次,可先执行一次 lseek + read 吸掉脏数据。
    这是 epoll 的通用惯例:在 add 到 epoll 前就要清空一次触发状态,否则可能误触发一次后就永远不再触发了。
  1.     while (1) {
  2.         struct epoll_event events[num_gpios];
  3.         LOGD("epoll_wai...");
  4.         int n = epoll_wait(epfd, events, num_gpios, -1);  // 阻塞直到事件发生
  5.         if (n > 0) {
  6.             for (int i = 0; i < n; i++) {
  7.                 if (events[i].events & EPOLLPRI) {
  8.                     char gpio_value;
  9.                     lseek(events[i].data.fd, 0, SEEK_SET);
  10.                     read(events[i].data.fd, &gpio_value, 1);
  11.                     LOGD("GPIO fd %d changed! New value: %c", events[i].data.fd, gpio_value);
  12.                 }
  13.             }
复制代码
4.监听是否乐成触发,和这些几个点相干:


  • /sys/class/gpio/gpioX/edge 的设置(none、rising、falling、both)
  • 内核中对应 gpio_irq_handler
  • 中断触发机制(边沿触发的行为)
  • 用户态 epoll 等候的数据准备逻辑(f_op->poll())
5. 写入 /sys/class/gpio/gpioX/edge,本质上调用了如下路径
  1. gpio_sysfs_set_edge()
  2. -> gpio_setup_irq()
  3.    -> irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING / IRQ_TYPE_EDGE_FALLING / BOTH)
复制代码
现实代码如下:


  • 中断触发 → 唤醒等候的进程
  1. static irqreturn_t gpio_irq_handler(int irq, void *dev_id) {
  2.     struct gpio_desc *desc = dev_id;
  3.     ...
  4.     // 设置状态标志
  5.     desc->irq_data.triggered = true;
  6.     // 唤醒 epoll/poll 的 waitqueue
  7.     wake_up_interruptible(&desc->wait);
  8.     return IRQ_HANDLED;
  9. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

一给

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表