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状态在第一次延时后就发生了变化,导致循环提前结束。
对应解决办法
- 修复switch分支逻辑:在
case 0的代码块末尾添加break;,避免逻辑串入case 1:case 0: /*no sleep*/ *param = 0; break; // 新增break case 1: /*SPIIO_IOCTL_CMD*/ - 替换msleep为高精度延时函数:使用
usleep_range(min, max)替代msleep,它不依赖系统tick,能提供更精确的短延时。比如需要1ms延时,可写为:usleep_range(900, 1100); // 延时900~1100微秒,接近1ms - 调整循环终止逻辑:如果需要确保完成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
相关产品推荐
相关产品推荐

