buffer_init(buffer_memory_end)
参考 [github这个博主的 厉害][ https://github.com/sunym1993/flash-linux0.11-talk ]
这个函数buffer_init
可以看出携带了一个参数buffer_memory_end
,这个参数前面出现过,已经赋值了,
也就是在mian.c
开始的时候
void main(void) { ROOT_DEV = ORIG_ROOT_DEV; drive_info = DRIVE_INFO; memory_end = (1<<20) + (EXT_MEM_K<<10); memory_end &= 0xfffff000; if (memory_end > 16*1024*1024) memory_end = 16*1024*1024; if (memory_end > 12*1024*1024) buffer_memory_end = 4*1024*1024; else if (memory_end > 6*1024*1024) buffer_memory_end = 2*1024*1024; else buffer_memory_end = 1*1024*1024; ......
就是这一段代码把buffer_init
的值设置好了;看看下面这个图吧;根据内存大小不一样位置也不一样;总之它是指向缓存区的主要需要用到的内存的开始
之前通过第一个初始化函数mem_init
把用mem_map
内存管理了起来;
buffer_head
这个结构体
#define NR_HASH 307 #define BLOCK_SIZE 1024 struct buffer_head { char * b_data; /* pointer to data block (1024 bytes) */ unsigned long b_blocknr; /* block number */ unsigned short b_dev; /* device (0 = free) */ unsigned char b_uptodate; unsigned char b_dirt; /* 0-clean,1-dirty */ unsigned char b_count; /* users using this block */ unsigned char b_lock; /* 0 - ok, 1 -locked */ struct task_struct * b_wait; //之前用到那个task[64]数组的那个结构体 struct buffer_head * b_prev; struct buffer_head * b_next; struct buffer_head * b_prev_free; struct buffer_head * b_next_free; };
看看这个初始化函数buffer_init
的代码吧:
#define BLOCK_SIZE 1024 extern int end; int NR_BUFFERS = 0; static struct buffer_head * free_list; struct buffer_head * hash_table[NR_HASH]; void buffer_init(long buffer_end) { struct buffer_head * h = start_buffer; void * b; int i; if (buffer_end == 1<<20) // 1<<20 为1MB b = (void *) (640*1024); else b = (void *) buffer_end; while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) { //BLOCK_SIZE=1024 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; h->b_prev = NULL; h->b_data = (char *) b; h->b_prev_free = h-1; h->b_next_free = h+1; h++; NR_BUFFERS++; //NR_BUFFERS = 0 if (b == (void *) 0x100000) b = (void *) 0xA0000; } h--; free_list = start_buffer; free_list->b_prev_free = h; h->b_next_free = free_list; for (i=0;i<NR_HASH;i++) //NR_HASH=307 hash_table[i]=NULL; }
先看看这一个宏定义
extern int end; struct buffer_head * start_buffer = (struct buffer_head *) &end;
这个end
是一个外部变量,
这个外部变量 end 并不是操作系统代码写就的,而是由链接器 ld 在链接整个程序时设置的一个外部变量,帮我们计算好了整个内核代码的末尾地址。
再看start_buffer就是取的end
的地址为buffer_head
这个结构体定义的start_buffer
所以start_buffer
是这个这个缓冲区的开始的位置
第一部分:
void * b; if (buffer_end == 1<<20) // 1<<20 为1MB buffer_end = buffr_memory_end b = (void *) (640*1024); else b = (void *) buffer_end;
判断了传入的主要内存开始的位置是否等于1MB;如果等于b
的值就置为640*1024这个位置;否则就是本身
第二部分:
struct buffer_head * h = start_buffer; void * b; int i; while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) { //BLOCK_SIZE=1024 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; h->b_prev = NULL; h->b_data = (char *) b; h->b_prev_free = h-1; h->b_next_free = h+1; h++; NR_BUFFERS++; //NR_BUFFERS = 0 if (b == (void *) 0x100000) b = (void *) 0xA0000; }
可以这是在把缓冲区分块,
很明显,b是缓冲区尾部,h为缓冲区的头部,
从尾部开始向下进行分块,一块大小为1024也就是一页,每分一块前面的缓冲区头就加一个,
并且它里面的h->b_data
就指向刚刚产生着块缓冲区,
h->b_prev_free = h-1
指向上一个头; h->b_next_free = h+1
指向下一个头
看看下面这个图吧
第三部分:
h--; free_list = start_buffer; free_list->b_prev_free = h; h->b_next_free = free_list;
这就在实现双向链表,把最后h
的下一个(next)指向第一个,把第一个的上一个(prve)指向最后一个
老规矩看图
第四部分:
struct buffer_head * hash_table[NR_HASH]; ...... for (i=0;i<NR_HASH;i++) hash_table[i]=NULL; //NR_HASH=307
这里可以看出就是把buffer_head
的结构体数组hash_table[307]
大小为307的置为空
这是数组的作用是干啥?
引用:[github的博主所讲的内容;多多支持][https://mp.weixin.qq.com/s/X8BSbf1qShS11_fzfyOhTg]
其实今天的这个代码在 buffer.c 中,而 buffer.c 是在 fs 包下的,也就是文件系统包下的。所以它今后是为文件系统而服务,具体是内核程序如果需要访问块设备中的数据,就都需要经过缓冲区来间接地操作。
也就是说,读取块设备的数据(硬盘中的数据),需要先读到缓冲区中,如果缓冲区已有了,就不用从块设备读取了,直接取走。
那怎么知道缓冲区已经有了要读取的块设备中的数据呢?从双向链表从头遍历当然可以,但是这效率可太低了。所以需要一个 hashmap 的结构方便快速查找,这就是 hash_table 这个数组的作用。