磁盘的管理(二)


前言


如果让普通用户使用生磁盘(raw disk),许多人连扇区都不知道是什么?要求他们根据盘块号来访问磁盘…这是不可能的。
所以,需要在盘块上引入更高一层次的抽象概念—文件

提示:以下是本篇文章正文内容

一、磁盘文件

磁盘使用的第三层抽象——文件,文件是一个连续的字符流
用户可以在字符流上随意操作,操作系统会根据映射表找到和字符流位置对应的磁盘块号,操作系统完成了从磁盘块到字符流的映射。

实现文件抽象的关键就在于能根据字符流位置找到对应的盘块号,即字符流和盘块号之间的映射关系,文件: 建立字符流到盘块集合的映射关系

1.顺序存储结构

文件使用顺序结构储存在磁盘上,文件的FCB(文件控制块)存储该文件的起始块号(第一个),和块数,根据这个就能知道对应的字符在那个盘块
在这里插入图片描述
在这里插入图片描述
若盘块的大小为100,则文件中200-212对应在盘块8

但是,用顺序存储的结构适合文件的直接读写,不适合文件的动态增长,与数组一样,不方便插入元素

2.链式存储结构

链式存储结构:操作系统在 FCB 中需要存放的主要映射信息是第一个磁盘块的盘块号,利用这个信息可以找到文件的第一个磁盘块,再利用每个磁盘块中存放的下一个盘块号的指针,可以找到第二个磁盘块……
在这里插入图片描述
若盘块的大小为100,则文件中200-212对应在盘块9

3.索引存储结构

索引存储结构:文件字符流被分割成多个逻辑块,在物理磁盘上寻找一些空闲物理盘块(无需连续)将这些逻辑块的内容存放进去,再找一个磁盘块作为索引块,其中按序存放各个逻辑块对应的物理磁盘块号(索引块来记录文件使用的盘块号)
在这里插入图片描述
索引结构指一个文件的信息存放在若干不连续的物理块中,系统为每个文件建立一个专用的数据结构——索引表,并将这些块的块号存放在索引表中。

优点是保留了链接结构的优点,同时解决了其缺点,即能顺序存取,又能随机存取,满足了文件动态增长,插入删除的需求,也能充分利用外存空间

缺点是索引表本身带来一定的系统开销

多级索引:
在这里插入图片描述

优点:

1.可以表示很大的文件
2.很小的文件高效访问
3.中等大小的文件访问速度也不慢

二、文件读取磁盘(第三层抽象)

通过文件对磁盘进行读写

在fs/read_write.c中
int sys_write(int fd, const char* buf, int count)
{
    
     
	struct file *file = current->filp[fd];
	struct m_inode *inode = file->inode;
	if(S_ISREG(inode->i_mode))
	return file_write(inode, file, buf, count);
}

既然对文件操作就要调用sys_write(),参数:fd文件描述符,buf内存缓冲区,count读写字符的个数
在这里插入图片描述

根据文件信息 inode 对应的不是字符设备,而是常规文件,跳到 file_write() 去执行

file_write()

int file_write(struct m_inode *inode, struct file *filp, char *buf, int count)
{
    
     
	off_t pos;
	if(filp->f_flags&O_APPEND)//如果是追加,从文末开始
	pos=inode->i_size; else pos=filp->f_pos;
	.....
	while(i<count)
	{
    
    
		block=create_block(inode, pos/BLOCK_SIZE);//算出对应的块
		bh=bread(inode->i_dev, block);//发送请求,放入“电梯” 队列!
		int c=pos%BLOCK_SIZE; char *p=c+bh->b_data; //写入数据后,修改修改pos,
		bh->b_dirt=1; c=BLOCK_SIZE-c; pos+=c; // pos指向文件的读写位置(字符流末位置)
		... 
		//一块一块拷贝用户字符, 并且释放写出
		while(c-->0) *(p++)=get_fs_byte(buf++);
		brelse(bh); 
	}
	filp->f_pos=pos; 
}
//pos    先找到 文件的读写位置 (记录在 字段 f_pos 中)
//block  计算物理盘块号
//bread   向磁盘发出请求

工作过程:

1.根据file中的一个读写指针fseek(文件的当前读写位置)及 count 找到文件读写对应的字符流位置

2.根据 字符流上的读写位置 算出逻辑块号 ,由inode 找到物理盘块号

3.用磁盘号,buf等形成request放入请求队列(“电梯”),就可以读写磁盘

create_block()计算盘号,文件抽象的核心

在这里插入图片描述
这里采用的多级索引
block:(0-6):直接数据块(直接索引), (7):一重间接, (8):二重间接

如果逻辑盘块号小于等于 6,说明 inode中的直接数据块就能映射出盘块号
若这个逻辑盘块没有映射到物理盘块,就调用 new_block() 从磁盘上申请一个空闲物理盘块

block-=7 , if(block<512) 说明逻辑盘块号对应的物理盘块号存放在一阶间接索引,接下来需要 bread(inode->i_dev,inode->i_zone[7]) 读入这个一阶索引块,接下来需要在这个索引块中寻找和逻辑块相对应的物理盘块号

m_inode结构体

struct m_inode{
    
     //读入内存后的inode
	unsigned short i_mode; //文件的类型和属性
	...
	unsigned short i_zone[9]; //指向文件内容数据块
	struct task_struct *i_wait;
	unsigned short i_count;
	unsigned char i_lock;
	unsigned char i_dirt; 
	... 
}

根据inode区分文件的属性和类型

int sys_open(const char* filename, int flag)
{
    
     
	if(S_ISCHR(inode->i_mode)) //字符设备
	{
    
     
		if(MAJOR(inode->i_zone[0])==4)  //设备文件
		current->tty=MINOR(inode->i_zone[0]);
	}
	...
}

如果是普通文件需要根据inode里面的映射表来建立磁盘号到字符流直接的映射
如果是特殊文件不需要inode去完成映射,inode存放主设备号(设备文件)

MAJOR的宏定义

#define MAJOR(a)(((unsigned)(a))>>8)) //取高字节
#define MINOR(a)((a)&0xff) //取低字节

总结

提示:这里对文章进行总结:

文件视图
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_53144843/article/details/120607053