进程状态如何更新为阻塞状态 (TASK_INTERRUPTIBLE)?
How is a process state updated to blocked state (TASK_INTERRUPTIBLE)?
进程在等待I/O时,任务状态如何更新为TASK_INTERRUPTIBLE(即阻塞)?
想象一下这种情况,进程向块设备发出 I/O 请求。根据我之前的thread,流程最终调用elv_add_request()
将请求添加到I/O队列。所以我想在这个 elv_add_request()
调用中,实现将类似于:
elv_add_request(){
// Register IO_CALLBACK()
set_task_state(task, TASK_INTERRUPTABLE); // blocked
// flush IO request to disk
...
}
IO_CALLBACK(){
set_task_state(task, TASK_RUNNING); // IO completed, ready to run
}
逻辑是这样的:当I/O请求完成后,会使用回调函数通知内核进程已经就绪。有道理吗?
如果是这样,回调机制是如何实现的?它是 CPU/hardware 功能吗?
除了在调用 elv_add_request()
之前设置 io 回调外,它的行为与您描述的类似。如果我们从前一个线程中取出堆栈:
[<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
调用堆栈(我正在查看 4.1-rc1 源代码)如下所示:
pkt_generic_packet()
blk_execute_rq()
initialize a 'struct completion' object
set it as 'struct request->end_io_data'
blk_execute_rq_nowait(..., blk_end_sync_rq) // that's the io callback
wait_for_completion_io() //sets task to TASK_UNINTERRUPTIBLE, waits on the 'struct completion' object to be complete
...
the io then happens, a possible scenario would then be:
blk_end_request()
blk_end_bidi_request()
blk_finish_request()
req->end_io() // This is blk_end_sync_rq
blk_end_sync_rq()
complete() // sets task to TASK_NORMAL, signals completion
进程在等待I/O时,任务状态如何更新为TASK_INTERRUPTIBLE(即阻塞)?
想象一下这种情况,进程向块设备发出 I/O 请求。根据我之前的thread,流程最终调用elv_add_request()
将请求添加到I/O队列。所以我想在这个 elv_add_request()
调用中,实现将类似于:
elv_add_request(){
// Register IO_CALLBACK()
set_task_state(task, TASK_INTERRUPTABLE); // blocked
// flush IO request to disk
...
}
IO_CALLBACK(){
set_task_state(task, TASK_RUNNING); // IO completed, ready to run
}
逻辑是这样的:当I/O请求完成后,会使用回调函数通知内核进程已经就绪。有道理吗?
如果是这样,回调机制是如何实现的?它是 CPU/hardware 功能吗?
除了在调用 elv_add_request()
之前设置 io 回调外,它的行为与您描述的类似。如果我们从前一个线程中取出堆栈:
[<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
调用堆栈(我正在查看 4.1-rc1 源代码)如下所示:
pkt_generic_packet()
blk_execute_rq()
initialize a 'struct completion' object
set it as 'struct request->end_io_data'
blk_execute_rq_nowait(..., blk_end_sync_rq) // that's the io callback
wait_for_completion_io() //sets task to TASK_UNINTERRUPTIBLE, waits on the 'struct completion' object to be complete
...
the io then happens, a possible scenario would then be:
blk_end_request()
blk_end_bidi_request()
blk_finish_request()
req->end_io() // This is blk_end_sync_rq
blk_end_sync_rq()
complete() // sets task to TASK_NORMAL, signals completion