You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux v3.10 GPIO驱动poll延迟未生效及中断回调mutex合理性咨询

问题解答

一、驱动poll超时未生效的原因及修复

核心原因分析

从代码和dmesg日志来看,poll超时未生效主要由以下几点导致:

  1. poll函数逻辑违反设计原则
    驱动的spiio_drv_poll函数中,检测到IrqOccurred为1时直接将其置0,这是错误的。poll的核心作用是查询事件是否就绪,而非消费事件。这种操作会导致:用户态poll返回后未调用read时,事件标志已被清零,后续中断触发又会立即置位,poll反复被唤醒,无法进入超时等待。
  2. 用户态测试代码未消费事件
    测试代码中,poll检测到POLLIN事件后仅执行SPI传输,从未调用驱动的read函数。事件未被真正消费,加上poll函数提前清零标志,导致整个事件处理逻辑混乱。
  3. 中断频繁触发
    从dmesg的时间戳可以看到,短时间内多次触发GPIO中断,poll被频繁唤醒,自然无法进入8ms的超时等待状态。

修复方案

  1. 修正驱动poll函数逻辑
    移除poll函数中对IrqOccurred的修改,仅保留状态查询:
    static unsigned int spiio_drv_poll(struct file *file, struct poll_table_struct *wait)
    {
        printk("this is poll.\n");
        poll_wait(file, &WaitQueue, wait);
        if (IrqOccurred) {
            printk(" an irq occurred. return %d\n", POLLIN);
            return POLLIN;
        }
        printk("no irq occurred, IrqOccurred = %d\n", IrqOccurred);
        return 0;
    }
    
  2. 在read函数中消费事件
    将IrqOccurred的清零操作移到read函数中,确保事件只有在被真正读取后才被标记为已处理:
    static ssize_t spiio_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
    {
        char userValue = gpio_get_value(SpiioGpio) ? 1 : 0;
        printk("read value is %d\n", userValue);
        
        // 消费事件,清零标志
        mutex_lock(&RWLock);
        IrqOccurred = 0;
        mutex_unlock(&RWLock);
        
        if (copy_to_user(buf, &userValue, sizeof(char)) != 0) {
            printk("failed to copy to user\n");
            return  -1;
        } else {
            return sizeof(char);
        }
    }
    
  3. 修改测试代码,触发read操作
    在poll返回POLLIN事件后,调用read函数读取GPIO值,完成事件消费:
    while (1) {
        memset(buffer, 0, sizeof(buffer));
        ret = poll(&pfd, 1, timeout_ms);
        revents = pfd.revents;
        if (ret > 0) {
            // 读取GPIO值,消费事件
            ret2 = read(fd, buffer, sizeof(buffer));
            if (ret2 > 0) {
                TransferSpi(SpiDevFd, &spi_settings, tx, sizeof(tx), rx);
                hex_dump(rx, ARRAY_SIZE(rx), 32, "RX");
            }
        } else {
            printf("ret = %d\n", ret);
            exit(-1);
        }
    }
    

二、中断回调函数中使用mutex是否合适?

完全不合适,原因如下:

  • mutex是可睡眠锁:当mutex被其他线程持有时,mutex_lock会触发进程调度进入睡眠状态,但中断上下文(中断顶半部)不允许睡眠,强制调用会导致系统崩溃。
  • 替代方案:
    1. 自旋锁(spinlock):适合中断上下文和进程上下文共享资源的场景,获取不到锁时会循环等待(自旋),不会触发睡眠:
      #include <linux/spinlock.h>
      static spinlock_t RWLock;
      
      // 初始化
      spin_lock_init(&RWLock);
      
      // 中断中使用
      spin_lock(&RWLock);
      IrqOccurred = 1;
      spin_unlock(&RWLock);
      
      // 进程上下文使用
      spin_lock(&RWLock);
      IrqOccurred = 0;
      spin_unlock(&RWLock);
      
    2. 原子操作:如果仅操作IrqOccurred这类整型变量,用原子操作更高效,无需锁:
      #include <linux/atomic.h>
      static atomic_t IrqOccurred = ATOMIC_INIT(0);
      
      // 中断中设置
      atomic_set(&IrqOccurred, 1);
      
      // poll中查询
      if (atomic_read(&IrqOccurred)) {
          return POLLIN;
      }
      
      // read中清零
      atomic_set(&IrqOccurred, 0);
      

内容的提问来源于stack exchange,提问作者Vimer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 05:45:54