文件系统(二)

来源:互联网 发布:征服者4g008软件 编辑:程序博客网 时间:2024/06/06 02:57

前言

本篇主要讲述src/fs目录下的代码,该部分代码是文件系统的核心部分,也是kernel最为复杂的部分。将从下面四部分进行分析:

1. 高速缓冲区的管理程序,是对磁盘以块为单位数据的cache;

    buffer.c

  1. struct buffer_head * getblk(int dev,int block);
      在cache-buffer中找到指定的缓存块,如果不存在则找个未使用的bh(这个不从磁盘加载数据).
      icount++

  2. void brelse(struct buffer_head * buf);
     释放一个bh,如果icount为0则唤醒等待缓冲区的进程.
     icount--

  3. struct buffer_head * bread(int dev,int block);
      加载指定的缓存块,如果失败返回null.

  4. void bread_page(unsigned long address,int dev,int b[4]);
      读一页4KB数据到address开始处, 不能保证4个1KB数据都是有效的.

  5. struct buffer_head * breada(int dev,int first, ...);
     带预读功能的加载缓存块函数, 主要是读first, 后面的...是允许失败的且不执行icount++

  6. void buffer_init(long buffer_end);
     fs的缓存模块初始化. main()中调用.

    函数的调用关系:
               +-------------------------------+
               |  bread   breada  bread_page    |    对外接口
               +-------------------------------+
               |          getblk()                          |
               |                                                |
               |                                                |
               |                                                |
               +-------------------------------+

    资源竞争:
         1. 空闲的buffer-header队列
         2. 加锁的单个buffer-header

2. 文件系统的低层通用函数,说明了文件索引节点的管理、磁盘数据块的分配和释放、文件名于i节点的转换;

    bitmap.c

     这个处理了文件系统上的逻辑块位图和i节点位图.
     1. void free_block(int dev, int block);
         dev: 设备号, block: 磁盘逻辑块号(盘块号)
         释放磁盘上的逻辑块.
    2. int new_block(int dev);
         在文件系统dev上申请一个磁盘逻辑块(清零), 返回磁盘逻辑块号.
     3. void free_inode(struct m_inode *inode);
         释放指定的i节点, 复位文件系统中对应的i节点位图比特位.
     4. struct m_inode *new_inode(int dev);
         在文件系统dev中新建一个i节点,并在inode内存表中缓存之.
     操作对象: 磁盘文件系统的资源(block+inode).

    inode.c

仅对内存inode这个数组进行管理.

内部接口:
1. static inline void wait_on_inode(struct m_inode * inode);
2. static inline void lock_inode(struct m_inode * inode);
3. static inline void unlock_inode(struct m_inode * inode);
4. static int _bmap(struct m_inode * inode,int block,int create);

   将一个文件(inode)的内部数据逻辑块号映射到该文件系统的数据逻辑块号.
5. static void read_inode(struct m_inode * inode);
   读取指定inode.
6. static void write_inode(struct m_inode * inode);
   将指定的inode写盘(有cache-buffer在,存在延迟写),最基本要求是同步到cache-buffer上.

外部接口:
1. void invalidate_inodes(int dev);
   将dev的内存inode无效.
2. void sync_inodes(void);
   将内存inode写盘(有cache-buffer在,存在延迟写).
3. int bmap(struct m_inode * inode,int block);
   对内部函数4的包装, 此函数不会创建数据块,用于查询.
4. int create_block(struct m_inode * inode, int block);
   对内部函数4的包装, 此函数会帮助创建数据块.
5. void iput(struct m_inode * inode);
   释放一个内存inode, icount--, 干很多事情:
   a. 若为无名管道, 则唤醒等待进程,释放管道内存;
   b. 若为设备文件,则同步该设备;
   c. 若nlinks为0, 则从相应的fs释放之;
   d. 若dirt,       则write_inode.
6. struct m_inode * get_empty_inode(void);
   获取一个空的的内存inode.
7. struct m_inode * get_pipe_inode(void);
   获取一个无名管道内存inode.
8. struct m_inode * iget(int dev,int nr);
   获取指定的内存inode, icount++.  考虑了inode为挂载点情况.

资源竞争:
1. 单个内存inode被加锁.

truncate.c
1. void truncate(struct m_inode *inode);
    将普通文件or目录的内容截除为0, 并不是删除该i节点.

super.c
     对文件系统的super block的管理, 存在一个super-blocks cache-buffer.
     内部接口:
    1. static void lock_super(struct super_block *sb);
     2. static void unlock_super(struct super_block *sb);
     3. static void wait_on_super(struct super_block *sb);
     4. static struct super_block *read_super(int dev);
        从设备上读取超级块到super-cache中, 如果超级块已经存在则直接返回.

     外部接口:
    1. struct super_block *get_super(int dev);
        获取指定设备的超级块. 这个只是查找super_table, 别忘了文件系统是通过mount接口加载的.
     2. void put_super(int dev);
        将指定设备的超级块从super-cache中移除.
     3. int sys_umount(char *dev_name);
        卸载文件系统, dev_name为块设备名称.
    4. int sys_mount(char *dev_name, char *dir_name, int rw_flag);
        安装文件系统, 被加载的地方必须是一个目录名,并且对应的i节点没有被其它程序占用.
     5. void mount_root();
        初始化系统的根文件系统.

namei.c
    主要用于完成从一个给定文件系统路径名寻找并加载其对应i节点.
    内部接口:
    1. static int permission(struct m_inode * inode,int mask);
       权限检查, user-mode ---> group-mode ---> other-mode
   2. static int match(int len,const char * name,struct dir_entry * de);
       目录项比较, 成功返回1, 否则返回0.
    3. static struct buffer_head * find_entry(struct m_inode ** dir, const char * name, int namelen, struct dir_entry ** res_dir);
       查找一个目录项,并返回所在的buffer-header. name仅是目录项的名字. 内部考虑了. .., 进程root节点, 挂载点.
    4. static struct buffer_head * add_entry(struct m_inode * dir, const char * name, int namelen, struct dir_entry ** res_dir);
       在目录文件中添加一个目录项.
   5. static struct m_inode * get_dir(const char * pathname);
       根据路径名找到顶层目录的inode, 例如project/include/math 返回include的节点, project/include/math/ 返回math的节点.
    6. static struct m_inode * dir_namei(const char * pathname, int * namelen, const char ** name);
       根据路径名找到目录inode, 并返回基本名称. 例如 project/include/math   返回include的inode, 并置name为"math".
    7. static int empty_dir(struct m_inode * inode);
       判断该目录是否为空目录.

    外部接口:
    1. struct m_inode * namei(const char * pathname);
       从路径找到文件的inode. 例如project/include/math 返回math的inode.
    2. int open_namei(const char * pathname, int flag, int mode, struct m_inode ** res_inode);
       根据路径打开文件, 获取文件的inode.
   3. int sys_mknod(const char * filename, int mode, int dev);
       创建一个设备文件(字符或块设备).
    4. int sys_mkdir(const char * pathname, int mode);
       创建一个目录pathname.
    5. int sys_rmdir(const char * name);
       删除目录name.
    6. int sys_unlink(const char * name);
       取消硬链接.
    7. int sys_link(const char * oldname, const char * newname);
       创建一个硬链接newname,链接到oldname. 不能为目录创建别名.

3. 对文件中数据进行读写操作,包括对字符设备、管道、块读写文件中数据的访问;

    block_dev.c

块设备的读写,是对整个文件系统占用的区域哦.
1. int block_write(int dev, long * pos, char * buf, int count);
   向设备dev的pos开始处写数据.
2. int block_read(int dev, unsigned long * pos, char * buf, int count);
   从设备dev的pos开始处读数据.

    file_dev.c

对设备上文件的读写.
1. int file_read(struct m_inode * inode, struct file * filp, char * buf, int count);
   读文件inode, filp为描述它的文件结构.
2. int file_write(struct m_inode * inode, struct file * filp, char * buf, int count);
   向文件写数据.

    char_dev.c

字符设备的读写.
int rw_char(int rw,int dev, char * buf, int count, off_t * pos);

    pipe.c

无名管道的读写操作.
1. int read_pipe(struct m_inode * inode, char * buf, int count);
   读管道inode.
2. int write_pipe(struct m_inode * inode, char * buf, int count);
   写管道inode.
3. int sys_pipe(unsigned long * fildes);
   创建无名管道, 文件句柄fildes[0]&[1]均指向管道i节点。fildes[0]用于读管道数据, fildes[1]用于写管道数据。

    read_write.c

通过文件描述符对文件操作.
1. int sys_lseek(unsigned int fd,off_t offset, int origin);
2. int sys_read(unsigned int fd,char * buf,int count);

   根据fd的具体类型调用:
   a. read_pipe
   b. block_read
   c. file_read.
   d. rw_char
3. int sys_write(unsigned int fd,char * buf,int count);
   同2.

4. 文件的系统调用接口的实现,涉及文件打开、关闭、创建及文件目录操作等。

execute.c

加载程序。

open.c
    1. int sys_utime(char * filename, struct utimbuf * times);
       读取或修改文件的访问和修改时间.
    2. int sys_access(const char * filename,int mode);
       验证文件的访问权限。
    3. int sys_chdir(const char * filename);
       将当前进程的工作目录改为filename.
    4. int sys_chroot(const char * filename);
        把指定的目录名设置成为当前进程的根目录'/'.
    5. int sys_chmod(const char * filename,int mode);
       修改文件属性。
    6. int sys_chown(const char * filename,int uid,int gid);
       修改文件宿主, 得有root权限。
    7. int sys_open(const char * filename,int flag,int mode);
       打开文件filename.
       flag -- 打开文件标志. O_RDONLY, O_WRONLY, O_RDWR, O_CREATE, O_APPEND, O_EXCL等组合。
       mode -- 如果创建了文件,文件属性设置为此。
   8. int sys_creat(const char * pathname, int mode);
    9. int sys_close(unsigned int fd);

stat.c
   1. int sys_stat(char * filename, struct stat * statbuf);
       获取文件filename的状态信息。
    2. int sys_fstat(unsigned int fd, struct stat * statbuf);
        获取文件fd的状态信息。

fcntl.c
    1. int sys_dup2(unsigned int oldfd, unsigned int newfd);
       复制文件句柄oldfd到newfd.
    2. int sys_dup(unsigned int fildes);
       复制文件句柄,返回另一个句柄。
   3. int sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg);

ioctl.c
    1. int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg);
       输入输出控制。

下图是我整理的一个fs中数据的 layer图:

fsLayer

我将按照如上的思路阅读源码。

Minix文件系统格式

1. 对物理磁盘的划分(格式化)

    minix

对硬盘这样的大块,进行分区而治:

hd分区和fs

2. 数据结构

minix超级块

minix-I节点

InodeMode

NOTE:  关于inode map的位0不使用,第0个inode在imap中的序号为1,  对于data logic-block的位0不使用,第0个data logic-block的在bmap中的序号为1.

File的内核表示

file_struct 

文件读写操作流程

read_layer

疑问:

1. 文件的访问权限验证仅在open()函数中验证,这个不严谨,read() && write()中应该也验证。

原创粉丝点击