问自己的问题
- lock_buffer:
static inline void lock_buffer(struct buffer_head * bh)
{
cli(); // 1. 为什么需要用cli/sti来控制IF中断屏蔽?
while(bh->b_block) // 2. 'while'可以用'if'替换吗?
sleep_on(bh->b_wait); // 可能你得先弄明白sleep_on的运作
bh->b_lock =1; // 3. 放在cli/sti外面行吗?
sti();
}
- add_request:
1. 为什么需要用cli/sti来屏蔽中断?
2. 为什么请求项不能插入到链表头部?
3. 到底如何理解电梯算法?
- make_request:
1. READA/WRITEA这样做的意义是什么?
2. 如何理解b_dirt和b_uptodate标识?
3. 如何理解'2/3'?
- ll_rw_page:
1. 对于page的操作具有什么特殊性?
2. page和buffer head有什么不同?
code部分
/* * linux/kernel/blk_dev/ll_rw.c * * (C) 1991 Linus Torvalds */ /* * This handles all read/write requests to block devices */ #include <errno.h> #include <linux/sched.h> #include <linux/kernel.h> #include <asm/system.h> #include "blk.h" /* * The request-struct contains all necessary data * to load a nr of sectors into memory */ struct request request[NR_REQUEST]; /* * used to wait on when there are no free requests */ struct task_struct * wait_for_request = NULL; /* blk_dev_struct is: * do_request-address * next-request */ struct blk_dev_struct blk_dev[NR_BLK_DEV] = { { NULL, NULL }, /* no_dev */ { NULL, NULL }, /* dev mem */ { NULL, NULL }, /* dev fd */ { NULL, NULL }, /* dev hd */ { NULL, NULL }, /* dev ttyx */ { NULL, NULL }, /* dev tty */ { NULL, NULL } /* dev lp */ }; /* * blk_size contains the size of all block-devices: * * blk_size[MAJOR][MINOR] * * if (!blk_size[MAJOR]) then no minor size checking is done. */ int * blk_size[NR_BLK_DEV] = { NULL, NULL, }; /* 锁住一块buffer 1. 在内核态其他进程无法抢占 2. 影响buffer的只有块设备驱动,它会在一个请求项完成后把对应的buffer unlock 第一种NO情况: static inline void lock_buffer(struct buffer_head * bh) { while (bh->b_lock) sleep_on(&bh->b_wait); bh->b_lock=1; } 判断该buffer被lock了,当进程进入睡眠时,发生了块设备中断,于是唤醒该buffer的等待队列头 (若此时刚好为当前进程)然后buffer unlock,此时进程正式进入睡眠,然后尴尬的是,该buffer已 经unlock了,但它的队列头却进入了睡眠状态,一旦该buffer被释放,那么这些等待的进程将永远 无法得到执行。 第二种NO情况: static inline void lock_buffer(struct buffer_head * bh) { cli(); if (bh->b_lock) sleep_on(&bh->b_wait); bh->b_lock=1; sti(); } 所有等待该buffer的进程都会进入等待队列,而最开始被唤醒的进程一定是队列头进程。当buffer unlock后唤醒队列头A进程,时间片在B进程中,B进程发现该buffer unlock,于是lock了它,然后主 动放弃时间片,一旦被A进程捕获,它又去lock该buffer,造成了同一个buffer被两个进程同时拥有 的错误情况。 第三种YES情况: static inline void lock_buffer(struct buffer_head * bh) { cli(); while (bh->b_lock) sleep_on(&bh->b_wait); sti(); bh->b_lock=1; } 一旦该buffer被进程抢到,即可随意处置,但如果你想放弃时间片且不让其他进程占用它,那么请 记得先lock住它在处置。 由于该buffer已经处于unlock状态,因此即使有块设备中断也没有对此buffer的任何操作发生。 从代码美观性考虑,下面的代码可读性高。 */ static inline void lock_buffer(struct buffer_head * bh) { cli(); while (bh->b_lock) sleep_on(&bh->b_wait); bh->b_lock=1; sti(); } static inline void unlock_buffer(struct buffer_head * bh) { if (!bh->b_lock) printk("ll_rw_block.c: buffer not locked\n\r"); bh->b_lock = 0; wake_up(&bh->b_wait); } /* * add-request adds a request to the linked list. * It disables interrupts so that it can muck with the * request-lists in peace. * * Note that swapping requests always go before other requests, * and are done in the order they appear. */ static void add_request(struct blk_dev_struct * dev, struct request * req) { struct request * tmp; req->next = NULL; cli(); if (req->bh) req->bh->b_dirt = 0; // 该buffer即将要与磁盘同步 /* 设备上没有请求项,是第一次或者之前设备上的请求都处理完了 */ if (!(tmp = dev->current_request)) { dev->current_request = req; sti(); // 记得一定要开中断,使块设备中断恢复执行 (dev->request_fn)(); // 开始执行设备请求 return; } /* 把当前请求项加入到设备请求链表中,使用电梯算法 把req请求项添加到tmp后面,它不能被插入到链表头部,因为链表头 正在进行请求执行 */ for ( ; tmp->next ; tmp=tmp->next) { // 关于新增的page操作,原则是page操作优先于block if (!req->bh) { // 是page请求项 if (tmp->next->bh) // 是block请求项 break; else continue; } /* 电梯算法部分,原则是读操作优先于写 低扇区优先于高扇区保证顺序性 IN_ORDER(S1,S2)为真,则S1优先于S2. 如何理解电梯算法: 假想,所有坐电梯的人都要到达0楼。那么这个问题就变成如何在电梯折返 次数最少且所有人等待电梯时间最短的情况下把他们运送到0楼的方法。 */ if ((IN_ORDER(tmp,req) || !IN_ORDER(tmp,tmp->next)) && IN_ORDER(req,tmp->next)) break; } req->next=tmp->next; tmp->next=req; sti(); } /* block的请求 */ static void make_request(int major,int rw, struct buffer_head * bh) { struct request * req; int rw_ahead; /* WRITEA/READA is special case - it is not really needed, so if the */ /* buffer is locked, we just forget about it, else it's a normal read */ /* 预读操作是为了能在保证在进程不需要利用等待时间来处理块设备请求操作的操作 */ if ((rw_ahead = (rw == READA || rw == WRITEA))) { if (bh->b_lock) return; if (rw == READA) rw = READ; else rw = WRITE; } if (rw!=READ && rw!=WRITE) panic("Bad block dev command, must be R/W/RA/WA"); /* 锁住buffer */ lock_buffer(bh); /* 如何理解b_dirt和b_uptodate标志: 1. b_dirt:脏标志,为什么会脏呢,因为这块buffer的数据被破坏了,一旦 这块buffer开始与对应的硬盘block进行同步后,脏标志会被撤除。 2. b_uptodate:更新标志,当buffer被拿来使用的时候,会清除该标志,因 为它需要对应硬盘的block来更新它的数据,一旦该buffer被新数据填充, 那么b_uptodate=1,表示数据已经被更新过了。 b_dirt与b_uptodate无任何联系,b_dirt用于写操作,对于不脏的buffer, 说明buffer数据与硬盘数据保持着一致性,不需要写入,b_uptodate用于读操 作,对于已经更新过的buffer,说明buffer数据是最新的,不需要读取。 */ if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) { unlock_buffer(bh); return; } /* 获取请求项,开始请求 */ repeat: /* we don't allow the write-requests to fill up the queue completely: * we want some room for reads: they take precedence. The last third * of the requests are only for reads. */ if (rw == READ) req = request+NR_REQUEST; else req = request+((NR_REQUEST*2)/3); /* find an empty request */ while (--req >= request) if (req->dev<0) break; /* if none found, sleep on new requests: check for rw_ahead */ if (req < request) { if (rw_ahead) { unlock_buffer(bh); return; } sleep_on(&wait_for_request); goto repeat; } /* fill up the request-info, and add it to the queue */ req->dev = bh->b_dev; req->cmd = rw; req->errors=0; req->sector = bh->b_blocknr<<1; req->nr_sectors = 2; req->buffer = bh->b_data; req->waiting = NULL; req->bh = bh; req->next = NULL; add_request(major+blk_dev,req); } /* 对page的请求比较特殊,没有采用系统buffer,有且仅有一个等待进程, 因此比较简单,只需要在块设备处理完请求项时唤醒该进程,那么它就 可以对此buffer进行处理,可以理解为该进程独占该buffer。 */ void ll_rw_page(int rw, int dev, int page, char * buffer) { struct request * req; unsigned int major = MAJOR(dev); if (major >= NR_BLK_DEV || !(blk_dev[major].request_fn)) { printk("Trying to read nonexistent block-device\n\r"); return; } /* make request */ if (rw!=READ && rw!=WRITE) panic("Bad block dev command, must be R/W"); /* 获取请求项,开始请求 */ repeat: req = request+NR_REQUEST; while (--req >= request) if (req->dev<0) break; if (req < request) { sleep_on(&wait_for_request); goto repeat; } /* fill up the request-info, and add it to the queue */ req->dev = dev; req->cmd = rw; req->errors = 0; req->sector = page<<3; req->nr_sectors = 8; req->buffer = buffer; req->waiting = current; req->bh = NULL; req->next = NULL; current->state = TASK_UNINTERRUPTIBLE; add_request(major+blk_dev,req); schedule(); // 放弃时间片,进入不可中断的等待中... } void ll_rw_block(int rw, struct buffer_head * bh) { unsigned int major; if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV || !(blk_dev[major].request_fn)) { printk("Trying to read nonexistent block-device\n\r"); return; } /* 没必要分开,可以合并的函数 */ make_request(major,rw,bh); } void blk_dev_init(void) { int i; for (i=0 ; i<NR_REQUEST ; i++) { request[i].dev = -1; request[i].next = NULL; } }