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链表的创建)