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

Linux驱动中msleep延时不准问题求助:实际时长与预期不符

问题:Linux驱动中msleep延时不符合预期的排查

我在Linux驱动代码中想要实现8ms的延时,使用了msleep函数,但发现仅循环了两次,dmesg中两次打印的时间差实际为10ms,而非预期的2ms,请问问题出在哪里?

dmesg日志片段

[  386.199343] this is ioctl
[  386.199359] ioctl value = 0
[  386.210085] ioctl value = 1

驱动中ioctl函数代码

static long spiio_drv_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
        int i = 0;
        int value = 0;
        int *param = (int *)arg;
        printk("this is ioctl\n");

        switch (cmd)
        {
                case 0: /*no sleep*/
                        *param = 0;
                case 1: /*SPIIO_IOCTL_CMD*/
                        do {
                                value = gpio_get_value(spiio_gpio);
                                printk("ioctl value = %d\n", value);
                                if (value == 1) {
                                        return 1;
                                } else {
                                        if (*param) {
                                                msleep(1);
                                        }
                                        i ++;
                                }

                        } while(i < *param);
                        break;
                default:
                        printk("spiio ioctrl cmd %d is error", cmd);
                        return -1;

        }
        return 0;
}

问题原因及解决办法

核心问题1:switch分支缺少break导致逻辑串流

case 0分支执行完*param = 0;后没有添加break;,会直接落入case 1的循环逻辑。如果调用ioctl时传入cmd=0,会意外修改*param的值,破坏原本的循环次数和延时逻辑,可能导致实际执行的延时次数和预期不符。

核心问题2:msleep的精度受系统HZ限制

Linux内核的msleep依赖系统时钟tick(时钟中断频率):

  • 如果系统默认HZ=100,每个tick间隔是10ms,此时msleep(1)会被向上对齐到10ms,因为内核无法提供比tick间隔更细的时间粒度,这就是日志中两次打印间隔10ms的直接原因。
  • 只有当HZ=1000时,tick间隔为1ms,msleep(1)才能接近预期延时,但修改HZ需要重新编译内核,且会增加系统开销,不推荐。

核心问题3:循环提前终止

日志中仅循环两次就停止,是因为第二次检测到gpio_get_value返回1,触发了return 1直接退出函数,没有完成预期的8次循环。这说明GPIO状态在第一次延时后就发生了变化,导致循环提前结束。

对应解决办法

  1. 修复switch分支逻辑:在case 0的代码块末尾添加break;,避免逻辑串入case 1:
    case 0: /*no sleep*/
        *param = 0;
        break; // 新增break
    case 1: /*SPIIO_IOCTL_CMD*/
    
  2. 替换msleep为高精度延时函数:使用usleep_range(min, max)替代msleep,它不依赖系统tick,能提供更精确的短延时。比如需要1ms延时,可写为:
    usleep_range(900, 1100); // 延时900~1100微秒,接近1ms
    
  3. 调整循环终止逻辑:如果需要确保完成8次延时再返回,可修改return逻辑,比如先记录GPIO状态,等循环结束后再返回结果,而不是中途退出:
    int result = 0;
    do {
        value = gpio_get_value(spiio_gpio);
        printk("ioctl value = %d\n", value);
        if (value == 1) {
            result = 1; // 记录状态,不直接返回
        } else {
            if (*param) {
                usleep_range(900, 1100);
            }
            i ++;
        }
    } while(i < *param);
    return result;
    

内容的提问来源于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 13:17:02