1、安装文件系统
在shell下输入mount /dev/hd1 /mnt,shell进程接到命令后,会创建一个新进程,新进程调用mount()函数,并最终映射到sys_mount函数执行
代码路径:fs/super.c
int sys_mount(char * dev_name, char * dir_name, int rw_flag)//dev_name为/dev/hd1 dir_name为/mnt
{
struct m_inode * dev_i, * dir_i;
struct super_block * sb;
int dev;
if (!(dev_i=namei(dev_name)))//获取hd1设备文件i节点
return -ENOENT;
dev = dev_i->i_zone[0];//通过i节点,获取设备号
if (!S_ISBLK(dev_i->i_mode)) {//暂不考虑
iput(dev_i);
return -EPERM;
}
iput(dev_i);//释放hd1设备文件i节点
if (!(dir_i=namei(dir_name)))//获取mnt的i节点
return -ENOENT;
if (dir_i->i_count != 1 || dir_i->i_num == ROOT_INO) {//如果mnt的节点只被引用过一次,并且不是根i节点,它才能被使用
iput(dir_i);
return -EBUSY;
}
if (!S_ISDIR(dir_i->i_mode)) {//确定mnt不是目录文件
iput(dir_i);
return -EPERM;
}
if (!(sb=read_super(dev))) {//通过设备号,读取设备的超级块
iput(dir_i);
return -EBUSY;
}
if (sb->s_imount) {//确保hd1设备的文件系统没有被安装在其他地方
iput(dir_i);
return -EBUSY;
}
if (dir_i->i_mount) {//确保mnt目录文件i节点没有安装过其他文件系统
iput(dir_i);
return -EPERM;
}
sb->s_imount=dir_i; //将超级块中s_imount与根文件系统中dir_i挂接
dir_i->i_mount=1; //dir的i节点上已经挂接了文件系统
dir_i->i_dirt=1; //dir的i节点信息已经被更改
return 0; /* we do that in umount */
}
read_super(dev),把硬盘超级块的信息读到超级块项中(struct d_super_block),硬盘i节点位图和逻辑块位图放入缓冲区,并且把地址保存在超级块中。
hd1的i节点被释放了,因为没有用了。/mnt的i节点没有被释放,因为它的数据i_mount被改变了,i_dirt被设置为1,说明会被同步到虚拟盘上。
用户进程打开一个在硬盘上已存在的文件,并读取文件的内容
void main()
{
char buffer[12000];
int fd=open("/mnt/user/user1/user2/hello.txt",O_RDWR,0644);
int size = read(fd,buffer,sizeof(buffer));
return;
}
int sys_open(const char * filename,int flag,int mode)
{
struct m_inode * inode;
struct file * f;
int i,fd;
mode &= 0777 & ~current->umask;
for(fd=0 ; fd<NR_OPEN ; fd++)
if (!current->filp[fd])//从当前进程*filp[20]中寻找空闲项
break;
if (fd>=NR_OPEN)//检查*filp[20]结构是否已经超出了使用极限
return -EINVAL;
current->close_on_exec &= ~(1<<fd);
f=0+file_table;
for (i=0 ; i<NR_FILE ; i++,f++)//在file_table[64]中寻找空闲项
if (!f->f_count) break;
if (i>=NR_FILE)
return -EINVAL;
(current->filp[fd]=f)->f_count++;//f_count同时加1
if ((i=open_namei(filename,flag,mode,&inode))<0) {//获取/mnt/user/user1/user2/hello.txt的i节点
current->filp[fd]=NULL;
f->f_count=0;
return i;
}
/* ttys are somewhat special (ttyxx major==4, tty major==5) */
if (S_ISCHR(inode->i_mode)) {
if (MAJOR(inode->i_zone[0])==4) {
if (current->leader && current->tty<0) {
current->tty = MINOR(inode->i_zone[0]);
tty_table[current->tty].pgrp = current->pgrp;
}
} else if (MAJOR(inode->i_zone[0])==5)
if (current->tty<0) {
iput(inode);
current->filp[fd]=NULL;
f->f_count=0;
return -EPERM;
}
}
/* Likewise with block-devices: check for floppy_change */
if (S_ISBLK(inode->i_mode))
check_disk_change(inode->i_zone[0]);
f->f_mode = inode->i_mode;//用该i节点属性,设置文件属性
f->f_flags = flag;//用flag参数,设置文件操作方式
f->f_count = 1;//将文件引用计数加1
f->f_inode = inode;//文件于i节点建立关系
f->f_pos = 0;
return (fd);//返回0
}
获取/mnt/user/user1/user2/hello.txt的i节点,这个节点是硬盘上的节点,不是虚拟盘上的节点,所以open_namei与之前有所不同,不同的地方获取/mnt节
点,调用iget获取/mnt节点的时候,实际上获取了硬盘设备的根i节点,具体代码如下:
代码路径:fs/inode.c
struct m_inode * iget(int dev,int nr)
{
struct m_inode * inode, * empty;
if (!dev)
panic("iget with dev==0");
empty = get_empty_inode();//从inode_table[32]中,获取空闲的i节点表项
inode = inode_table;
while (inode < NR_INODE+inode_table) {//检测指定的i节点是否已经加载过了,本案例mnt目录文件i节点就加载过
if (inode->i_dev != dev || inode->i_num != nr) {//对比设备号和i节点号是否与指定的i节点相匹配
inode++;
continue;
}
wait_on_inode(inode);//暂时不考虑
if (inode->i_dev != dev || inode->i_num != nr) {//暂时不考虑
inode = inode_table;
continue;
}
inode->i_count++;//mnt的i节点i_count加1,变成2
if (inode->i_mount) {//mnt的i节点安装了文件系统
int i;
for (i = 0 ; i<NR_SUPER ; i++)
if (super_block[i].s_imount==inode)
break;//所安装文件系统的超级块
if (i >= NR_SUPER) {
printk("Mounted inode hasn't got sb\n");
if (empty)
iput(empty);
return inode;
}
iput(inode);//mnt的i节点i_count减1,变成1
dev = super_block[i].s_dev;//硬盘的设备号
nr = ROOT_INO;//硬盘的根i节点号
inode = inode_table;//看看硬盘的设备号,硬盘的根i节点号在inode_table[32]中是否存在,本例中,并不存在,get_empty_inode已经覆盖了那个节点
continue;
}
if (empty)
iput(empty);
return inode;
}
if (!empty)
return (NULL);
inode=empty;
inode->i_dev = dev;//新的节点的设备号是硬盘的设备号
inode->i_num = nr;//新的节点的节点号是硬盘根i节点号
read_inode(inode);//给这个inode赋上值
return inode;
}
3、读文件
read(fd,buffer,sizeof(buffer),映射到sys_read执行
代码路径:fs/read_write.c
int sys_read(unsigned int fd,char * buf,int count)
{
struct file * file;
struct m_inode * inode;
if (fd>=NR_OPEN || count<0 || !(file=current->filp[fd]))//当前的文件
return -EINVAL;
if (!count)
return 0;
verify_area(buf,count);
inode = file->f_inode;//文件的i节点
if (inode->i_pipe)
return (file->f_mode&1)?read_pipe(inode,buf,count):-EIO;
if (S_ISCHR(inode->i_mode))
return rw_char(READ,inode->i_zone[0],buf,count,&file->f_pos);
if (S_ISBLK(inode->i_mode))
return block_read(inode->i_zone[0],&file->f_pos,buf,count);
if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) {
if (count+file->f_pos > inode->i_size)
count = inode->i_size - file->f_pos;
if (count<=0)
return 0;
return file_read(inode,file,buf,count);//此时只考虑这一行
}
printk("(Read)inode->i_mode=%06o\n\r",inode->i_mode);
return -EINVAL;
}
代码路径:fs/file_dev.c
int file_read(struct m_inode * inode, struct file * filp, char * buf, int count)
{
int left,chars,nr;
struct buffer_head * bh;
if ((left=count)<=0)//left为总共的长度,比如现在为4
return 0;
while (left) {
if ((nr = bmap(inode,(filp->f_pos)/BLOCK_SIZE))) {//找到该数据的逻辑块号
if (!(bh=bread(inode->i_dev,nr)))//读取一个数据块(1024个字节)
break;
} else
bh = NULL;
nr = filp->f_pos % BLOCK_SIZE;//此时f_pos为0,nr为0
chars = MIN( BLOCK_SIZE-nr , left );//此时为4
filp->f_pos += chars;//f_pos现在为4
left -= chars;//left为0
if (bh) {
char * p = nr + bh->b_data;//p为缓冲区的地址
while (chars-->0)
put_fs_byte(*(p++),buf++);//从缓冲区赋值到内存
brelse(bh);//释放缓冲区
} else {
while (chars-->0)
put_fs_byte(0,buf++);
}
}//left为0跳出循环
inode->i_atime = CURRENT_TIME;
return (count-left)?(count-left):-ERROR;//返回总的数量
}
下面我们看下bmap函数
代码路径:fs/inode.c
...
static int _bmap(struct m_inode * inode,int block,int create)
{
struct buffer_head * bh;
int i;
if (block<0)
panic("_bmap: block<0");
if (block >= 7+512+512*512)
panic("_bmap: block>big");
if (block<7) {
if (create && !inode->i_zone[block])
if ((inode->i_zone[block]=new_block(inode->i_dev))) {
inode->i_ctime=CURRENT_TIME;
inode->i_dirt=1;
}
return inode->i_zone[block];//此时读取inode->i_zone[0]里面的逻辑号
}
block -= 7;
if (block<512) {
if (create && !inode->i_zone[7])
if ((inode->i_zone[7]=new_block(inode->i_dev))) {
inode->i_dirt=1;
inode->i_ctime=CURRENT_TIME;
}
if (!inode->i_zone[7])
return 0;
if (!(bh = bread(inode->i_dev,inode->i_zone[7])))
return 0;
i = ((unsigned short *) (bh->b_data))[block];
if (create && !i)
if ((i=new_block(inode->i_dev))) {
((unsigned short *) (bh->b_data))[block]=i;
bh->b_dirt=1;
}
brelse(bh);
return i;
}
block -= 512;
if (create && !inode->i_zone[8])
if ((inode->i_zone[8]=new_block(inode->i_dev))) {
inode->i_dirt=1;
inode->i_ctime=CURRENT_TIME;
}
if (!inode->i_zone[8])
return 0;
if (!(bh=bread(inode->i_dev,inode->i_zone[8])))
return 0;
i = ((unsigned short *)bh->b_data)[block>>9];
if (create && !i)
if ((i=new_block(inode->i_dev))) {
((unsigned short *) (bh->b_data))[block>>9]=i;
bh->b_dirt=1;
}
brelse(bh);
if (!i)
return 0;
if (!(bh=bread(inode->i_dev,i)))
return 0;
i = ((unsigned short *)bh->b_data)[block&511];
if (create && !i)
if ((i=new_block(inode->i_dev))) {
((unsigned short *) (bh->b_data))[block&511]=i;
bh->b_dirt=1;
}
brelse(bh);
return i;
}
int bmap(struct m_inode * inode,int block)
{
return _bmap(inode,block,0);
}
...
请看下图:
此图为文件数据小于7块时i节点的管理示意图
文件数据大于7块,少于7+512块时i节点的管理示意图,第8个成员记录一个数据块的块号,但这个块里面存储的并不是文件数据内容,而是该文件后续512个数据块在外设的“逻辑块号”。因为一个数据块的大小为1024个字节,而每个块号需要占用两个字节,所以一个数据块能存储512个块号。
文件数据大于7+512块时i节点的管理示意图暂不说明。
用户进程在硬盘上新建一个文件,并将内容写入这个文件
void main()
{
char str1[]="Hello,world";
int fd=creat("/mnt/user/user1/user2/hello.txt",0644);
int size=write(fd,str1,strlen(str1));
close(fd);
}
4、新建文件
creat映射到sys_creat
代码路径:fs/open.c
int sys_creat(const char* pathname,in mode)
{
return sys_open(pathname,O_CREAT |O_TRUNC,mode);
}
代码路径:fs/open.c
int sys_open(const char * filename,int flag,int mode)
{
struct m_inode * inode;
struct file * f;
int i,fd;
mode &= 0777 & ~current->umask;
for(fd=0 ; fd<NR_OPEN ; fd++)
if (!current->filp[fd])
break;
if (fd>=NR_OPEN)
return -EINVAL;
current->close_on_exec &= ~(1<<fd);
f=0+file_table;
for (i=0 ; i<NR_FILE ; i++,f++)
if (!f->f_count) break;
if (i>=NR_FILE)
return -EINVAL;
(current->filp[fd]=f)->f_count++;
if ((i=open_namei(filename,flag,mode,&inode))<0) {
current->filp[fd]=NULL;
f->f_count=0;
return i;
}
/* ttys are somewhat special (ttyxx major==4, tty major==5) */
if (S_ISCHR(inode->i_mode)) {
if (MAJOR(inode->i_zone[0])==4) {
if (current->leader && current->tty<0) {
current->tty = MINOR(inode->i_zone[0]);
tty_table[current->tty].pgrp = current->pgrp;
}
} else if (MAJOR(inode->i_zone[0])==5)
if (current->tty<0) {
iput(inode);
current->filp[fd]=NULL;
f->f_count=0;
return -EPERM;
}
}
/* Likewise with block-devices: check for floppy_change */
if (S_ISBLK(inode->i_mode))
check_disk_change(inode->i_zone[0]);
f->f_mode = inode->i_mode;
f->f_flags = flag;
f->f_count = 1;
f->f_inode = inode;
f->f_pos = 0;
return (fd);
}
调用open_namei
代码路径:fs/namei.c
int open_namei(const char * pathname, int flag, int mode,
struct m_inode ** res_inode)
{
const char * basename;
int inr,dev,namelen;
struct m_inode * dir, *inode;
struct buffer_head * bh;
struct dir_entry * de;
if ((flag & O_TRUNC) && !(flag & O_ACCMODE))
flag |= O_WRONLY;
mode &= 0777 & ~current->umask;
mode |= I_REGULAR;
if (!(dir = dir_namei(pathname,&namelen,&basename)))//获取枝梢i节点
return -ENOENT;
if (!namelen) { /* special case: '/usr/' etc */
if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) {
*res_inode=dir;
return 0;
}
iput(dir);
return -EISDIR;
}
bh = find_entry(&dir,basename,namelen,&de);//通过枝梢i节点,找到目标文件hello.txt的目录项,发现不存在
if (!bh) {
if (!(flag & O_CREAT)) {//确定用户确实是要新建一个文件
iput(dir);
return -ENOENT;
}
if (!permission(dir,MAY_WRITE)) {//确定用户是否在user2目录文件中有写入权限
iput(dir);
return -EACCES;
}
inode = new_inode(dir->i_dev);//新建i节点
if (!inode) {
iput(dir);
return -ENOSPC;
}
inode->i_uid = current->euid;//设置i节点用户id
inode->i_mode = mode;//设置i节点访问模式
inode->i_dirt = 1;//将i节点已修改标志置1
bh = add_entry(dir,basename,namelen,&de);//新建目录项
if (!bh) {
inode->i_nlinks--;
iput(inode);
iput(dir);
return -ENOSPC;
}
de->inode = inode->i_num;//设置新目录项的inode
bh->b_dirt = 1;//又设置了一遍,目录项所在的缓冲区b_dirt为1
brelse(bh);//释放缓冲区
iput(dir);//释放枝梢i节点
*res_inode = inode;
return 0;
}
....
return 0;
}
调用new_inode创建新的节点(hello.txt)
代码路径:fs/bitmap.c
...
struct m_inode * new_inode(int dev)
{
struct m_inode * inode;
struct super_block * sb;
struct buffer_head * bh;
int i,j;
if (!(inode=get_empty_inode()))//在inode_table[32]中获取空闲i节点项
return NULL;
if (!(sb = get_super(dev)))//获取设备超级块
panic("new_inode with unknown device");
j = 8192;
for (i=0 ; i<8 ; i++)
if ((bh=sb->s_imap[i]))
if ((j=find_first_zero(bh->b_data))<8192)
break;
if (!bh || j >= 8192 || j+i*8192 > sb->s_ninodes) {
iput(inode);
return NULL;
}
if (set_bit(j,bh->b_data))//以上是根据超级块中i节点位图信息,设置i节点位图
panic("new_inode: bit already set");
bh->b_dirt = 1;//i节点位图的缓冲区b_dirt为1
inode->i_count=1;//i_count为1
inode->i_nlinks=1;//链接数为1
inode->i_dev=dev;//设备号
inode->i_uid=current->euid;
inode->i_gid=current->egid;
inode->i_dirt=1;//i节点b_dirt为1
inode->i_num = j + i*8192;//i节点号
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
return inode;
}
...
目前hello.txt的i节点(内存中)和i节点位图(缓冲区中)都已经更新。
调用add_entry,新建目录项
代码路径:fs/namei.c
static struct buffer_head * add_entry(struct m_inode * dir,
const char * name, int namelen, struct dir_entry ** res_dir)
{
int block,i;
struct buffer_head * bh;
struct dir_entry * de;
*res_dir = NULL;
....
if (!(block = dir->i_zone[0]))//确定user2目录文件第一个文件块在设备上的逻辑块号
return NULL;
if (!(bh = bread(dir->i_dev,block)))//将目录文件的内容放入缓冲区
return NULL;
i = 0;
de = (struct dir_entry *) bh->b_data;//赋值给目录项指针
while (1) {
if ((char *)de >= BLOCK_SIZE+bh->b_data) {//暂不考虑
brelse(bh);
bh = NULL;
block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK);
if (!block)
return NULL;
if (!(bh = bread(dir->i_dev,block))) {
i += DIR_ENTRIES_PER_BLOCK;
continue;
}
de = (struct dir_entry *) bh->b_data;
}
if (i*sizeof(struct dir_entry) >= dir->i_size) {//暂不考虑
de->inode=0;
dir->i_size = (i+1)*sizeof(struct dir_entry);
dir->i_dirt = 1;
dir->i_ctime = CURRENT_TIME;
}
if (!de->inode) {//在数据块的中间某位置找到空闲项,就在该位置加载目录项
dir->i_mtime = CURRENT_TIME;
for (i=0; i < NAME_LEN ; i++)
de->name[i]=(i<namelen)?get_fs_byte(name+i):0;//将名字hello.txt存入目录项的结构中
bh->b_dirt = 1;//目录项对应的缓冲块b_dirt为1
*res_dir = de;//返回目录项的引用
return bh;
}
de++;
i++;
}
brelse(bh);
return NULL;
}
此刻,目录项所在的缓冲区已经更新。
总结:新建文件,建立了hello.txt的i节点,并设置了i节点位图,然后更新的目录项。i节点信息存放在inode_table(没有释放,i_dirt为1)中,i节点位图(常驻缓冲区,b_dirt为1)和目录项(已经释放,但是b_dirt为1)存放在内存缓冲区中。
最后别忘了,还会返回到sys_open继续执行,设置f_pos等参数。
5、写文件
write(fd,str1,strlen(str1))映射到sys_write来执行
代码路径:fs/read_write.c
int sys_write(unsigned int fd,char * buf,int count)
{
struct file * file;
struct m_inode * inode;
if (fd>=NR_OPEN || count <0 || !(file=current->filp[fd]))//获取了file
return -EINVAL;
if (!count)
return 0;
inode=file->f_inode;//获取了inode
if (inode->i_pipe)
return (file->f_mode&2)?write_pipe(inode,buf,count):-EIO;
if (S_ISCHR(inode->i_mode))
return rw_char(WRITE,inode->i_zone[0],buf,count,&file->f_pos);
if (S_ISBLK(inode->i_mode))
return block_write(inode->i_zone[0],&file->f_pos,buf,count);
if (S_ISREG(inode->i_mode))
return file_write(inode,file,buf,count);//执行这句话
printk("(Write)inode->i_mode=%06o\n\r",inode->i_mode);
return -EINVAL;
}
代码路径:fs/file_dev.c
int file_write(struct m_inode * inode, struct file * filp, char * buf, int count)
{
off_t pos;
int block,c;
struct buffer_head * bh;
char * p;
int i=0;
/*
* ok, append may not work when many processes are writing at the same time
* but so what. That way leads to madness anyway.
*/
if (filp->f_flags & O_APPEND)
pos = inode->i_size;
else
pos = filp->f_pos;//此时为0
while (i<count) {
if (!(block = create_block(inode,pos/BLOCK_SIZE)))//inode为hello.txt的i节点,block为0
break;
if (!(bh=bread(inode->i_dev,block)))//返回新建的逻辑块对应的缓冲区
break;
c = pos % BLOCK_SIZE;//此时为0
p = c + bh->b_data;
bh->b_dirt = 1;//逻辑块对应的缓冲区b_dirt为1
c = BLOCK_SIZE-c;//1024
if (c > count-i) c = count-i;//c为4
pos += c;//pos为4
if (pos > inode->i_size) {
inode->i_size = pos;
inode->i_dirt = 1;
}
i += c;//i为4
while (c-->0)
*(p++) = get_fs_byte(buf++);//把buf中的数据拷贝到缓冲区
brelse(bh);//释放缓冲区
}
inode->i_mtime = CURRENT_TIME;
if (!(filp->f_flags & O_APPEND)) {//不执行
filp->f_pos = pos;
inode->i_ctime = CURRENT_TIME;
}
return (i?i:-1);//返回4
}
会执行creat_block
代码路径:fs/inode.c
int create_block(struct m_inode * inode, int block)
{
return _bmap(inode,block,1);
}
代码路径:fs/inode.c
static int _bmap(struct m_inode * inode,int block,int create)
{
struct buffer_head * bh;
int i;
if (block<0)
panic("_bmap: block<0");
if (block >= 7+512+512*512)
panic("_bmap: block>big");
if (block<7) {
if (create && !inode->i_zone[block])
if ((inode->i_zone[block]=new_block(inode->i_dev))) {inode->i_zone[0]被设置为新的逻辑块的地址
inode->i_ctime=CURRENT_TIME;
inode->i_dirt=1;
}
return inode->i_zone[block];
}
....
}
下面创建新的逻辑块,new_block
代码路径:fs/bitmap.c
int new_block(int dev)
{
struct buffer_head * bh;
struct super_block * sb;
int i,j;
if (!(sb = get_super(dev)))//获取设备的超级块
panic("trying to get new block from nonexistant device");
j = 8192;
for (i=0 ; i<8 ; i++)
if ((bh=sb->s_zmap[i]))
if ((j=find_first_zero(bh->b_data))<8192)
break;
if (i>=8 || !bh || j>=8192)
return 0;
if (set_bit(j,bh->b_data))//以上是根据超级块中逻辑块位图信心,对新数据块的逻辑块位图进行设置
panic("new_block: bit already set");
bh->b_dirt = 1;//逻辑块位图的缓冲区b_dirt为1
j += i*8192 + sb->s_firstdatazone-1;//逻辑块号
if (j >= sb->s_nzones)
return 0;
if (!(bh=getblk(dev,j)))//根据设备号和逻辑块号,申请一个空闲缓冲区
panic("new_block: cannot get block");
if (bh->b_count != 1)
panic("new block: count is != 1");
clear_block(bh->b_data);//将刚申请的缓冲块中数据清零
bh->b_uptodate = 1;//已更新标志设置为1
bh->b_dirt = 1;//刚申请的缓冲区b_dirt为1
brelse(bh);//释放刚申请的缓冲区
return j;
}
返回到file_write继续执行。
总结:新建文件,建立了hello.txt的i节点,并设置了i节点位图,然后更新的目录项。i节点信息存放在inode_table(没有释放,i_dirt为1)中,i节点位图(常驻缓冲区,b_dirt为1)和目录项(已经释放,但是b_dirt为1)存放在内存缓冲区中。
总结:写文件,建立了新的逻辑块,并往里拷贝了要存入硬盘的数据,设置了逻辑块位图,也设置了hello.txt的i节点的i_zone[0]。逻辑块信息(已经释放,但b_dirt为1)存放在内存缓冲区,逻辑块位图(常驻缓冲区,b_dirt为1)存放在内存缓冲区。
数据从缓冲区同步到硬盘有两种方法。一种是updata定期同步,另一种是因缓冲区使用达到极限,操作系统强行同步。
具体代码如下
代码路径:fs/buffer.c
int sys_sync(void)
{
int i;
struct buffer_head * bh;
sync_inodes(); /* write out inodes into buffers */
bh = start_buffer;
for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
wait_on_buffer(bh);//如果哪个缓冲区正在使用,就等这个缓冲区解锁
if (bh->b_dirt)//如果b_dirt为1
ll_rw_block(WRITE,bh);//将该缓冲块的内容同步到外设
}
return 0;
}
代码路径:fs/inode.c
void sync_inodes(void)
{
int i;
struct m_inode * inode;
inode = 0+inode_table;
for(i=0 ; i<NR_INODE ; i++,inode++) {//遍历所有i节点
wait_on_inode(inode);
if (inode->i_dirt && !inode->i_pipe)//如果i节点的内容已经被修改过
write_inode(inode);//将i节点同步到缓冲区
}
}
代码路径:fs/inode.c
static void write_inode(struct m_inode * inode)
{
struct super_block * sb;
struct buffer_head * bh;
int block;
lock_inode(inode);//先将i节点加锁,以免被干扰
if (!inode->i_dirt || !inode->i_dev) {
unlock_inode(inode);
return;
}
if (!(sb=get_super(inode->i_dev)))
panic("trying to write inode without device");
block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +
(inode->i_num-1)/INODES_PER_BLOCK;//i节点所在的逻辑块号
if (!(bh=bread(inode->i_dev,block)))//将i节点所在逻辑块载入缓冲区
panic("unable to read i-node block");
((struct d_inode *)bh->b_data)//复制数据
[(inode->i_num-1)%INODES_PER_BLOCK] =
*(struct d_inode *)inode;
bh->b_dirt=1;//缓冲区b_dirt为1
inode->i_dirt=0;//所以inode的i_dirt为0
brelse(bh);//释放缓冲区
unlock_inode(inode);//解锁
}
i节点的信息也被放到了缓冲区来同步。
到目前位置,所有的更新的信息都放入了缓冲区,i节点位图缓冲区内容放入硬盘的i节点位图区域,逻辑块位图缓冲区放入了硬盘逻辑块位图区域,i节点缓冲区放到了硬盘i节点区域,逻辑块缓冲区(目录项和要写的数据)放到了硬盘逻辑块区域。
还有一种情况当缓冲区已满的时候,也会同步到外设。
代码路径:fs/buffer.c
struct buffer_head * getblk(int dev,int block)
{
struct buffer_head * tmp, * bh;
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);//找到空闲的缓冲块(不等价于b_dirt是0)
if (!bh) {
sleep_on(&buffer_wait);
goto repeat;
}
wait_on_buffer(bh);
if (bh->b_count)
goto repeat;
while (bh->b_dirt) {/虽然找到了空闲缓冲块,但b_dirt仍是1,说明缓冲区中已无可用的缓冲块,需要同步腾空
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);
bh->b_dev=dev;
bh->b_blocknr=block;
insert_into_queues(bh);
return bh;
}
6、关闭文件
close会映射到sys_close执行
代码路径:fs/open.c
int sys_close(unsigned int fd)
{
struct file * filp;
if (fd >= NR_OPEN)
return -EINVAL;
current->close_on_exec &= ~(1<<fd);
if (!(filp = current->filp[fd]))//获取filp
return -EINVAL;
current->filp[fd] = NULL;//清空
if (filp->f_count == 0)
panic("Close: file count is 0");
if (--filp->f_count)//f_count为0
return (0);
iput(filp->f_inode);//由于f_count为0,释放hello.txt节点
return (0);
}
代码路径:fs/inode.c
void iput(struct m_inode * inode)
{
if (!inode)
return;
wait_on_inode(inode);
...
repeat:
if (inode->i_count>1) {此时i_count为1
inode->i_count--;
return;
}
if (!inode->i_nlinks) {//此时i_nlinks为1
truncate(inode);
free_inode(inode);
return;
}
if (inode->i_dirt) {//如果还没有同步,那就同步
write_inode(inode); /* we can sleep - so do again */
wait_on_inode(inode);
goto repeat;
}
inode->i_count--;//i_count减为0
return;
}
7、删除文件
最后映射到sys_unlink,取消链接。
代码路径:fs/namei.c
int sys_unlink(const char * name)
{
const char * basename;
int namelen;
struct m_inode * dir, * inode;
struct buffer_head * bh;
struct dir_entry * de;
if (!(dir = dir_namei(name,&namelen,&basename)))//获取枝梢i节点
return -ENOENT;
...
bh = find_entry(&dir,basename,namelen,&de);//获取目标文件的目录项
if (!bh) {
iput(dir);
return -ENOENT;
}
if (!(inode = iget(dir->i_dev, de->inode))) {//获取要删除文件的i节点
iput(dir);
brelse(bh);
return -ENOENT;
}
...
if (!inode->i_nlinks) {//目前为1
printk("Deleting nonexistent file (%04x:%d), %d\n",
inode->i_dev,inode->i_num,inode->i_nlinks);
inode->i_nlinks=1;
}
de->inode = 0;//目录项中标记着hello.txt的节点号清0
bh->b_dirt = 1;//该目录项b_dirt为1,等待同步
brelse(bh);//释放该缓冲块
inode->i_nlinks--;//i_nlicks等于0
inode->i_dirt = 1;//i_dirt为1
inode->i_ctime = CURRENT_TIME;
iput(inode);//释放hello.txt的i节点,由于i_nlinks为0,所以要调用truncate,free_inode
iput(dir);//释放枝梢i节点,由于i_nlinks不为0,只是把i_count设置为0
return 0;
}
调用iput来释放节点,仔细阅读。
代码路径:fs/inode.c
void iput(struct m_inode * inode)
{
if (!inode)
return;
wait_on_inode(inode);
if (!inode->i_count)
panic("iput: trying to free free inode");
...
repeat:
if (inode->i_count>1) {//i_count为1
inode->i_count--;
return;
}
if (!inode->i_nlinks) {
truncate(inode);
free_inode(inode);
return;//memset(inode,0,sizeof(*inode));所以i_count不用再清0了
}
if (inode->i_dirt) {
write_inode(inode); /* we can sleep - so do again */
wait_on_inode(inode);
goto repeat;
}
inode->i_count--;
return;
}
执行truncate函数
代码路径:fs/truncate.c
void truncate(struct m_inode * inode)
{
int i;
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
return;
for (i=0;i<7;i++)
if (inode->i_zone[i]) {
free_block(inode->i_dev,inode->i_zone[i]);
inode->i_zone[i]=0;
}
free_ind(inode->i_dev,inode->i_zone[7]);
free_dind(inode->i_dev,inode->i_zone[8]);
inode->i_zone[7] = inode->i_zone[8] = 0;
inode->i_size = 0;
inode->i_dirt = 1;
inode->i_mtime = inode->i_ctime = CURRENT_TIME;
}
上面一段函数,目的是清空hello.txt的逻辑块对应的逻辑块位图所在的缓冲块对应的位
又执行free_inode
void free_inode(struct m_inode * inode)
{
struct super_block * sb;
struct buffer_head * bh;
if (!inode)
return;
if (!inode->i_dev) {//如果i节点为空
memset(inode,0,sizeof(*inode));
return;
}
if (inode->i_count>1) {//如果设备号为0
printk("trying to free inode with count=%d\n",inode->i_count);
panic("free_inode");
}
if (inode->i_nlinks)//如果i节点被多次引用
panic("trying to free inode with links");
if (!(sb = get_super(inode->i_dev)))//如果i节点所在文件系统的超级块不存在
panic("trying to free inode on nonexistent device");
if (inode->i_num < 1 || inode->i_num > sb->s_ninodes)//检查i节点号
panic("trying to free inode 0 or nonexistant inode");
if (!(bh=sb->s_imap[inode->i_num>>13]))//如果该i节点对应的i节点位图不存在
panic("nonexistent imap in superblock");
if (clear_bit(inode->i_num&8191,bh->b_data))//清空i节点位图中与hello.txt文件i节点对应的位
printk("free_inode: bit already cleared.\n\r");
bh->b_dirt = 1;//i节点位图所在缓冲块的b_dirt为1
memset(inode,0,sizeof(*inode));//将i节点表中hello.txt文件i节点的表项清零
}
上面一段函数,目的是清空hello.txt的i节点对应的i节点位图所在的缓冲块对应的位,同时将i节点表中hello.txt文件i节点的表项清零。
总结:删除文件,本质是根据hello.txt清空超级块i节点位图,逻辑块位图所在缓冲块对应的位,上一次目录对应的目录项所在缓冲块也要清空,并且将i节点表中hello.txt文件i节点的表项清零,这些缓冲块的i_dirt均为1,等待同步。i节点和逻辑块在硬盘的内容放那就行,已经用不上了。