📄 buffer.c.bak
字号:
{ struct buffer_head * tmp, * bh;repeat:
// 搜索hash 表,如果指定块已经在高速缓冲中,则返回对应缓冲区头指针,退出。 if (bh = get_hash_table(dev,block)) return bh;
// 扫描空闲数据块链表,寻找空闲缓冲区。
// 首先让tmp 指向空闲链表的第一个空闲缓冲区头。 tmp = free_list; do {
// 如果该缓冲区正被使用(引用计数不等于0),则继续扫描下一项。
if (tmp->b_count)
continue;
// 如果缓冲头指针bh 为空,或者tmp 所指缓冲头的标志(修改、锁定)权重小于bh 头标志的权重,
// 则让bh 指向该tmp 缓冲区头。如果该tmp 缓冲区头表明缓冲区既没有修改也没有锁定标志置位,
// 则说明已为指定设备上的块取得对应的高速缓冲区,则退出循环。 if (!bh || BADNESS(tmp)<BADNESS(bh)) { bh = tmp; if (!BADNESS(tmp)) break; }/* 重复操作直到找到适合的缓冲区 */ } while ((tmp = tmp->b_next_free) != free_list);
// 如果所有缓冲区都正被使用(所有缓冲区的头部引用计数都>0),
// 则睡眠,等待有空闲的缓冲区可用。 if (!bh) { sleep_on(&buffer_wait); goto repeat; }
// 等待该缓冲区解锁(如果已被上锁的话)。 wait_on_buffer(bh);
// 如果该缓冲区又被其它任务使用的话,只好重复上述过程。 if (bh->b_count) goto repeat;
// 如果该缓冲区已被修改,则将数据写盘,并再次等待缓冲区解锁。如果该缓冲区又被其它任务使用
// 的话,只好再重复上述过程。 while (bh->b_dirt) { sync_dev(bh->b_dev); wait_on_buffer(bh); if (bh->b_count) goto repeat; }/* 注意!!当进程为了等待该缓冲块而睡眠时,其它进程可能已经将该缓冲块 *//* 加入进高速缓冲中,所以要对此进行检查。 */
// 在高速缓冲hash 表中检查指定设备和块的缓冲区是否已经被加入进去。如果是的话,就再次重复
// 上述过程。 if (find_buffer(dev,block)) goto repeat;/* OK,最终我们知道该缓冲区是指定参数的唯一一块, *//* 而且还没有被使用(b_count=0),未被上锁(b_lock=0),并且是干净的(未被修改的) */
// 于是让我们占用此缓冲区。置引用计数为1,复位修改标志和有效(更新)标志。 bh->b_count=1; bh->b_dirt=0; bh->b_uptodate=0;
// 从hash 队列和空闲块链表中移出该缓冲区头,让该缓冲区用于指定设备和其上的指定块。 remove_from_queues(bh); bh->b_dev=dev; bh->b_blocknr=block;
// 然后根据此新的设备号和块号重新插入空闲链表和hash 队列新位置处。并最终返回缓冲头指针。 insert_into_queues(bh); return bh;}
//// 释放指定的缓冲区。
// 等待该缓冲区解锁。引用计数递减1。唤醒等待空闲缓冲区的进程。void brelse(struct buffer_head * buf){ if (!buf) // 如果缓冲头指针无效则返回。 return; wait_on_buffer(buf); if (!(buf->b_count--)) panic("Trying to free free buffer"); wake_up(&buffer_wait);}/* * 从设备上读取指定的数据块并返回含有数据的缓冲区。如果指定的块不存在 * 则返回NULL。 */
//// 从指定设备上读取指定的数据块。struct buffer_head * bread(int dev,int block){ struct buffer_head * bh;
// 在高速缓冲中申请一块缓冲区。如果返回值是NULL 指针,表示内核出错,死机。 if (!(bh=getblk(dev,block))) panic("bread: getblk returned NULL\n");
// 如果该缓冲区中的数据是有效的(已更新的)可以直接使用,则返回。 if (bh->b_uptodate) return bh;
// 否则调用ll_rw_block()函数,产生读设备块请求。并等待缓冲区解锁。 ll_rw_block(READ,bh); wait_on_buffer(bh);
// 如果该缓冲区已更新,则返回缓冲区头指针,退出。 if (bh->b_uptodate) return bh;
// 否则表明读设备操作失败,释放该缓冲区,返回NULL 指针,退出。 brelse(bh); return NULL;}
//// 复制内存块。
// 从from 地址复制一块数据到to 位置。
extern __inline void COPYBLK(char* from, char* to)
{_asm{
mov ecx,BLOCK_SIZE/4
mov esi,from
mov edi,to
cld
rep movsd
}}/*#define COPYBLK(from,to) \__asm__("cld\n\t" \ "rep\n\t" \ "movsl\n\t" \ ::"c" (BLOCK_SIZE/4),"S" (from),"D" (to) \ :"cx","di","si")*//* * bread_page 一次读四个缓冲块内容读到内存指定的地址。它是一个完整的函数,
* 因为同时读取四块可以获得速度上的好处,不用等着读一块,再读一块了。 */
//// 读设备上一个页面(4 个缓冲块)的内容到内存指定的地址。void bread_page(unsigned long address,int dev,int b[4]){ struct buffer_head * bh[4]; int i;
// 循环执行4 次,读一页内容。 for (i=0 ; i<4 ; i++) if (b[i]) {
// 取高速缓冲中指定设备和块号的缓冲区,如果该缓冲区数据无效则产生读设备请求。 if (bh[i] = getblk(dev,b[i])) if (!bh[i]->b_uptodate) ll_rw_block(READ,bh[i]); } else bh[i] = NULL;
// 将4 块缓冲区上的内容顺序复制到指定地址处。 for (i=0 ; i<4 ; i++,address += BLOCK_SIZE) if (bh[i]) { wait_on_buffer(bh[i]); // 等待缓冲区解锁(如果已被上锁的话)。 if (bh[i]->b_uptodate) // 如果该缓冲区中数据有效的话,则复制。 COPYBLK(bh[i]->b_data,(char *)address); brelse(bh[i]); // 释放该缓冲区。 }}/* * OK,breada 可以象bread 一样使用,但会另外预读一些块。该函数参数列表
* 需要使用一个负数来表明参数列表的结束。 */
//// 从指定设备读取指定的一些块。
// 成功时返回第1 块的缓冲区头指针,否则返回NULL。struct buffer_head * breada(int dev,int first, ...){ va_list args; struct buffer_head * bh, *tmp;
// 取可变参数表中第1 个参数(块号)。 va_start(args,first);
// 取高速缓冲中指定设备和块号的缓冲区。如果该缓冲区数据无效,则发出读设备数据块请求。 if (!(bh=getblk(dev,first))) panic("bread: getblk returned NULL\n"); if (!bh->b_uptodate) ll_rw_block(READ,bh);
// 然后顺序取可变参数表中其它预读块号,并作与上面同样处理,但不引用。 while ((first=va_arg(args,int))>=0) { tmp=getblk(dev,first); if (tmp) { if (!tmp->b_uptodate) ll_rw_block(READA,bh); tmp->b_count--; } }
// 可变参数表中所有参数处理完毕。等待第1 个缓冲区解锁(如果已被上锁)。 va_end(args); wait_on_buffer(bh);
// 如果缓冲区中数据有效,则返回缓冲区头指针,退出。否则释放该缓冲区,返回NULL,退出。 if (bh->b_uptodate) return bh; brelse(bh); return (NULL);}
//// 缓冲区初始化函数。
// 参数buffer_end 是指定的缓冲区内存的末端。对于系统有16MB 内存,则缓冲区末端设置为4MB。
// 对于系统有8MB 内存,缓冲区末端设置为2MB。void buffer_init(long buffer_end){ struct buffer_head * h = start_buffer; void * b; int i;
// 如果缓冲区高端等于1Mb,则由于从640KB-1MB 被显示内存和BIOS 占用,因此实际可用缓冲区内存
// 高端应该是640KB。否则内存高端一定大于1MB。 if (buffer_end == 1<<20) b = (void *) (640*1024); else b = (void *) buffer_end;
// 这段代码用于初始化缓冲区,建立空闲缓冲区环链表,并获取系统中缓冲块的数目。
// 操作的过程是从缓冲区高端开始划分1K 大小的缓冲块,与此同时在缓冲区低端建立描述该缓冲块
// 的结构buffer_head,并将这些buffer_head 组成双向链表。
// h 是指向缓冲头结构的指针,而h+1 是指向内存地址连续的下一个缓冲头地址,也可以说是指向h
// 缓冲头的末端外。为了保证有足够长度的内存来存储一个缓冲头结构,需要b 所指向的内存块
// 地址>= h 缓冲头的末端,也即要>=h+1。 while ( (b = (char*)b - BLOCK_SIZE) >= ((void *) (h+1)) ) { h->b_dev = 0; // 使用该缓冲区的设备号。 h->b_dirt = 0; // 脏标志,也即缓冲区修改标志。 h->b_count = 0; // 该缓冲区引用计数。 h->b_lock = 0; // 缓冲区锁定标志。 h->b_uptodate = 0; // 缓冲区更新标志(或称数据有效标志)。 h->b_wait = NULL; // 指向等待该缓冲区解锁的进程。 h->b_next = NULL; // 指向具有相同hash 值的下一个缓冲头。 h->b_prev = NULL; // 指向具有相同hash 值的前一个缓冲头。 h->b_data = (char *) b; // 指向对应缓冲区数据块(1024 字节)。 h->b_prev_free = h-1; // 指向链表中前一项。 h->b_next_free = h+1; // 指向链表中下一项。
h++; // h 指向下一新缓冲头位置。 NR_BUFFERS++; // 缓冲区块数累加。 if (b == (void *) 0x100000) // 如果地址b 递减到等于1MB,则跳过384KB, b = (void *) 0xA0000; // 让b 指向地址0xA0000(640KB)处。 } h--; // 让h 指向最后一个有效缓冲头。 free_list = start_buffer; // 让空闲链表头指向头一个缓冲区头。 free_list->b_prev_free = h; // 链表头的b_prev_free 指向前一项(即最后一项)。 h->b_next_free = free_list; // h 的下一项指针指向第一项,形成一个环链。
// 初始化hash 表(哈希表、散列表),置表中所有的指针为NULL。 for (i=0;i<NR_HASH;i++) hash_table[i]=NULL;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -