在Linux中,当需要从磁盘读取块时,进程状态会发生什么变化?被封锁了吗?如果是这样,如何选择另一个流程来执行?
Answers:
在等待文件描述符返回read()
或write()
从文件描述符返回时,该过程将进入一种特殊的睡眠状态,称为“ D”或“磁盘睡眠”。这很特殊,因为在这种状态下无法终止或中断该进程。等待ioctl()返回的进程也将以这种方式进入睡眠状态。
例外情况是,当文件(例如终端或其他字符设备)以O_NONBLOCK
模式打开时,如果假定文件(例如调制解调器)需要时间来初始化,则通过该文件。但是,您在问题中指出了阻止设备。另外,我从未尝试过以ioctl()
非阻塞模式打开的fd可能会阻塞的(至少是在不知情的情况下)。
如何选择另一个进程完全取决于您使用的调度程序,以及其他进程可能在该调度程序中修改其权重的方法。
已知某些用户空间程序在某些情况下会一直保持这种状态,直到重新启动为止。这些通常与其他“僵尸”归为一组,但该术语不正确,因为它们在技术上并未失效。
当某个进程需要从磁盘中获取数据时,它实际上会停止在CPU上运行以让其他进程运行,因为该操作可能需要很长时间才能完成-至少需要5ms的磁盘寻道时间,而5ms就是1000万从程序的角度来看,CPU周期是永恒的!
从程序员的角度(也称为“在用户空间中”),这称为阻塞系统调用。如果您进行调用write(2)
(这是同名系统调用周围的薄libc包装器),则您的进程不会完全在该边界处停止;它继续在内核中运行系统调用代码。在大多数情况下,它一直一直到特定的磁盘控制器驱动程序(文件名→文件系统/ VFS→块设备→设备驱动程序),在该驱动程序中,将要在磁盘上获取块的命令提交给适当的硬件,这是非常重要的。大多数时候都可以快速操作。
然后,该过程将进入睡眠状态(在内核空间中,阻塞称为睡眠-从内核的角度来看,从来没有“阻塞”任何东西)。一旦硬件最终获取了正确的数据,它将被唤醒,然后该过程将被标记为可运行并进行调度。最终,调度程序将运行该过程。
最后,在用户空间中,阻塞的系统调用将返回正确的状态和数据,并且程序流继续进行。
它可以调用大部分的I / O系统调用非阻塞模式(见O_NONBLOCK
中open(2)
和fcntl(2)
)。在这种情况下,系统调用立即返回,并且仅报告提交磁盘操作。程序员将不得不在稍后的时间显式检查操作是否成功完成,并获取其结果(例如使用select(2)
)。这称为异步或基于事件的编程。
这里提到D状态(TASK_UNINTERRUPTIBLE
在Linux状态名称中称为D状态)的大多数答案都是错误的。在d状态是一种特殊的睡眠模式,这是只有在内核空间的代码路径,当代码路径引发不能被中断(因为这将是太复杂,程序),并期望它只会阻止了很短时间。我相信大多数“ D状态”实际上是不可见的。它们的寿命很短,无法通过“ top”之类的采样工具进行观察。
在某些情况下,您会在D状态下遇到无法杀死的进程。NFS为此而闻名,我已经遇到过很多次了。我认为某些VFS代码路径之间存在语义冲突,它们假定总是到达本地磁盘并进行快速错误检测(在SATA上,错误超时将在几百毫秒左右),而NFS实际上从网络中获取数据,更具弹性,恢复速度较慢(TCP超时通常为300秒)。阅读本文,了解带有TASK_KILLABLE
状态的Linux 2.6.25中引入的出色解决方案。在这个时代之前,有一种黑客,您实际上可以通过将SIGKILL发送到内核线程来向NFS进程客户端发送信号rpciod
,但是请不要理会这个丑陋的把戏。
/proc/stat
?
执行I / O的进程将进入D状态(不间断睡眠),这将释放CPU,直到发生硬件中断,告诉CPU返回执行程序为止。有关man ps
其他过程状态,请参见。
根据您的内核,有一个Process Scheduler,它跟踪准备好要执行的进程的运行队列。它与调度算法一起,告诉内核将哪个进程分配给哪个CPU。有内核进程和用户进程要考虑。每个进程都分配了一个时间片,这是允许使用的大量CPU时间。一旦进程使用了所有时间片,就将其标记为已过期,并在调度算法中将其赋予较低的优先级。
在2.6内核中,有一个O(1)时间复杂度调度程序,因此,无论您正在运行多少个进程,它都会以恒定的时间分配CPU。但是,由于2.6引入了抢占,因此CPU负载平衡并不是一个简单的算法,因此更为复杂。在任何情况下,它都是高效的,并且在您等待I / O时CPU不会保持空闲状态。
正如其他人已经解释的那样,处于“ D”状态(不间断睡眠)的进程导致ps进程挂起。对我来说,RedHat 6.x和自动挂载的NFS主目录已经发生了很多次。
要列出处于D状态的进程,可以使用以下命令:
cd /proc
for i in [0-9]*;do echo -n "$i :";cat $i/status |grep ^State;done|grep D
要了解进程的当前目录(可能是已装入的NFS磁盘),可以使用类似于以下示例的命令(将31134替换为正在休眠的进程号):
# ls -l /proc/31134/cwd
lrwxrwxrwx 1 pippo users 0 Aug 2 16:25 /proc/31134/cwd -> /auto/pippo
我发现,将带有-f(force)开关的umount命令提供给相关的已挂载nfs文件系统,可以唤醒睡眠过程:
umount -f /auto/pippo
文件系统没有卸载,因为它很忙,但是相关进程确实唤醒了,我能够解决问题而无需重新启动。
假设您的进程是一个单线程,并且您正在使用阻塞I / O,则您的进程将阻塞等待I / O完成。内核将根据优先级,优先级,上次运行时间等选择要同时运行的另一个进程。如果没有其他可运行的进程,则内核将不会运行。相反,它会告诉计算机机器处于空闲状态(这将降低功耗)。
等待I / O完成的进程通常显示在状态D中,例如ps
和中top
。