操作系统|磁盘的管理(二)


文章目录

  • 前言
  • 一、磁盘文件
    • 1.顺序存储结构
    • 2.链式存储结构
    • 3.索引存储结构
  • 二、文件读取磁盘(第三层抽象)
  • 总结

前言 【操作系统|磁盘的管理(二)】
如果让普通用户使用生磁盘(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(ii_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) //取低字节

总结 提示:这里对文章进行总结:
文件视图
操作系统|磁盘的管理(二)
文章图片

    推荐阅读