linux0.11源码分析-缓冲区

361 阅读3分钟

缓冲区的作用是提高系统效率,相对CPU来说,低速的IO设备是CPU受不了的,那就把IO设备中的数据先读取到缓冲区中,然后再复制到用户空间,如果想写数据到IO设备,进程先修改自己空间中的数据(用户空间),然后复制到缓冲区,然会就会等待系统同步数据到IO了。那么我们使用的open、read函数就是从磁盘数据到缓冲区然后到用户空间,经过了2次的复制。后面我们学习内存映射,直接把磁盘数据映射到用户空间,数据只需要一次复制,进一步提高IO效率。

buffer_init(buffer_memory_end);

linux0.11最大支持16MB的内存大小,假设现在内存就是16MB,那么缓存区的末端就在4MB的地址地方。 变量end是由编译时的连接程序ld生成,用于表明内核代码的末端。

include/linux/fs.h

#define BLOCK_SIZE 1024 //块大小

#define NR_HASH 307

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;
	struct buffer_head * b_prev;
	struct buffer_head * b_next;
	struct buffer_head * b_prev_free;
	struct buffer_head * b_next_free;
};

b_uptodate表示该缓冲区的中的数据已经是最新的了。进程可以使用该缓冲中的数据,否咋就是垃圾。b_dirt表示该缓冲区的数据已经被修改,与磁盘中的数据不一致,需要同步到磁盘。

fs/buffer.c

extern int end;

struct buffer_head * start_buffer = (struct buffer_head *) &end;

static struct buffer_head * free_list;              // 空闲缓冲块链表头指针

struct buffer_head * hash_table[NR_HASH];           // NR_HASH = 307项

...

void buffer_init(long buffer_end)
{
	struct buffer_head * h = start_buffer;
	void * b;
	int i;

    // 首先根据参数提供的缓冲区高端位置确定实际缓冲区高端位置b。如果缓冲区高端等于1MB,
    // 则因为从640KB - 1MB被显示内存和BIOS占用,所以实际可用缓冲区内存高端位置应该是
    // 640KB。否则缓冲区内存高端一定大于1MB。
	if (buffer_end == 1<<20)
		b = (void *) (640*1024);
	else
		b = (void *) buffer_end;
 
	while ( (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;
}

buffer_init就是创建一些缓冲块,每个大小1024字节