我正在探索 Linux 0.11 源代码,特别是它如何将数据从磁盘读取到内存中。我一直在分析
hd_out
函数以及 do_hd_request
中相关的 hd.c
。
以下是相关代码片段:
#define CURRENT (blk_dev[MAJOR_NR].current_request)
// hd.c
void do_hd_request(void) {
...
unsigned int dev = MINOR(CURRENT->dev);
unsigned int block = CURRENT->sector;
...
nsect = CURRENT->nr_sectors;
...
if (CURRENT->cmd == WRITE) {
hd_out(dev, nsect, sec, head, cyl, WIN_WRITE, &write_intr);
// Poll status register to check if ready to write
for (i = 0; i < 3000 && !(r = inb_p(HD_STATUS) & DRQ_STAT); i++);
if (!r) {
bad_rw_intr();
goto repeat;
}
port_write(HD_DATA, CURRENT->buffer, 256);
} else if (CURRENT->cmd == READ) {
hd_out(dev, nsect, sec, head, cyl, WIN_READ, &read_intr);
} else
panic("unknown hd-command");
}
static void hd_out(unsigned int drive, unsigned int nsect, unsigned int sect,
unsigned int head, unsigned int cyl, unsigned int cmd,
void (*intr_addr)(void)) {
...
do_hd = intr_addr;
outb_p(hd_info[drive].ctl, HD_CMD);
port = HD_DATA;
outb_p(hd_info[drive].wpcom >> 2, ++port);
outb_p(nsect, ++port);
outb_p(sect, ++port);
outb_p(cyl, ++port);
outb_p(cyl >> 8, ++port);
outb_p(0xA0 | (drive << 4) | head, ++port);
outb(cmd, ++port);
}
根据我的理解,
hd_out
向磁盘发出命令并分配适当的中断处理程序(例如,read_intr
或write_intr
)。然后磁盘开始准备数据。我的问题是:磁盘准备数据时内核做了什么?
我假设内核应该切换到另一个进程(通过计时器中断或类似机制),但是当我调试该进程时,我注意到在
hd_out
之后,执行的下一条指令是磁盘中断处理程序(例如,hd_interrupt
),表明磁盘已立即准备就绪。这种行为看起来很奇怪,因为我预计在磁盘准备期间会发生进程切换。
对此行为的任何澄清将不胜感激!
它切换到不同的进程。
您缺少的是:存在一个请求队列。当进程读取或写入时,它不会直接调用
do_hd_request()
。相反,它将请求放入队列中,然后进入睡眠状态。如果队列为空,它只会调用 do_hd_request()
。
当磁盘操作完成时,磁盘中断会将请求标记为已完成,唤醒等待该请求的进程,然后从队列中发出下一个请求。因此,除非队列为空,否则您的请求将由中断处理程序在完成前一个请求后发出。这就是为什么从
do_hd_request()
调用 hd_interrupt
。
相关代码:
fs/buffer.c
寻找 wait_for_buffer()
和 ll_rw_block()
kernel/blk_drv/ll_rw_blk.c
注意:对于 HDD,(dev->request_fn)()
将解析为 do_hd_request()
。kernel/system_call.s:hd_interrupt
。致电 read_intr()
或 write_intr()
。kernel/blk_drv/hd.c
。再次呼叫 do_hd_request()
,以及 end_request()
kernel/blk_drv/blk.h