1. 用户进程
当一个用户程序需要去读取一个普通的文件时,会执行如下的语句:
? read系统调用语句
n = read(fd,buffer, nbyte);
? read构造的消息
message m;
m.m1_i1 = fd;
m.m1_i2 = nbytes;
m.m1_p1 = (char *) buffer;
m.type = READ
? sendrec给文件系统进程发送消息
_sendrec(VFS_PROC_NR, &m)
2. 文件系统进程
? 文件系统进程阻塞在接收消息
receive(ANY,&m_in);
? 接收到用户进程的read请求,调用do_read函数
Figure 5-46. Some of the procedures involved in reading a file.
备注:
在文件系统进程初始化时,将dmap 列表中索引为DRVR_PROC_NR(磁盘驱动程序的进程号) 的表项的dmap_io字段设置为gen_io,即图中的rw_dev为gen_io。函数调用路径如下图所示。
? 调用read_write函数,将待传输的数据离散成n个块(chunk)
? 循环调用n次rw_chunk函数,即每读一块调用一次该函数,如果要读取的块不在缓冲区buf中,将通过原语sendrec给磁盘驱动程序发送应答消息,请求读取块操作。
对于rw_chunk函数:
? 调用read_map函数
Given an inode and a position within the correspondingfile, locate the block (not zone) number in which that position is to be foundand return it.
? 调用rahead函数
Fetch a block from the cache or thedevice. If a physical read is required,prefetch as many more blocks as convenient into the cache.
? 调用内核调用sys_copy函数
Copy a chunk from the block buffer touser space.
? 调用put_block函数
Return a block to the list ofavailable blocks.
如果块不在缓冲区,则在rahead->get_block->rw_block->dev_io中:
? 构造消息结构
message dev_mess;
/*Set up the message passed to task. */
dev_mess.m_type = DEV_READ;
dev_mess.DEVICE = 从设备号;
dev_mess.POSITION = 文件位置;
dev_mess.PROC_NR = 文件系统进程;
dev_mess.ADDRESS = 文件系统进程数据块缓冲区地址&buf[i];
dev_mess.COUNT = 块长度;
dev_mess.TTY_FLAGS = 文件标志;
如果块不在缓冲区,则在rahead->get_block->rw_block->dev_io->gen_io中:
? sendrec给磁盘设备驱动程序发送消息
_sendrec (DRVR_PROC_NR,& dev_mess);
3. 磁盘驱动进程
? 磁盘驱动进程阻塞在语句
receive(ANY,&mess);
? 磁盘驱动进程接收到文件系统的消息
执行的主要代码粘贴如下:
/* Tell thecontroller to transfer nbytes bytes. */
r= do_transfer(wn, wn->precomp, ((nbytes>> SECTOR_SHIFT) & BYTE),
block,opcode);
while(r == OK && nbytes > 0) {
/*For each sector, wait for an interrupt and fetchthe data
* (read), or supply data to the controller andwait for an
* interrupt (write).
*/
if(opcode == DEV_GATHER) { //读操作
/*First an interrupt, then data. */
if((r = at_intr_wait()) != OK) {}
}
/*Wait for data transfer requested. */
if(!w_waitfor(STATUS_DRQ, STATUS_DRQ)) {}
/*Copy bytes to or from the device's buffer. */
if(opcode == DEV_GATHER) { //读操作
if ((s=sys_insw(wn->base_cmd+ REG_DATA, proc_nr,
(void *) iov->iov_addr, SECTOR_SIZE))!= OK)
panic(w_name(),"Callto sys_insw() failed", s);
}else { //写操作
if ((s=sys_outsw(wn->base_cmd +REG_DATA, proc_nr,
(void *) iov->iov_addr, SECTOR_SIZE)) != OK)
panic(w_name(),"Call tosys_insw() failed", s);
/*Data sent, wait for an interrupt. */
if((r = at_intr_wait()) != OK) break;
}
}
? 调用函数do_transfer,给磁盘控制器发送读命令
对于磁盘控制器,当接收到磁盘驱动程序的读取命令时,磁盘控制器便开始读取一个扇区(512字节/扇区)。
? 调用函数at_intr_wait,阻塞磁盘驱动进程,等待数据读取完成的消息
对于磁盘控制器,当一个扇区读取完毕时,磁盘控制器会向CPU发送一个中断请求,CPU检测到中断事件并响应后,最终会跳转到内核的中断服务函数generic_handler中去执行,该函数中调用原语notify给磁盘驱动程序发送一条通知消息。
等待中断函数at_intr_wait:
/* Wait for aninterrupt that sets w_status to "not busy". */
while(w_wn->w_status & (STATUS_ADMBSY|STATUS_BSY)) {
receive(ANY, &m); /* expect HARD_INT message */
if (m.m_type== HARD_INT) {
sys_inb(w_wn->base_cmd+ REG_STATUS, &w_wn->w_status);
ack_irqs(m.NOTIFY_ARG);
}
}
? 接收到数据读取完成消息,解除阻塞
当磁盘驱动程序接收到内核中断服务函数的notify通知消息后,阻塞状态被解除,当驱动程序再此被调度时,继续执行后续代码。
? 调用函数w_waitfor,等待磁盘控制器数据传输请求
? 调用函数sys_insw(内核调用),读入一个扇区的数据
将数据从磁盘控制器的数据缓冲区拷贝到文件系统进程地址空间的块缓冲区。
磁盘驱动程序通过内核调用sys_irqsetpolicy(sys_irqctl的简写形式),注册中断服务程序,调用路径如下图所示:
内核系统任务在接收到消息后,调用函数do_irqctl来处理,在do_irqctl中调用put_irq_handler函数将generic_handler设为中断服务函数。
4. 内核中断服务函数
磁盘驱动进程注册的中断服务函数为generic_handler,核心代码如下:
/* Build notification message and return.*/
lock_notify(HARDWARE, hook->proc_nr);
i'gey