Linux中同步写入操作导致进程进入“disk sleep”状态的原因探究
嘿,这个问题我之前折腾U盘镜像的时候也碰到过,当时用dd写完ISO后,终端卡着不动,进程挂在D状态(disk sleep)好几秒,连kill -9都搞不定,那叫一个郁闷。咱们来拆解一下背后的原因:
首先得明白啥是“disk sleep”状态:这个状态标记为
D,全称是Uninterruptible Sleep(不可中断睡眠)。进程进入这个状态,是因为它正在等待硬件IO操作完成,而且内核不允许任何信号(包括威力最大的SIGKILL)打断它——毕竟要是中途强行终止进程,可能会导致数据写入不完整,甚至损坏存储设备的文件系统。所以这时候你急也没用,只能等IO操作彻底结束。同步写入的“等待链”:当你用
dd做同步写入(比如加了oflag=sync参数,或者某些场景下内核自动触发同步),进程不会写完数据就立刻返回。它会先把数据写到内核的页缓存,然后触发fsync()或者fdatasync()调用,要求内核把所有“脏页”(还没写到硬件的缓存数据)刷到存储设备里,并且必须等存储设备返回“写入完成”的确认信号,进程才会退出。这个等待过程就是你看到的“卡壳”。USB存储设备的“小套路”:这锅很大程度上得甩给U盘这类USB存储设备。很多U盘自带了小型写缓存,当系统发送写请求时,U盘会立刻告诉内核“我写完啦”,但实际上数据还在它自己的缓存里,没真正写到闪存芯片里。当系统发起同步操作时,就必须等U盘把缓存里的内容真正写到闪存,这个过程可能需要几秒(取决于U盘的缓存大小和闪存写入速度),进程就只能卡在D状态等这个最终确认。
为啥早期Linux或其他Unix没这情况?:一方面是早期USB存储没这么普及,大家用的机械硬盘缓存小,写入反馈更真实;另一方面,早期内核的IO同步机制可能没这么严格,或者某些Unix系统更倾向于让进程尽早返回,把同步操作交给后台线程处理,而不是让进程原地等待硬件的最终确认。现在Linux更看重数据一致性,所以会严格等待硬件的写入完成信号,自然就会出现这种明显的等待了。
总结一下,这个现象不是bug,是内核为了保证数据安全和一致性的设计,再加上USB存储设备的缓存特性,才导致了进程短暂卡在不可中断的睡眠状态。
备注:内容来源于stack exchange,提问作者delt

