首页 > 其他 > 详细

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

时间:2014-03-09 23:58:42      阅读:920      评论:0      收藏:0      [点我收藏+]

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);
}
...
        请看下图:

bubuko.com,布布扣

        此图为文件数据小于7块时i节点的管理示意图

       bubuko.com,布布扣

       文件数据大于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));
}


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节点区域,逻辑块缓冲区(目录项和要写的数据)放到了硬盘逻辑块区域。

        


  






        
 


















Linux内核设计的艺术-文件操作,布布扣,bubuko.com

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

原文:http://blog.csdn.net/jltxgcy/article/details/20833861

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!