文章目录

  • inode
    • 索引表
    • inode 结构
    • 超级块
    • 创建文件系统
    • inode 操作解析
      • 打开inode
      • 回收inode
      • inode同步到磁盘
  • 目录
    • 创建目录
    • 打开目录
    • 关闭目录
  • 文件
    • 文件描述符
    • 常见文件操作解析
      • 创建文件
      • 打开文件
        • 内核层面打开文件
        • 用户层面打开文件
      • 关闭文件
      • 读写文件
  • 参考文献

inode

索引表

磁盘的IO是非常慢的,所以引入的块的概念,其是多个扇区组成的,也是文件系统的基本读写单位,所以一个文件至少要占一个块。当文件的大小过大的时候,其会占用多个块,而这些块可能在不同的地方。而其中记录哪个块对应哪个文件的元信息就被称之为文件分配表(FAT)

FAT采用索引来组织,其为每个文件的所有块建立一个索引表,索引表就是块地址数组。每个数组元素就是块的地址,数组元素下标就是文件块的索引。这样我们想要访问哪个块就可以查索引表快速获得块的起始地址。包含此索引表的索引结构被称之为inode

索引表总共有15个索引项,其中前12项被称之为直接索引,而包含直接索引的索引表被称之为直接块索引表。如果文件大于12个块的话,我们就建立新的块索引表,这个被叫做一级间接块索引表,之后从第13项开始存储的就是一级间接块索引表的起始位置。每个一级间接块总共有256项,可以容纳256个块。而直接块索引的第14项则是二级间接块索引表,其一个表也是256项,不过其中每一项都是一个一级间接块索引表的地址,所以二级间接块索引表支持256*256个文件块。同理,第15项是三级间接块索引表

inode 结构

而inode结构中,除了索引表之外还有文件的其他信息,比如说文件名字、文件大小、执行权限等等,这就构成了如下图的结构:

用代码表示便是如下的结构:

struct inode
{uint32_t inode_no;        //inode编号uint32_t inode_size;      //当inode表示目录,其表示目录项下文件大小和//若为文件,则表示文件大小uint32_t inode_open_cnts; //记录此文件被打开的次数bool write_deny;          //写文件标志,因为不能同时写uint32_t inode_sectors[13]; //数据块指针,0~11表示直接块,12表示一级间接块指针//简单实现一下,不需要13的二级间接块指针//也不需要14的三级间接块指针struct list_elem inode_tag; //在list链表中的标志位,这个list表示已打开的inode列表
};

超级块

这样我们对于每个文件都有其物理表示了,要注意的就是文件只是概念上的。而对于文件系统来说,我们需要在某个固定的地方获得文件系统的元信息配置,其需要表示这个分区的inode有多少,这个inode位图地址及大小,根目录位置、空间块位图地址和大小等等,这就是超级块。用代码表示如下,这些属性只有62字节,为了筹齐一个扇区,我们需要定义一个空闲数组,方便我们到时候的读取硬盘中分区超级块块初始化信息。

//超级块,存储着文件系统元信息的配置
struct super_block
{uint32_t magic;         //用来标识文件类型uint32_t sec_cnt;       //本分区总共的扇区数uint32_t inode_cnt;     //本分区中inode数量uint32_t part_lba_base; //本分区起始lba地址uint32_t block_bitmap_lba;   //super块位图本身起始扇区地址uint32_t block_bitmap_sects; //扇区位图本身占用的扇区数量uint32_t inode_bitmap_lba;   //inode节点位图起始扇区lba地址uint32_t inode_bitmap_sects; //inode节点位图占用的扇区数量uint32_t inode_table_lba;   //inode表起始扇区lbauint32_t inode_table_sects; //inode表占用的扇区数量uint32_t data_start_lba; //数据区开始的第一个扇区号uint32_t root_inode_no;  //根目录所在的inode节点号uint32_t dir_entry_size; //目录项大小uint8_t pad[460]; //加上460字节,凑够一个扇区
} __attribute__((packed));

超级块位于磁盘中。硬盘自从MBR引导扇区之后,剩余的空闲内容便被划分为一个个分区。在每个分区的起始位置,便是这个分区的一些元信息,主要包含操作系统引导块、超级块、空闲块位图、inode位图等等,详情如下图:

创建文件系统

创建文件系统需要我们初始化文件系统的元信息,其实也就是初始化上图中的内容,主要分为以下几个步骤:

  1. 根据分区part大小,计算分区文件系统元信息需要的扇区数及位置,我们假设每个分区最多支持4096个文件,所以我们就可以根据inode的位图需要多少磁盘块、一级inode表需要多少磁盘块,就能计算出我们已经使用的块数和空闲的块数。
    //block_bitmap_init,一个块大小是一扇区,用来表示磁盘的一个块被没被使用uint32_t boot_sector_sects = 1;                                                                        //根扇区数量,其为引导块uint32_t super_block_sects = 1;                                                                        //超级块数量uint32_t inode_bitmap_sects = DIV_ROUND_UP(MAX_FILES_PER_PART, BITS_PER_SECTOR);                       //inode节点位图占用的扇区数uint32_t inode_table_sects = DIV_ROUND_UP(((sizeof(struct inode) * MAX_FILES_PER_PART)), SECTOR_SIZE); //计算inode表所占用的扇区数uint32_t used_sects = boot_sector_sects + super_block_sects + inode_bitmap_sects + inode_table_sects; //已使用的扇区数uint32_t free_sects = part->sec_cnt - used_sects;                                                     //剩余扇区数//处理空闲块和空闲块位图,每个扇区位图可以表示4096个空闲扇区uint32_t block_bitmap_sects;                                     //空闲块最终占用的位图数block_bitmap_sects = DIV_ROUND_UP(free_sects, BITS_PER_SECTOR);  //空闲块位图所占用的扇区数uint32_t block_bitmap_bit_len = free_sects - block_bitmap_sects; //真正的空闲块数量block_bitmap_sects = DIV_ROUND_UP(block_bitmap_bit_len, BITS_PER_SECTOR);
  1. 内存创建超级块,初始化超级块的信息,以上元信息写入写入超级块。这个时候磁盘还是空闲的,所以根据上面计算出来的信息,我们很容易就算出超级块各项的信息。
  2. 将超级块写入磁盘。这个时候超级块的信息已经初始化完了,我们就可以把这部分信息先写入磁盘保存。
  3. 写入文件系统其他元信息,主要是块位图、inode位图以及inode表
  4. 写入根目录到磁盘中,根目录是文件系统的开始,一开始主要包含...这两项

执行完这几步其实我们的文件系统已经创建完成,剩下的就是各个数据结构的操作函数。

inode 操作解析

打开inode

/*** @brief 根据inode节点号返回inode节点* @param part 分区指针* @param inode_no inode号* @return 这个inode节点号代表的inode*/
struct inode *inode_open(struct partition *part, uint32_t inode_no);

对于inode来说,其本身的信息存储在是存储在inode表中的,表中的每个元素都是一个文件的inode信息,我们就拿根目录来举例。根目录的inode号为0,其也是inode表的第一个元素,那么我们我们根据inode号就可以计算出其在硬盘中的地址。之后,我们在内核内存池给inode号分配一个内存空间,从硬盘读取内容到这个内存区域中,再加入我们维护的一个inode队列中这样下次查找的时候,直接从这个队列中获取就不用读磁盘。

而我们去打开inode则是分为两种情况。一个是先去判断已打开的inode队列中是否有这个要打开的inode;如果没有就去磁盘读取这个inode的信息,之后把这个inode添加到队列中。

回收inode

/*** @brief 回收inode数据块和inode本身* * @param part 分区指针* @param inode_no 要回收的inode的inode号*/
void inode_release(struct partition *part, uint32_t inode_no)

inode中分为直接块和超级块,所以我们可以先获取这个inode中所有的块,把它们放到一起,之后我们先把块位图清空并同步到磁盘,之后再根据inode号清空此inode表项。同时要注意这个步骤我们要清除内存中队列这个inode节点的信息。

    //# 1.获取inode信息struct inode *delete_inode = inode_open(part, inode_no);ASSERT(delete_inode->inode_no == inode_no);//# 2.回收inode占用的所有块uint8_t block_idx = 0;uint8_t block_cnt = 12; //块的数量uint32_t block_bitmap_idx = 0;uint32_t all_blocks[140] = {0};...//逐个回收直接块和间接块所占的空间block_idx = 0;while (block_idx < block_cnt){//因为这里没读入间接表那个块,所以all_blocks[12] = 0if (all_blocks[block_idx] != 0){block_bitmap_idx = 0;block_bitmap_idx = all_blocks[block_idx] - part->su_block->data_start_lba;ASSERT(block_bitmap_idx > 0);bitmap_set(&part->block_bitmap, block_bitmap_idx, 0);bitmap_sync(current_partition, block_bitmap_idx, BLOCK_BITMAP);}block_idx++;}//# 3.回收该indeo所占用的inodebitmap_set(&part->inode_bitmap, inode_no, 0);bitmap_sync(current_partition, inode_no, BLOCK_BITMAP);//# 4.释放资源...

inode同步到磁盘

/*** @brief 将inode写入到分区part* @param part 分区指针* @param inode 待同步的inode指针* @param io_buf 主调函数提供的操作缓冲区*/
void inode_sync(struct partition *part, struct inode *inode, void *io_buf);
  1. 根据inode号在inode表中得到这个inode在磁盘上的偏移
    //# 1.定位该inode在磁盘中的位置uint8_t inode_no = inode->inode_no;struct inode_position inode_pos;inode_locate(part, inode_no, &inode_pos);
  1. 有些内容是不需要在磁盘中存在的,所以在这里我们可以清空其中的内容
    //# 2.清空磁盘中不需要的三项 inode_open_cnts write_deny inode_tagASSERT(inode_pos.sec_lba <= (part->start_lba + part->sec_cnt));struct inode pure_inode;memcpy(&pure_inode, inode, sizeof(struct inode));pure_inode.inode_open_cnts = 0;pure_inode.write_deny = false;pure_inode.inode_tag.prev = pure_inode.inode_tag.next = NULL;
  1. 之后的过程就是从磁盘中读取这个inode元信息所在的扇区到一个缓冲区,然后把我们新的inode数据写入这个缓冲区,再同步到磁盘之中。
    //读出两个删除中的数据,然后拼接写入ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 1);//再写入原来的位置memcpy((inode_buf + inode_pos.off_size), &pure_inode, sizeof(struct inode));ide_write(part->my_disk, inode_pos.sec_lba, inode_buf, 1);

目录

在操作系统的设计时,目录其实本质也是一个文件,所以其本身也是需要inode节点的。而目录文件中存储的内容就是该目录下的文件,其本身可以看做一个列表,每个列表元素的主要元素就是描述其包含文件的元信息。

inode编号 文件名 文件类型
12345 . 此目录
12346 上一级目录
12347 test.c 文件
12348 project 目录

用代码表示这几项便如下:

//目录项结构,连接文件名和inode节点的数据结构
struct dir_entry
{char filename[MAX_FILE_NAME_LEN]; //普通文件或目录名称uint32_t inode_no;                //文件或目录对应的inode编号enum file_types file_type;        //文件类型
};

创建目录

/*** @brief 创建目录pathname* * @param pathname 要创建的目录* @return int32_t 成功返回0,失败返回-1*/
int32_t sys_mkdir(const char *pathname)

创建目录主要分为以下过程:

  1. 我们传入的是一个字符串表示的文件路径,这里假设我们创建的路径是/home/ik,首先我们先判断一下这个路径是否存在。这个过程其实就是从根目录的目录文件表查有没有home这个目录,之后可以获得home这个目录的inode号,根据inode号在inode表中查home的目录文件表是否有ik这个目录。
    //# 1.先判断这个路径是否存在inode_no = search_file(pathname, &searched_record);
  1. 要开始创建目录了,这个过程我们先给这个目录去分配一个inode结构表示其元信息。
    //# 2.得到目录名称,不带路径的那种,创建其inode结构char *dirname = strrchr(searched_record.searched_path, '/') + 1;inode_no = inode_bitmap_alloc(current_partition);
  1. 给这个目录文件分配一个块用于实际存储,首先写入. 和…这两项,完善目录文件表。
    //# 3.为目录分配一个块,用来写入. 和 ..block_lba = block_bitmap_alloc(current_partition);...//# 4.将.和..写入写入目录memset(io_buf, 0, SECTOR_SIZE * 2);struct dir_entry *pdir_e = (struct dir_entry *)io_buf;//初始化当前目录.memcpy(pdir_e->filename, ".", 1);pdir_e->inode_no = inode_no;pdir_e->file_type = FT_DIRECTORY;pdir_e++;//初始化当前目录..memcpy(pdir_e->filename, "..", 2);pdir_e->inode_no = parent_dir->inode->inode_no;pdir_e->file_type = FT_DIRECTORY;
  1. 把这个块中的内容写入磁盘
    //同步到磁盘ide_write(current_partition->my_disk, new_dir_inode.inode_sectors[0], io_buf, 1);
  1. 把当前的目录写入父目录的目录文件表中并保存到磁盘
    struct dir_entry new_dir_entry;memset(&new_dir_entry, 0, sizeof(struct dir_entry));create_dir_entry(dirname, inode_no, FT_DIRECTORY, &new_dir_entry);memset(io_buf, 0, SECTOR_SIZE * 2);sync_dir_entry(parent_dir, &new_dir_entry, io_buf);
  1. 父目录的inode同步到硬盘
    //# 6.父目录的inode同步到硬盘inode_sync(current_partition, parent_dir->inode, io_buf);
  1. 新创建的inode同步到硬盘
    //# 7.新创建的inode同步到硬盘inode_sync(current_partition, &new_dir_inode, io_buf);
  1. 这个目录的inode位图信息同步到磁盘中
    //# 8.inode位图同步到硬盘bitmap_sync(current_partition, inode_no, INODE_BITMAP);

打开目录

inode作为文件的实体,所以打开目录的本质其实也就是是打开一个inode结构,将其添加到内核维护的inode队列。当然,这个目录本身也是要在内存中,存在的,所以分配一块内存空间。

/*** @brief 在分区part上打开inode节点为inode_no的目录并返回目录指针* @param part 分区指针* @param inode_no inode节点号* @return 返回dir目录指针*/
struct dir *dir_open(struct partition *part, uint32_t inode_no)
{struct dir *pdir = (struct dir *)sys_malloc(sizeof(struct dir));pdir->inode = inode_open(part, inode_no);pdir->dir_pos = 0;return pdir;
}

关闭目录

关闭目录就是相反的过程,释放内存,之后关闭目录文件。这里要注意的是多个进程可能都共享一个文件,所以文件时有一个引用计数的选项,当引用计数为0时,inode_close函数才会真正的去关闭这个inode。

/*** @brief 关闭dir指针所指向的目录* @param dir 目录指针*/
void dir_close(struct dir *dir)
{if (dir == &root_dir){return;}inode_close(dir->inode);sys_free(dir);
}

文件

在操作系统中,多个进程可以同时打开一个文件,对文件的不同位置进行操作,这肯定就需要一个叫做文件偏移量的属性。刚刚说文件的物理表示其实是inode,但是inode中并不存储文件偏移量这种属性。一旦存储这个属性,那么就会导致inode记载的信息随着进程数的增加记录的内容增多,这些信息都会随着inode加载到磁盘中,磁盘空间约占越多。所以我们还需要一种结构来存储这类信息,这个信息最好是在内存中,这样我们打开文件的时候这个文件结构存在并且随着文件的关闭这个数据结构,这就是文件结构。用代码表示就如下:

struct file
{uint32_t fd_pos;        //文件偏移量uint32_t fd_flag;       //文件操作标识如O_RDONLY等struct inode *fd_inode; //inode指针
};

inode是文件在磁盘上的物理实体,文件结构则是文件的概念表示。一个文件结构可以指向多个inode实体,一个inode实体则只可以表示一个文件。它们的关系如下图:

文件描述符

文件描述符(file descripitor)是用来描述文件的一个数据结构,其所描述的对象是文件的操作。每个进程都有一个进程文件表,其本质其实就是文件描述符数组,文件描述符中的元素便是内核文件表中的文件描述符的下标,而在操作系统中通过打开文件获得的文件描述符其实就是这个文件描述符数组的下标,我们查找的时候,根据进程文件表的下标拿到内核文件表中的内容,这个内容就是上面的文件结构。它们三者的关系如下图:

这样我们如果在进程A中查找文件描述符为2的文件,那么我们可以得到这个文件在内核文件表中的下标就是0,我们就可以得到我们要操作的inode节点信息了。

常见文件操作解析

创建文件

/*** @brief 创建文件* @param parent_dir 父目录的指针* @param file_name  此文件的文件名* @param flag 文件标志位* @return 成功返回进程中的文件描述符,失败返回-1*/
int32_t file_create(struct dir *parent_dir, char *file_name, uint8_t flag);

我们现在来梳理创建文件的过程。主要有以下几个步骤:

  1. 创建文件的实体inode并对其进行初始化。这个过程其实就是inode位图中去分配一个块。这个时候我们只是在内存中创建了这么一个结构,并没有将其同步到硬盘中。
    int32_t inode_no = inode_bitmap_alloc(current_partition); //分配一个位图为0的节点号struct inode *new_file_inode = (struct inode *)sys_malloc(sizeof(struct inode));//初始化inode信息inode_init(inode_no, new_file_inode);
  1. 将这个文件的信息存入内核文件表,建立内核文件表项和inode节点的映射关系
    //得到内核文件表的一个空闲下标int fd_idx = get_free_slot_in_global();file_table[fd_idx].fd_inode = new_file_inode;file_table[fd_idx].fd_pos = 0;file_table[fd_idx].fd_flag = flag;file_table[fd_idx].fd_inode->write_deny = false;
  1. 创建一个目录项实体,作为这个文件在文件目录中的表示。
    struct dir_entry new_dir_entry;create_dir_entry(file_name, inode_no, FT_REGULAR, &new_dir_entry);
  1. 同步内存目录项数据到磁盘
 //目录项new_dir_entry写入磁盘的parent_dir目录项sync_dir_entry(parent_dir, &new_dir_entry, io_buf);//父目录的inode被更改了,同步到磁盘中inode_sync(current_partition, parent_dir->inode, io_buf);//此文件的inode同步到磁盘中inode_sync(current_partition, new_file_inode, io_buf);//将inode_bitmap同步到磁盘bitmap_sync(current_partition, inode_no, INODE_BITMAP);//此文件加入内核打开的文件中list_push(&current_partition->open_inodes, &new_file_inode->inode_tag);new_file_inode->inode_open_cnts = 1;

打开文件

内核层面打开文件

/*** @brief 打开编号为inode_no的inode对应的文件* @param inode_no inode号* @param flag 文件操作标识* @return 成功返回文件描述符,失败返回-1* @note write_deny设置的时候保证原子性*/
int32_t file_open(uint32_t inode_no, uint8_t flag);

打开文件的时候,需要传入我们要打开的文件的inode号,也就是指定是磁盘中的哪个文件,之后我们要做的步骤如下:

  1. 打开这个inode结构,将inode信息加载到内存并得到这个inode结构的inode号
 inode_open(current_partition, inode_no);
  1. 将此inode结构加入到内核文件表,内核文件表和inode是一一对应的关系。
    //初始化file_table中fd_idx项file_table[fd_idx].fd_inode = inode_open(current_partition, inode_no); //得到这个inode节点号对应的inode结点指针file_table[fd_idx].fd_pos = 0;file_table[fd_idx].fd_flag = flag;
  1. 之后判断我们进程是否是要对这个文件进行读写,我们需要保证对于一个进程同一时间只能有一个进程在读写。
    bool *write_deny = &file_table[fd_idx].fd_inode->write_deny;//判断文件是否是关于写文件,因为不能多个进程同时写一个文件if (flag & O_WRONLY || flag & O_RDWR){enum intr_status old_status = intr_get_status();if (!(*write_deny)){//此进程占有此文件*write_deny = true;intr_set_status(old_status);}else{intr_set_status(old_status);printk("file_open: can't open this file, maybe occured by other process!\n");return -1;}}
  1. 将此文件的描述符加入到进程文件表之中,这一步就是看进程文件表哪个表现还是空的,把内核文件表的下标写入。
    //建立进程文件描述符和内核文件描述符的映射关系pcb_fd_install(fd_idx);

用户层面打开文件

对于用户层面大家可以想一下我们平时使用open函数打开文件的过程,其会传入文件的路径信息以及对文件的权限信息。

/*** @brief 打开或创建文件* @param path_name 文件路径* @param flags 文件操作标识信息* @return 成功返回文件描述符,失败返回-1*/
int32_t sys_open(const char *path_name, uint8_t flags);

这个过程主要分为以下几步:

  1. 判断打开的文件是不是目录文件,这个判断文件路径的最后一个是不是'/'就可以做到了。
 path_name[strlen(path_name) - 1] == '/'
  1. 根据文件路径找到这个文件的inode号。假设当前路径是/test/test.c,首先我们对文件进行解析,首先解析出来的就是根目录/,其是一个目录文件,根据目录的信息,我们直到根目录下面有关目录文件表如下:
inode编号 文件名 文件类型
12345 . 此目录
12346 上一级目录
12347 main.c 文件
12348 test 目录

然后解析我们要寻找的目录test,其inode号为12348,我们根据inode号打开test这个目录文件,读取其目录文件表,在其中找到文件test.c,返回test.c的inode号12351,这个过程就是search_file函数做的事情。

inode编号 文件名 文件类型
12349 . 此目录
12350 上一级目录
12351 test.c 文件
    int inode_no = search_file(path_name, &searched_record);

3.拿到了inode号之后,我们就可以判断文件权限标志了,是要不存在就创建还是单纯的要打开这个文件:

 switch (flags & O_CREAT){case O_CREAT:printk("create files: %s\n", path_name);fd = file_create(searched_record.parent_dir, (strrchr(path_name, '/') + 1), flags);dir_close(searched_record.parent_dir);break;//其余均为打开已存在文件//主要有O_RDONLY、O_WRONLY、O_RWRDdefault:fd = file_open(inode_no, flags);break;}

关闭文件

这个过程被封装到两个函数中去做了,一个是file_close主要做的是内核层面的关闭文件操作,我们直接交给inode_close去做。用户层面则是注重把文件描述符移出进程文件表这个过程,在sys_close,我们根据进程中的文件描述符查表可以得到内核文件表中的表项,之后我们调用file_close关闭就可以了。

/*** @brief 关闭文件* @param file 待关闭的文件指针* @return 成功返回0,失败返回-1*/
int32_t file_close(struct file *file)
{file->fd_inode->write_deny = false;inode_close(file->fd_inode);file->fd_inode = NULL;return 0;
}/*** @brief 关闭文件描述符fd指向的文件* * @param fd 文件描述符* @return int32_t 成功返回0,失败返回-1*/
int32_t sys_close(int32_t fd)
{int32_t ret = -1;if (fd > 2){uint32_t fd_ = fd_local2global(fd);ret = file_close(&file_table[fd_]);running_thread()->fd_table[fd] = -1;}return ret;
}

读写文件

/*** @brief 从文件file读取count个字节到buf中* * @param file 文件指针* @param buf 存储读出数据的缓冲区* @param count 读出的字节数* @return int32_t 成功返回读出的字节数,失败返回-1*/
int32_t file_read(struct file *file, void *buf, uint32_t count);

文件读写的逻辑差不多,我们这里就拿读文件举例。主要分为以下几个过程:

  1. 首先是要计算我们要读取的字节数+当前的偏移量是不是比整个文件都长。如果读的大小大于文件的字节数就有多少读多少。
    //# 1.如果要读取的字节数+文件偏移量是要大于文件总共的字节数的,那就有多少读多少if ((file->fd_pos + count) > file->fd_inode->inode_size){size = file->fd_inode->inode_size - file->fd_pos;size_left = size;}
  1. 根据文件指针我们很容易知道要操作的是哪个inode,所以我们就申请一个缓冲区存放inode中的块信息。
    //# 2.申请一个缓冲区以及存放inode表的all_blocksuint8_t *io_buf = sys_malloc(BLOCK_SIZE);uint32_t *all_blocks = (uint32_t *)sys_malloc(BLOCK_SIZE + 48);...
  1. 之后我们就可以根据当前的文件偏移量得到要读取的位置在哪个块内,然后从这个位置,每次开始读取一个扇区。
    //# 开始读取数据了uint32_t sec_idx;        //扇区索引uint32_t sec_lba;        //扇区地址uint32_t sec_off_bytes;  //扇区内字节偏移量uint32_t sec_left_bytes; //扇区内剩余字节量uint32_t chunk_size;     //每次写入硬盘的数据块大小uint32_t bytes_read = 0; //已读取的数据量while (bytes_read < size){sec_idx = file->fd_pos / BLOCK_SIZE;         //得到起始扇区的索引sec_lba = all_blocks[sec_idx];               //得到扇区的地址sec_off_bytes = file->fd_pos % BLOCK_SIZE;   //得到此扇区中的偏移量信息sec_left_bytes = BLOCK_SIZE - sec_off_bytes; //得到这个扇区中剩余待读取的字节数chunk_size = size_left < sec_left_bytes ? size_left : sec_left_bytes;memset(io_buf, 0, BLOCK_SIZE);ide_read(current_partition->my_disk, sec_lba, io_buf, 1);memcpy(buf_dst, io_buf + sec_off_bytes, chunk_size);buf_dst += chunk_size;file->fd_pos += chunk_size;bytes_read += chunk_size;size_left -= chunk_size;}

参考文献

[1] 操作系统真相还原

Gos —— 文件系统相关推荐

  1. 2021年大数据Hadoop(七):HDFS分布式文件系统简介

    2021大数据领域优质创作博客,带你从入门到精通,该博客每天更新,逐渐完善大数据各个知识体系的文章,帮助大家更高效学习. 有对大数据感兴趣的可以关注微信公众号:三帮大数据 目录 HDFS分布式文件系统 ...

  2. Linux系统中创建大文件,并作为文件系统使用

    在LInux系统的使用过程中,有时候会遇到诸如某个磁盘分区的大小不够用了,导致其下的文件系统不能正常写入数据.亦或者是系统swap分区太小,不够用或者不满足条件而导致的其他一系列问题.如果我们系统上挂 ...

  3. Linux 文件系统及 ext2 文件系统

    linux 支持的文件系统类型 Ext2:     有点像 UNIX 文件系统.有 blocks,inodes,directories 的概念. Ext3:     Ext2 的加强版,添加了日志的功 ...

  4. 使用Uboot启动内核并挂载NFS根文件系统

    配置编译好内核之后,将生成的内核文件uImage拷贝到/tftpboot/下,通过tftp服务器将内核下载到开发板,使用命令:tftp 31000000 uImage.下载完成之后配置bootargs ...

  5. Bqq服务器的缓存文件放什么目录,如何使文件系统缓存失效? - How to invalidate the file system cache? - 开发者知识库...

    30 At least on Windows 7, it seems that attempting to open a volume handle without FILE_SHARE_WRITE ...

  6. 镁光ssd管理工具 linux,在 SSD 上使用 Btrfs 文件系统的相关优化

    优化挂载参数 在 Linux 中挂载 SSD 上的 btrfs,可以采用各种参数进行优化: # UUID=/btrfs defaults,ssd,discard,noatime,compress=lz ...

  7. php文本计数器源码,php 简单文本计数器[基于文件系统的页面计数器范例]

    我们的计数器经常会用到文本文件来实现,定义计数器写入的文件是当前目录下count.txt,然后我们应当测试该文件能否打开 基于文件系统的页面计数器范例 $countfile = "num.t ...

  8. Linux系统的快照是什么,linux – 文件系统快照与简单复制文件有何不同?

    通过做这个, # btrfs subvolume snapshot /mnt/1 /mnt/1/snapshot # tree /mnt/1 /mnt/1 ├── a ├── snapshot │ ├ ...

  9. 解压ubi文件_制作ubi文件系统

    制作 ubi 文件系统 目录 开发环境 ................................................................................ ...

  10. Linux那些事儿之我是Sysfs(9)sysfs文件系统模型

    最近Linus炮轰C++,"C++是一种糟糕的(horrible)语言.而且因为有大量不够标准的程序员在使用而使许多真正懂得底层问题,而不会折腾那些白痴'对象模型'".牛人就是牛气 ...

最新文章

  1. python 怎么将数组转为列表_图片转换成pdf格式怎么操作?什么软件能将图片转为pdf?...
  2. 计算机多媒体理论知识,计算机多媒体技术07311.doc
  3. 手机无法配置exchange客户端的解决方法
  4. 印钞机 V1.0(量化选基总结)
  5. daily scrum 10.31
  6. python中kmeans怎么导入数据集_通过Python实践K-means算法
  7. 动态规划系列-连续的子数组和(leetcode523)
  8. POJ2352 Stars
  9. 《CCNA路由和交换(200-120)学习指南》——2.4节认证提要
  10. 无用小知识-递归的使用
  11. NPP/VIIRS逐月夜间灯光数据(2012-2020年)
  12. 2014大众点评Hackathon参赛感想
  13. VMware WorkStation5分钟快速安装黑群晖
  14. 小菜openstack nova 源码学习之 evacuate
  15. RSS是什么,RSS怎么玩,RSS原理是什么
  16. 计算机写配器音乐谱子,《电脑音乐配器与制作》教学思路及教材编写
  17. error C251: illegal octal digit 错误提示
  18. 52道常见Python面试题,你都能答对吗?
  19. ubuntu 中文拼音输入法
  20. javabean+servlet+jsp返利网

热门文章

  1. FFmpeg第一季:小白开窍+九阳神功
  2. 计算机网络修复提示DNS服务器,dns被劫持或提示配置错误,该怎么解决
  3. 【安装教程】——widows_pycharm远程连接Linux服务器
  4. turtle画分形树
  5. 一周信创舆情观察(1.24~2.6)
  6. FutureTask实现超时任务
  7. java 麻将胡牌算法_麻将胡牌算法研究
  8. 阿里达摩院发布并开源“通义”大模型,AI底座之上促场景创新
  9. 【安装Oracle 12.2.0.1补丁】Oracle Database SAP Bundle Patch 12.2.0.1.220118 - 202202
  10. 1分钟了解 rap2