IO调度器和CPU调度器的概念让我很困惑。以下是我的理解:
- Linux默认使用CFS调度器+nice值来调度进程。
- 每个进程都有一个IO队列。
- 有一个IO调度器内核线程。
- IO调度器是在块级别的,而不是在文件级别的。
- IO调度器是文件系统的一个模块。
问题:
IO调度器和CPU调度器之间有什么关系?从概念上讲,在我看来,CPU 调度程序优于 IO 调度程序。 CPU 调度首先发生。 IO调度器本身就是一个线程并服从CPU调度。
一个人为的场景如下所示:
步骤1:CPU调度器选择进程P1来执行
步骤2:P1将IO请求放入自己的IO队列中
步骤 3+:CPU 调度程序选择其他线程来运行。 (假设除了P1之外没有进程有IO)
....(一段时间后)
步骤n:CPU调度程序选择IO调度程序线程来运行。
步骤 n+1:IO 调度程序线程“通知”P1 有 IO 请求排队并将这些请求发送到磁盘。
我的理解和场景有意义吗?
答案1
我们先从IO调度器开始。每个块设备都有一个 IO 调度程序。它的工作是调度(排序)设备队列中堆积的请求。 Linux 内核当前提供三种不同的算法:deadline
、noop
和cfq
。cfq
是默认值,根据其文档:
CFQ I/O 调度程序尝试在系统中的所有进程之间平均分配带宽。它应该提供一个公平且低延迟的工作环境,适用于桌面和服务器系统
scheduler
您可以通过与您的块设备对应的文件配置哪个调度程序管理哪个设备/sys/
(您可以发出以下命令来查找它:)find /sys | grep queue/scheduler
。
这个简短的描述没有说明的是,这cfq
是唯一查看ioprio
进程的调度程序。ioprio
是您可以分配给进程的设置,算法在选择另一个请求之前会考虑到这一点。ioprio
可以通过ionice
实用程序进行设置。
然后是任务调度程序。它的工作是在进程之间分配CPU准备运行。它考虑了诸如给定进程的优先级、类别和良好性等因素,以及该进程运行的时间和其他启发式方法。
现在,回答你的问题:
IO调度器和CPU调度器之间有什么关系?
除了名字之外,没什么。他们安排不同的共享资源。第一个命令将请求发送到磁盘,第二个命令将“请求”(您可以将进程视为请求 CPU 时间才能运行)调度到 CPU。
CPU 调度首先发生。 IO调度器本身就是一个线程并服从CPU调度。
它不会像 IO 调度程序算法由正在排队请求的进程运行那样发生。了解这一点的一个好方法是查看elv_add_request()
其路径中发生的崩溃。例如:
[...]
[<c027fac4>] error_code+0x74/0x7c
[<c019ed65>] elv_next_request+0x6b/0x116
[<e08335db>] scsi_request_fn+0x5e/0x26d [scsi_mod]
[<c019ee6a>] elv_insert+0x5a/0x134
[<c019efc1>] __elv_add_request+0x7d/0x82
[<c019f0ab>] elv_add_request+0x16/0x1d
[<e0e8d2ed>] pkt_generic_packet+0x107/0x133 [pktcdvd]
[<e0e8d772>] pkt_get_disc_info+0x42/0x7b [pktcdvd]
[<e0e8eae3>] pkt_open+0xbf/0xc56 [pktcdvd]
[<c0168078>] do_open+0x7e/0x246
[<c01683df>] blkdev_open+0x28/0x51
[<c014a057>] __dentry_open+0xb5/0x160
[<c014a183>] nameidata_to_filp+0x27/0x37
[<c014a1c6>] do_filp_open+0x33/0x3b
[<c014a211>] do_sys_open+0x43/0xc7
[<c014a2cd>] sys_open+0x1c/0x1e
[<c0102b82>] sysenter_past_esp+0x5f/0x85
注意进程如何进入内核调用 open(),这最终涉及到 lift ( elv
) 算法。