linux内核笔记六 文件系统Linux对文件系统的支持层解析

1、linux中使用文件系统分几个部分

1.1 有关于linux中高速缓冲区的管理程序。

分页机制。
缺页中断就是刚开始只载入了比如两页,然后用到了第三页就会有一个异常,导致一个中断。 buffer.c

1.2 文件系统底层通用函数(对于硬盘的读写 分配 释放等,对于目录的节点管理等 inode,内存与磁盘的映射)
1.3 对文件数据进行读写操作的代码(VFS 虚拟文件系统 把一个设备当成文件读取 硬件驱动和文件系统的关系 pipe 块设备的读取(磁盘光驱等))
1.4 文件系统与其他程序的接口实现(fopen 关闭 常见等使用文件的调用方式)

2、文件系统的基本概念

磁盘中要有目录的映射,我们把磁盘分成盘片
每一个盘片都有一个文件系统的子系统(章节目录)

引导块:用来引导设备的(也就是告诉你我这个盘片怎么怎么滴),引导块可以为空,但是一定要空开,因为要保持格式
超级块:(作用非常的巨大)是该文件子系统的描述符(记录该盘片的逻辑块位图的地址,i节点位图的地址,通过设备号可以获取)
逻辑块位图: 其每一位对应一个逻辑块的使用情况(逻辑块没占用对应的位就变成1)
i节点位图:跟逻辑块位图一样,其每一位对应一个i节点的使用情况。
逻辑块:用来存储数据的数据存储单元
i节点: 目录与磁盘的桥接,文件的属性描述(文件的名片)

在这里插入图片描述
fs.h

inode结构体

struct m_inode {
	unsigned short i_mode;   //文件的类型和属性
	unsigned short i_uid;    //宿主用户ID
	unsigned long i_size;    //该文件的大小
	unsigned long i_mtime;   //该文件的修改时间
	unsigned char i_gid;     //宿主的组ID
	unsigned char i_nlinks;  //链接数  硬链接
	unsigned short i_zone[9];//该文件映射在逻辑块的号的数组
/* these are in memory also */
	struct task_struct * i_wait;
	unsigned long i_atime;
	unsigned long i_ctime;
	unsigned short i_dev;
	unsigned short i_num;
	unsigned short i_count;
	unsigned char i_lock;
	unsigned char i_dirt;
	unsigned char i_pipe;
	unsigned char i_mount;
	unsigned char i_seek;
	unsigned char i_update;
};

crw-rw 文件的类型和属性
c就代表文件类型
-代表文件类型
d 目录
s 符号链接
p pipe管道
c 字符设备
b 块设备
l 链接文件

每个文件有三种属性 r w x执行
777代表全属性 可读可写可执行
每一个文件有三个属主的属性
777--------rwxrwxrwx
第一个rwx是当前用户的属性
第二个rwx是用户组的权限
第三个rwx是他人的权限

i_zone 文件和磁盘的映射
izone的前七个是直接块号 如果文件只占用了7个文件块,那么这个数组中的每一个单元则存储了一个逻辑块的号
izone[8] 一次间接块号 如果占用逻辑块较多, 大于7 小于512+7 则占用一次间接块号
izone[9]二次间接块号 如果占用的逻辑块太多 大于512+7 小于512 * 512+7 则占用二次间接号

设备号 超级块 逻辑块位图
超级快 i节点位图 i节点信息

3、高速缓冲区

在这里插入图片描述

高速缓冲区的管理要素
1、映射关系(内存)
2、应用程序与高速缓冲区的交互API
3、高速缓冲区与磁盘的交互API
4、高速缓冲区的管理系统(循环链表+哈希表+单链表)
eg:应用程序要从某一块磁盘上读一些数据或者写一些数据
在这里插入图片描述

4、高速缓冲区的管理机制

1、目的
用户与磁盘文件交互时 它的流程
磁盘与高速缓冲区的关系
加深块设备驱动的理解
hash 循环链表 单链表的使用方法

2、高速缓冲区的工作流程
高速缓冲区中存储着对应的块设备驱动的数据
当从块设备中读取数据的时候,OS会从高速缓冲区中进行检索,如果没有则从块设备中读出数据,如果有并且时最新的,就直接和高速缓冲区进行数据交互。

buffer.c

在这里插入图片描述

高速缓冲区中有缓冲区低区和高区
低区和高区中的缓冲块一一对应
缓冲区的描述头-------高速缓冲区

高速缓冲区的头是?
高速缓冲区的头是一个结构体,里面存着高速缓冲区所有的状态 时间 信息

关键结构体
buffer_head

在fs.h中

struct buffer_head {
	char * b_data;			/* pointer to data block (1024 bytes) */   //数据  1024比特
	unsigned long b_blocknr;	/* block number */     //数据逻辑块号   1k
	unsigned short b_dev;		/* device (0 = free) */   //块设备号  1k
	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;
};

几个链表结构定义帮助理解

struct buffer_head * start_buffer = (struct buffer_head *) &end;
struct buffer_head * hash_table[NR_HASH];
static struct buffer_head * free_list;
static struct task_struct * buffer_wait = NULL;

struct buffer_head * b_prev_free;
struct buffer_head * b_next_free;
构成了空闲缓冲区的循环链表
当前高速缓冲区中所有剩余的没有用到的缓冲区的循环链表

空闲的缓冲区链表

在这里插入图片描述

有效的缓冲区链表

在这里插入图片描述
我们来写一个buffer.c
分配一个buffer_head
设置该buffer_head指向空闲缓冲区的一个有效buffer
更新该buffer_head里面的所有设置项
把该buffer_head从空闲中出链
算出该buffer_head对饮的散列序号并把它加入到对应的链表尾部

看看它对应的代码长啥样

#define BADNESS(bh) (((bh)->b_dirt<<1)+(bh)->b_lock)
//算一个比重值  也就是优先级的一个算法  就是一会先拿这个缓冲区出来
struct buffer_head * getblk(int dev,int block)
{
	struct buffer_head * tmp, * bh;    //定义一个buffer_head结构体指针

repeat:
	if (bh = get_hash_table(dev,block))    
		return bh;
	tmp = free_list;
	do {
		if (tmp->b_count)
			continue;
		if (!bh || BADNESS(tmp)<BADNESS(bh)) {
			bh = tmp;
			if (!BADNESS(tmp))
				break;
		}
/* and repeat until we find something good */
	} while ((tmp = tmp->b_next_free) != free_list);
	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;
	}
/* NOTE!! While we slept waiting for this block, somebody else might */
/* already have added "this" block to the cache. check it */
	if (find_buffer(dev,block))
		goto repeat;
/* OK, FINALLY we know that this buffer is the only one of it's kind, */
/* and that it's unused (b_count=0), unlocked (b_lock=0), and clean */
	bh->b_count=1;
	bh->b_dirt=0;
	bh->b_uptodate=0;
	remove_from_queues(bh);  //删除节点从HASH和双向链表
	bh->b_dev=dev;
	bh->b_blocknr=block;
	insert_into_queues(bh);  //添加节点从
	return bh;
}

get_hash_block

struct buffer_head * get_hash_table(int dev, int block)
{
	struct buffer_head * bh;

	for (;;) {
		if (!(bh=find_buffer(dev,block)))
			return NULL;
		bh->b_count++;
		wait_on_buffer(bh);
		if (bh->b_dev == dev && bh->b_blocknr == block)
			return bh;
		bh->b_count--;
	}
}

getblk里面有get_hash_block。然后里面又有find_buffer

static struct buffer_head * find_buffer(int dev, int block)
{		
	struct buffer_head * tmp;

	for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next)
		if (tmp->b_dev==dev && tmp->b_blocknr==block)
			return tmp;
	return NULL;
}

dev 设备号 block 逻辑号

所以fing_buffer做的事情就是用你计算出来的号去找,然后找到拿出来就好啦。

找到之后计数去加
get_hash_block主要是有一个多线程的操作。
用的是wait_on_buffer

没找到就搜索空闲缓冲队列,找合适的块
找不到就在等待进程中就睡了

找到就再次看一下是不是有残余数据
如果有这样的数据,就写盘,也就是回写同步
sync_dev 磁盘写函数

int sync_dev(int dev)
{
	int i;
	struct buffer_head * bh;

	bh = start_buffer;
	for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
		if (bh->b_dev != dev)
			continue;
		wait_on_buffer(bh);
		if (bh->b_dev == dev && bh->b_dirt)
			ll_rw_block(WRITE,bh);    //底层的块设备读写函数
	}
	sync_inodes();
	bh = start_buffer;
	for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
		if (bh->b_dev != dev)
			continue;
		wait_on_buffer(bh);
		if (bh->b_dev == dev && bh->b_dirt)
			ll_rw_block(WRITE,bh);
	}
	return 0;
}

在这里插入图片描述

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);
}

释放指定的高速缓冲区

这是一个释放块的过程
步骤:
传入头部,等待解锁,count–,唤醒。

struct buffer_head * bread(int dev,int block)
{
	struct buffer_head * bh;

	if (!(bh=getblk(dev,block)))
		panic("bread: getblk returned NULL\n");
	if (bh->b_uptodate)
		return bh;
	ll_rw_block(READ,bh);
	wait_on_buffer(bh);
	if (bh->b_uptodate)
		return bh;
	brelse(bh);
	return NULL;
}

从设备上读取指定的数据块,并返回含有数据的高速缓冲区。
在这里插入图片描述

ll_rw_block(READ,bh);
这个是底层块设备驱动那一层的
这个函数其实是通用的块设备读写函数的接口
下面还有函数

#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")

从from的地方拷贝到to的地方

void bread_page(unsigned long address,int dev,int b[4])
{
	struct buffer_head * bh[4];
	int i;

	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;
	for (i=0 ; i<4 ; i++,address += BLOCK_SIZE)
		if (bh[i]) {
			wait_on_buffer(bh[i]);
			if (bh[i]->b_uptodate)
				COPYBLK((unsigned long) bh[i]->b_data,address);
			brelse(bh[i]);
		}
}

前面那个bread是一次一块,这个是一次四块

最后一个buffer_init

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

	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;
		h->b_prev = NULL;
		h->b_data = (char *) b;
		h->b_prev_free = h-1;
		h->b_next_free = h+1;
		h++;
		NR_BUFFERS++;
		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++)
		hash_table[i]=NULL;
}	

高速缓冲区的初始化程序
(空闲缓冲区双向链表的创建和hash链表的创建)

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值