Linux-0.11内核分析05:文件操作

文件操作

1. 安装文件系统

// 示例
mount /dev/hd1 /mnt
// 对应操作
--- fs --- super.c --- sys_mount() --- dev_i=namei("/dev/hd1")//获得hd1的inode
                    |               |- dev = dev_i->i_zone[0]//获取hd1的设备号
                    |               |- dir_i=namei("/mnt")//获得mnt的inode
                    |               |- sb=read_super(dev)//读取hd1超级块
                    |               |- sb->s_imount=dir_i//将inode挂载在超级块上
                    |
                    |- read_super(dev) --- 检查超级块是否已经读入super_block[8]
                                     |- 从super_block[8]中找到没有使用的一项
                                     |- bh = bread(dev,1)//读超级块
                                     |- s = bh->b_data
                                     |- s->s_imap[i]=bread(dev,block)
                                     |  //读外设节点位图并挂接
                                     |- s->s_zmap[i]=bread(dev,block)
                                     |  //读外设逻辑位图并挂接

2. 打开文件

// 示例
char buffer[12000];
int fd = open("/mnt/user/user1/user2/hello.txt", O_RDWR, 0644);
int size = read(fd, buffer, sizeof(buffer));
--- fs --- open.c --- sys_open() --- 在当前进程的filp[20]中
        |                         |  找到一个空闲项filp[fd]
        |                         |- 在file_table[64]中找到一个空闲项f
        |                         |- current->filp[fd]=f
        |                         |- open_namei(&inode)//获取文件节点
        |                         |- f->f_inode = inode//挂接到f上
        |
        |- namei.c --- open_namei() --- dir=dir_namei(pathname,&namelen,&basename)
        |           |                |  //获取文件枝梢节点、文件名及其长度
        |           |                |- bh=find_entry(&dir,basename,namelen,&de)
        |           |                |  //从枝梢节点读取对应的目录项de
        |           |                |- inr=de->inode;dev=dir->i_dev
        |           |                |  //获取文件inode号
        |           |                |- inode=iget(dev,inr)
        |           |- dir_namei() --- dir = get_dir(pathname)
        |           |               |- 获得文件名,计算其长度
        |           |- get_dir() --- inode设置为根节点,pathname指向'mnt'
        |           |             |- 遍历路径名字符串
        |           |             |- thisname=pathname
        |           |             |- namelen=pathname长度
        |           |             |- pathname指向下一个目录名
        |           |             |- 字符串到末尾了吗 --- Yes --- 返回inode
        |           |             |- find_entry(&inode,thisname,namelen,&de)
        |           |             |  //找到这个目录中名为thisname的目录项de
        |           |             |- inr=de->inode;idev=inode->i_dev
        |           |             |- inode=iget(idev,inr)
        |           |             |- 返回遍历
        |           |- find_entry() --- block=(*dir)->i_zone[0])
        |                            |- bh = bread((*dir)->i_dev,block)
        |                            |  //读取文件的第一个数据块
        |                            |- de=bh->b_data//de指向数据块头
        |                            |- de迭代遍历数据块,查找名字为name
        |                            |  长度为namelen的目录项
        |                            |- 找到返回bh,否则返回NULL
        | 
        |- inode.c --- iget(dev,nr) --- empty=get_empty_inode()
                    |                |- 遍历inode_table[32]
                    |                 |- 设备号=dev且节点号=nr吗 --- No --- 返回遍历
                    |                |- inode挂载了文件系统吗 --- No --- 返回inode
                    |                |- 查找对应超级块super_block[i]
                    |                |- dev=super_block[i].s_dev
                    |                |- nr=1
                    |                |- inode = inode_table
                    |                |- 返回遍历
                    |                |- 遍历结束,没有找到inode
                    |                |- inode=empty
                    |                |- inode->i_dev=dev
                    |                |- inode->i_num=nr
                    |                |- read_inode(inode)//从硬盘上读取节点
                    |                |- 返回inode
                    |- read_inode() --- sb=get_super(inode->i_dev)
                                     |- 由超级块中的节点位图和inode号
                                     |  计算出文件所在块号block
                                     |- bh=bread(inode->i_dev,block)
                                     |- 由inode号计算出块内偏移量
                                     |- 根据偏移量拷贝bh->b_data到inode

3. 读取文件

--- fs --- read_write.c --- sys_read() --- 若是管道文件,则执行read_pipe()返回
        |                               |- 若是字符设备文件,则执行rw_char()返回
        |                               |- 若是块设备文件,则执行block_read()返回
        |                               |- 若是目录或普通文件,则执行file_read()返回
        |
        |- file_dev.c --- file_read() --- 剩余字节数不为0时循环
        |                              |- nr = bmap((filp->f_pos)/BLOCK_SIZE))
        |                              |  //确定要读的部分在哪个块上
        |                              |- bh=bread(inode->i_dev,nr)
        |                              |- 计算剩余字节数
        |                              |- 复制数据到指定用户空间
        |
        |- inode.c --- bmap(block) --- _bmap(block,0)
                    |- _bmap(block,create) --- 若block<7,返回i_zone[block]
                                            |- block -= 7
                                            |- 若block<512
                                            |--- 若create=1且i_zone[7]不存在
                                            |----- i_zone[7]=new_block()
                                            |--- i_zone[7]=new_block()
                                            |--- i=(bh->b_data)[block]
                                            |    //求出块号
                                            |--- 若create=1且i=0
                                            |----- i=new_block
                                            |--- 返回i
                                            |- block-=512
                                            |- 若create=1且i_zone[8]不存在
                                            |--- i_zone[8]=new_block()
                                            |- bh=bread(i_zone[8])//读一级索引块
                                            |- i = (bh->b_data)[block/512]
                                            |  //i为二级索引块块号
                                            |- 若create=1且i=0
                                            |--- i=new_block()
                                            |- bh=bread(i)//读二级索引块
                                            |- i = (bh->b_data)[block%512]
                                            |- 若create=1且i=0
                                            |--- i=new_block()
                                            |- 返回i

4. 新建文件

char str1[]="Hello, world";
int fd = creat("/mnt/user/user1/user2/hello.txt", 0644);
int size = write(fd, str1, strlen(str1));
--- fs --- open.c --- sys_creat() --- sys_open(O_CREAT | O_TRUNC)
        |          |- sys_open() --- 在当前进程的filp[20]中
        |                         |  找到一个空闲项filp[fd]
        |                         |- 在file_table[64]中找到一个空闲项f
        |                         |- current->filp[fd]=f
        |                         |- open_namei(&inode)//获取文件节点
        |                         |- f->f_inode = inode//挂接到f上
        |
        |- namei.c --- open_namei() --- dir=dir_namei(pathname,&namelen,&basename)
        |           |                |  //获取文件枝梢节点、文件名及其长度
        |           |                |- bh=find_entry(&dir,basename,namelen,&de)
        |           |                |  //从枝梢节点读取对应的目录项de
        |           |                |- 若bh=NULL
        |           |                |--- inode = new_inode()
        |           |                |--- inode->i_dirt = 1
        |           |                |--- bh = add_entry(dir,basename,namelen,&de)
        |           |                |    //在dir中新建一个目录项,名字为basename,返回到de中
        |           |                |--- de->inode = inode->i_num
        |           |                |--- bh->b_dirt = 1
        |           |                |--- 返回0
        |           |- add_entry() --- block = dir->i_zone[0]
        |                           |- bh = bread(dir->i_dev,block)//读取文件第一个块
        |                           |- de = bh->b_data//de指向第一个目录项
        |                           |- 开始遍历
        |                           |--- 如果该数据块中没有空闲目录项
        |                           |----- block = create_block(i/DIR_ENTRIES_PER_BLOCK)
        |                           |      //当前第i个目录项,参数是i_zone号
        |                           |----- bh = bread(dir->i_dev,block)
        |                           |----- de = bh->b_data
        |                           |----- 返回遍历
        |                           |--- 如果已达到inode末尾,没有找到空闲项
        |                           |----- de->inode=0//在inode末尾添加一个空闲项
        |                           |--- 如果de->inode为0//已找到空闲项
        |                           |----- 给de->name赋值
        |                           |----- bh->b_dirt = 1
        |                           |----- 返回bh
        |                           |--- de++
        |                           |- 返回NULL
        |
        |- bitmap.c --- new_inode() --- inode=get_empty_inode()
        |                            |- sb = get_super(dev)
        |                            |- 找到bit为0的节点位图所在的缓冲区bh
        |                            |- 由于新建了inode,将bh中对应bit位置1
        |                            |- bh->b_dirt = 1
        |                            |- 对inode属性进行设置
        |
        |- inode.c --- create_block() --- _bmap(1)

5. 写文件

  • 将数据写到缓冲区
--- fs --- open.c --- sys_write() --- file_write()
        |
        |- file_dev.c --- file_write() --- 已写入字节小于count循环
        |                               |--- block=create_block(pos/BLOCK_SIZE)
        |                               |    //找到需要写入的块号
        |                               |--- bh=bread(inode->i_dev,block)
        |                               |--- 计算
        |                               |--- 拷贝buf到缓冲区
        |                               |- 返回写入字节数
        |
        |- inode.c --- create_block(block) --- _bmap(block,1)
        |           |- _bmap(block,create) --- 若block<7,返回i_zone[block]
        |                                   |- block -= 7
        |                                   |- 若block<512
        |                                   |--- 若create=1且i_zone[7]不存在
        |                                   |----- i_zone[7]=new_block()
        |                                   |--- i_zone[7]=new_block()
        |                                   |--- i=(bh->b_data)[block]
        |                                   |    //求出块号
        |                                   |--- 若create=1且i=0
        |                                   |----- i=new_block
        |                                   |--- 返回i
        |                                   |- block-=512
        |                                   |- 若create=1且i_zone[8]不存在
        |                                   |--- i_zone[8]=new_block()
        |                                   |- bh=bread(i_zone[8])//读一级索引块
        |                                   |- i = (bh->b_data)[block/512]
        |                                   |  //i为二级索引块块号
        |                                   |- 若create=1且i=0
        |                                   |--- i=new_block()
        |                                   |- bh=bread(i)//读二级索引块
        |                                   |- i = (bh->b_data)[block%512]
        |                                   |- 若create=1且i=0
        |                                   |--- i=new_block()
        |                                   |- 返回i
        |
        |- bitmap.c --- new_block() --- sb = get_super(dev)
                                     |- 找到bit为0的逻辑位图所在的缓冲区bh
                                     |- 由于要新建块,将bh中逻辑位图对应bit位置1
                                     |- 由位图的信息计算逻辑块号j
                                     |- bh=getblk(dev,j)
                                     |- clear_block(bh->b_data)
                                     |- bh->b_dirt = 1
                                     |- 返回j
  • 从缓冲区同步到外设,sys_sync()在update进程中,一定时间运行一次
--- fs --- buffer.c --- sys_sync() --- sync_inodes()
        |                           |- 遍历缓冲区,若bh->b_dirt=1
        |                           |--- ll_rw_block(bh)
        |
        |- inode.c --- sync_inodes() --- 遍历inode_table,若inode->i_dirt=1
                    |                 |--- write_inode()
                    |- write_inode() --- sb=get_super(inode->i_dev)
                                      |- 根据inode号计算其所在块号
                                      |- bh=bread(inode->i_dev,block)
                                      |- 将inode写入bh缓冲区
                                      |- bh->b_dirt=1
                                      |- inode->i_dirt=0

6. 关闭文件

//示例
close(fd);
unlink("/mnt/user/user1/user2/hello.txt");
--- fs --- open.c --- sys_close() --- filp = current->filp[fd])
        |                          |- current->filp[fd]=NULL//当前进程fd置空
        |                          |- --filp->f_count//file_table项引用计数减1
        |                          |- iput(filp->f_inode)//inode引用计数减1
        |
        |- inode.c --- iput(inode) --- 循环开始
                                    |- 如果inode->i_count>1
                                    |--- inode->i_count--
                                    |--- 返回
                                    |- 如果inode->i_nlinks=0
                                    |--- truncate(inode)
                                    |--- free_inode(inode)
                                    |--- 返回
                                    |- 如果inode->i_dirt=1
                                    |--- write_inode(inode)
                                    |--- 返回循环
                                    |- inode->i_count--
                                    |- 返回

7. 删除文件

--- fs --- namei.c --- sys_unlink() --- dir=dir_namei(name,&namelen,&basename)
        |                            |  //找到要删除文件的枝梢节点
        |                            |- bh = find_entry(&dir,basename,namelen,&de)
        |                            |  //读取枝梢节点对应目录项
        |                            |- inode = iget(dir->i_dev, de->inode)
        |                            |  //获得文件i节点
        |                            |- de->inode = 0//该目录项设置为空闲项
        |                            |- bh->b_dirt = 1
        |                            |- inode->i_nlinks--
        |                            |- inode->i_dirt = 1
        |                            |- iput(inode)
        |                            |- iput(dir)
        |
        |- inode.c --- iput(inode) --- 循环开始
        |                           |- 如果inode->i_count>1
        |                           |--- inode->i_count--
        |                           |--- 返回
        |                           |- 如果inode->i_nlinks=0
        |                           |--- truncate(inode)
        |                           |--- free_inode(inode)
        |                           |--- 返回
        |                           |- 如果inode->i_dirt=1
        |                           |--- write_inode(inode)
        |                           |--- 返回循环
        |                           |- inode->i_count--
        |                           |- 返回
        |
        |- truncate.c --- truncate() --- 将i_zone[0]到i_zone[6]置0
        |                             |- 释放一级索引块
        |                             |- 释放二级索引块
        |                             |- 将i_zone[7]和i_zone[8]置0
        |                             |- inode->i_size = 0
        |                             |- inode->i_dirt = 1
        |
        |- bitmap.c --- free_inode() --- sb = get_super(inode->i_dev)
                                      |- bh=sb->s_imap[inode->i_num/8192]
                                      |  //得到inode所在缓冲区
                                      |- clear_bit(inode->i_num%8191,bh->b_data)
                                      |  //清除缓冲区中对应节点位图bit位
                                      |- bh->b_dirt = 1
                                      |- memset(inode,0,sizeof(*inode))
                                         //对应i节点清零

猜你喜欢

转载自blog.csdn.net/egean/article/details/81000846