Linux内核设计的艺术-文件操作

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



2、 打开文件,open会映射到sys_open,代码路径: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])//从当前进程*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节点和逻辑块在硬盘的内容放那就行,已经用不上了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值