ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

操作系统真象还原实验记录之实验二十八:实现文件删除、创建与遍历目录

2022-01-30 09:30:12  阅读:206  来源: 互联网

标签:真象 遍历 idx 实验 dir entry inode 目录 block


操作系统真象还原实验记录之实验二十八:实现文件删除、创建与遍历目录

1.文件的删除

位于打开文件表的文件不能被删除。目录不能用此函数删除,用rmdir(暂未实现)。

inode.c的基本函数inode_release、inode_delete(回收inode)


/* 回收inode的数据块和inode本身 */
void inode_release(struct partition* part, uint32_t inode_no) {
   struct inode* inode_to_del = inode_open(part, inode_no);
   ASSERT(inode_to_del->i_no == inode_no);

/* 1 回收inode占用的所有块 */
   uint8_t block_idx = 0, block_cnt = 12;
   uint32_t block_bitmap_idx;
   uint32_t all_blocks[140] = {0};	  //12个直接块+128个间接块

   /* a 先将前12个直接块存入all_blocks */
   while (block_idx < 12) {
      all_blocks[block_idx] = inode_to_del->i_sectors[block_idx];
      block_idx++;
   }

   /* b 如果一级间接块表存在,将其128个间接块读到all_blocks[12~], 并释放一级间接块表所占的扇区 */
   if (inode_to_del->i_sectors[12] != 0) {
      ide_read(part->my_disk, inode_to_del->i_sectors[12], all_blocks + 12, 1);
      block_cnt = 140;

      /* 回收一级间接块表占用的扇区 */
      block_bitmap_idx = inode_to_del->i_sectors[12] - part->sb->data_start_lba;
      ASSERT(block_bitmap_idx > 0);
      bitmap_set(&part->block_bitmap, block_bitmap_idx, 0);
      bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
   }
   
   /* c inode所有的块地址已经收集到all_blocks中,下面逐个回收 */
   block_idx = 0;
   while (block_idx < block_cnt) {
      if (all_blocks[block_idx] != 0) {
	 block_bitmap_idx = 0;
	 block_bitmap_idx = all_blocks[block_idx] - part->sb->data_start_lba;
	 ASSERT(block_bitmap_idx > 0);
	 bitmap_set(&part->block_bitmap, block_bitmap_idx, 0);
	 bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
      }
      block_idx++; 
   }

/*2 回收该inode所占用的inode */
   bitmap_set(&part->inode_bitmap, inode_no, 0);  
   bitmap_sync(cur_part, inode_no, INODE_BITMAP);

   /******     以下inode_delete是调试用的    ******
   * 此函数会在inode_table中将此inode清0,
   * 但实际上是不需要的,inode分配是由inode位图控制的,
   * 硬盘上的数据不需要清0,可以直接覆盖*/
   void* io_buf = sys_malloc(1024);
   inode_delete(part, inode_no, io_buf);
   sys_free(io_buf);
   /***********************************************/
    
   inode_close(inode_to_del);
}


/* 将硬盘分区part上的inode清空 */
void inode_delete(struct partition* part, uint32_t inode_no, void* io_buf) {
   ASSERT(inode_no < 4096);
   struct inode_position inode_pos;
   inode_locate(part, inode_no, &inode_pos);     // inode位置信息会存入inode_pos
   ASSERT(inode_pos.sec_lba <= (part->start_lba + part->sec_cnt));
   
   char* inode_buf = (char*)io_buf;
   if (inode_pos.two_sec) {   // inode跨扇区,读入2个扇区
      /* 将原硬盘上的内容先读出来 */
      ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 2);
      /* 将inode_buf清0 */
      memset((inode_buf + inode_pos.off_size), 0, sizeof(struct inode));
      /* 用清0的内存数据覆盖磁盘 */
      ide_write(part->my_disk, inode_pos.sec_lba, inode_buf, 2);
   } else {    // 未跨扇区,只读入1个扇区就好
      /* 将原硬盘上的内容先读出来 */
      ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 1);
      /* 将inode_buf清0 */
      memset((inode_buf + inode_pos.off_size), 0, sizeof(struct inode));
      /* 用清0的内存数据覆盖磁盘 */
      ide_write(part->my_disk, inode_pos.sec_lba, inode_buf, 1);
   }
}

inode_release:函数功能是接受分区和i_no,然后根据文件的inode->i_sector[],找到占有的所有扇区地址,依次转化成block位图位,置0。最后将inode位图对应位置0并同步。
回收文件占用的扇区,无需对扇区清0,只需要对位图清0再同步到磁盘即可。

注意:普通文件扇区地址非连续,但是索引逻辑上连续,所以可以无需遍历140个all_block,当all_block=0时,那么文件必然到头。
但是目录可能出现,直接索引1为0,但是直接索引2记录了扇区地址的情况。这是因为目录项操作是单独的,对于这个目录而言,存在第一个扇区目录项被删除完了,扇区也回收了。但是第二个扇区的目录项没有动的情况。
这里为了简单,不判断普通文件还是目录,一律遍历140个扇区地址。

inode_delete:可有可无,功能接受i_no,将inode_table对应inode清0。
回收inode结点其实只需要将inode位图对应位清0再同步回磁盘即可,所以此函数可有可无,用于调试

删除目录项 dir.c之delete_dir_entry

/* 把分区part目录pdir中编号为inode_no的目录项删除 */
bool delete_dir_entry(struct partition* part, struct dir* pdir, uint32_t inode_no, void* io_buf) {
   struct inode* dir_inode = pdir->inode;
   uint32_t block_idx = 0, all_blocks[140] = {0};
   /* 收集目录全部块地址 */
   while (block_idx < 12) {
      all_blocks[block_idx] = dir_inode->i_sectors[block_idx];
      block_idx++;
   }
   if (dir_inode->i_sectors[12]) {
      ide_read(part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1);
   }

   /* 目录项在存储时保证不会跨扇区 */
   uint32_t dir_entry_size = part->sb->dir_entry_size;
   uint32_t dir_entrys_per_sec = (SECTOR_SIZE / dir_entry_size);       // 每扇区最大的目录项数目
   struct dir_entry* dir_e = (struct dir_entry*)io_buf;   
   struct dir_entry* dir_entry_found = NULL;
   uint8_t dir_entry_idx, dir_entry_cnt;
   bool is_dir_first_block = false;     // 目录的第1个块 

   /* 遍历所有块,寻找目录项 */
   block_idx = 0;
   while (block_idx < 140) {
      is_dir_first_block = false;
      if (all_blocks[block_idx] == 0) {
	 block_idx++;
	 continue;
      }
      dir_entry_idx = dir_entry_cnt = 0;
      memset(io_buf, 0, SECTOR_SIZE);
      /* 读取扇区,获得目录项 */
      ide_read(part->my_disk, all_blocks[block_idx], io_buf, 1);

      /* 遍历所有的目录项,统计该扇区的目录项数量及是否有待删除的目录项 */
      while (dir_entry_idx < dir_entrys_per_sec) {
	 if ((dir_e + dir_entry_idx)->f_type != FT_UNKNOWN) {
	    if (!strcmp((dir_e + dir_entry_idx)->filename, ".")) { 
	       is_dir_first_block = true;
	    } else if (strcmp((dir_e + dir_entry_idx)->filename, ".") && 
	       strcmp((dir_e + dir_entry_idx)->filename, "..")) {
	       dir_entry_cnt++;     // 统计此扇区内的目录项个数,用来判断删除目录项后是否回收该扇区
	       if ((dir_e + dir_entry_idx)->i_no == inode_no) {	  // 如果找到此i结点,就将其记录在dir_entry_found
		  ASSERT(dir_entry_found == NULL);  // 确保目录中只有一个编号为inode_no的inode,找到一次后dir_entry_found就不再是NULL
		  dir_entry_found = dir_e + dir_entry_idx;
		  /* 找到后也继续遍历,统计总共的目录项数 */
	       }
	    }
	 }
	 dir_entry_idx++;
      } 

      /* 若此扇区未找到该目录项,继续在下个扇区中找 */
      if (dir_entry_found == NULL) {
	 block_idx++;
	 continue;
      }

   /* 在此扇区中找到目录项后,清除该目录项并判断是否回收扇区,随后退出循环直接返回 */
      ASSERT(dir_entry_cnt >= 1);
    /* 除目录第1个扇区外,若该扇区上只有该目录项自己,则将整个扇区回收 */
      if (dir_entry_cnt == 1 && !is_dir_first_block) {
	 /* a 在块位图中回收该块 */
	 uint32_t block_bitmap_idx = all_blocks[block_idx] - part->sb->data_start_lba;
	 bitmap_set(&part->block_bitmap, block_bitmap_idx, 0);
	 bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);

	 /* b 将块地址从数组i_sectors或索引表中去掉 */
	 if (block_idx < 12) {
	    dir_inode->i_sectors[block_idx] = 0;
	 } else {    // 在一级间接索引表中擦除该间接块地址
	    /*先判断一级间接索引表中间接块的数量,如果仅有这1个间接块,连同间接索引表所在的块一同回收 */
	    uint32_t indirect_blocks = 0;
	    uint32_t indirect_block_idx = 12;
	    while (indirect_block_idx < 140) {
	       if (all_blocks[indirect_block_idx] != 0) {
		  indirect_blocks++;
	       }
	    }
	    ASSERT(indirect_blocks >= 1);  // 包括当前间接块

	    if (indirect_blocks > 1) {	  // 间接索引表中还包括其它间接块,仅在索引表中擦除当前这个间接块地址
	       all_blocks[block_idx] = 0; 
	       ide_write(part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1); 
	    } else {	// 间接索引表中就当前这1个间接块,直接把间接索引表所在的块回收,然后擦除间接索引表块地址
	       /* 回收间接索引表所在的块 */
	       block_bitmap_idx = dir_inode->i_sectors[12] - part->sb->data_start_lba;
	       bitmap_set(&part->block_bitmap, block_bitmap_idx, 0);
	       bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
	       
	       /* 将间接索引表地址清0 */
	       dir_inode->i_sectors[12] = 0;
	    }
	 }
      } else { // 仅将该目录项清空
	 memset(dir_entry_found, 0, dir_entry_size);
	 ide_write(part->my_disk, all_blocks[block_idx], io_buf, 1);
      }

   /* 更新i结点信息并同步到硬盘 */
      ASSERT(dir_inode->i_size >= dir_entry_size);
      dir_inode->i_size -= dir_entry_size;
      memset(io_buf, 0, SECTOR_SIZE * 2);
      inode_sync(part, dir_inode, io_buf);

      return true;
   }
   /* 所有块中未找到则返回false,若出现这种情况应该是serarch_file出错了 */
   return false;
}


函数接受分区part、目录pdir、i_no。功能是删除pdir目录的i_no对应文件的目录项。
(1)文件所在目录的目录项必须清0
(2)如果目录项独占一个块,且不是该目录最初块的话,回收扇区,则目录inode的索引要清0
(3)目录的inode的i_size是目录项大小总和,需修改并同步于磁盘。
(4)如果该索引独占间接索引块,那么此块也要回收,i_sector[12]置0,并同步inode和block位图。

首先,所有目录均有“。”、“。。”这两个目录项。
代码流程如下:
先在pdir目录的inode结点中,将140个索引全部读入all_block,目录项不跨扇区,所以依次遍历这140个扇区每个扇区的目录项,统计每个扇区目录项数目(不包括“。”、“。。”),同时寻找i_no符合的目录项。

没找到继续遍历下一个扇区,找到则进行判断目录项是否独占一个扇区且不是该目录最初块。不是就只清空目录项并同步该扇区。

是的话则回收扇区即清理对应位位图并同步,然后将目录的inode的对应索引清0,
如果是直接索引那么直接清0,如果是间接索引,如果该索引独占索引块,那就回收这个索引块即位图相应位置0并同步,再将i_sector[12]置0;如果不是独占,那就对应间接索引置0并同步至磁盘间接索引块

最后,由于pdir的inode的i_sector和i_size被修改了,需要同步回磁盘的inode_table。

fs.c之sys_unlink


/* 删除文件(非目录),成功返回0,失败返回-1 */
int32_t sys_unlink(const char* pathname) {
   ASSERT(strlen(pathname) < MAX_PATH_LEN);

   /* 先检查待删除的文件是否存在 */
   struct path_search_record searched_record;
   memset(&searched_record, 0, sizeof(struct path_search_record));
   int inode_no = search_file(pathname, &searched_record);
   ASSERT(inode_no != 0);
   if (inode_no == -1) {
      printk("file %s not found!\n", pathname);
      dir_close(searched_record.parent_dir);
      return -1;
   }
   if (searched_record.file_type == FT_DIRECTORY) {
      printk("can`t delete a direcotry with unlink(), use rmdir() to instead\n");
      dir_close(searched_record.parent_dir);
      return -1;
   }

   /* 检查是否在已打开文件列表(文件表)中 */
   uint32_t file_idx = 0;
   while (file_idx < MAX_FILE_OPEN) {
      if (file_table[file_idx].fd_inode != NULL && (uint32_t)inode_no == file_table[file_idx].fd_inode->i_no) {
	 break;
      }
      file_idx++;
   }
   if (file_idx < MAX_FILE_OPEN) {
      dir_close(searched_record.parent_dir);
      printk("file %s is in use, not allow to delete!\n", pathname);
      return -1;
   }
   ASSERT(file_idx == MAX_FILE_OPEN);
   
   /* 为delete_dir_entry申请缓冲区 */
   void* io_buf = sys_malloc(SECTOR_SIZE + SECTOR_SIZE);
   if (io_buf == NULL) {
      dir_close(searched_record.parent_dir);
      printk("sys_unlink: malloc for io_buf failed\n");
      return -1;
   }

   struct dir* parent_dir = searched_record.parent_dir;  
   delete_dir_entry(cur_part, parent_dir, inode_no, io_buf);
   inode_release(cur_part, inode_no);
   sys_free(io_buf);
   dir_close(searched_record.parent_dir);
   return 0;   // 成功删除文件 
}

接受一个pathname
先用searched_record检查路径pathname
若目录或者位于打开文件表则报错。
否则就调用delete_dir_entry,清除目录项
inode_release,清除inode结点。

main.c

int main(void) {
   put_str("I am kernel\n");
   init_all();

   intr_enable();
   process_execute(u_prog_a, "u_prog_a");
   process_execute(u_prog_b, "u_prog_b");   
	thread_start("k_thread_a", 31, k_thread_a, "argA ");
	thread_start("k_thread_b", 31, k_thread_b, "argB ");
	
	printf("/file1 delete %s!\n", sys_unlink("/file1") == 0 ? "done" : "fall");		
	while(1);
   return 0;
}

调了个sys_unlink删除file1

实验结果

在这里插入图片描述

2.创建目录

fs.c之sys_mkdir


/* 创建目录pathname,成功返回0,失败返回-1 */
int32_t sys_mkdir(const char* pathname) {
   uint8_t rollback_step = 0;	       // 用于操作失败时回滚各资源状态
   void* io_buf = sys_malloc(SECTOR_SIZE * 2);
   if (io_buf == NULL) {
      printk("sys_mkdir: sys_malloc for io_buf failed\n");
      return -1;
   }

   struct path_search_record searched_record;
   memset(&searched_record, 0, sizeof(struct path_search_record));
   int inode_no = -1;
   inode_no = search_file(pathname, &searched_record);
   if (inode_no != -1) {      // 如果找到了同名目录或文件,失败返回
      printk("sys_mkdir: file or directory %s exist!\n", pathname);
      rollback_step = 1;
      goto rollback;
   } else {	     // 若未找到,也要判断是在最终目录没找到还是某个中间目录不存在
      uint32_t pathname_depth = path_depth_cnt((char*)pathname);
      uint32_t path_searched_depth = path_depth_cnt(searched_record.searched_path);
      /* 先判断是否把pathname的各层目录都访问到了,即是否在某个中间目录就失败了 */
      if (pathname_depth != path_searched_depth) {   // 说明并没有访问到全部的路径,某个中间目录是不存在的
	 printk("sys_mkdir: can`t access %s, subpath %s is`t exist\n", pathname, searched_record.searched_path);
	 rollback_step = 1;
	 goto rollback;
      }
   }

   struct dir* parent_dir = searched_record.parent_dir;
   /* 目录名称后可能会有字符'/',所以最好直接用searched_record.searched_path,无'/' */
   char* dirname = strrchr(searched_record.searched_path, '/') + 1;

   inode_no = inode_bitmap_alloc(cur_part); 
   if (inode_no == -1) {
      printk("sys_mkdir: allocate inode failed\n");
      rollback_step = 1;
      goto rollback;
   }

   struct inode new_dir_inode;
   inode_init(inode_no, &new_dir_inode);	    // 初始化i结点

   uint32_t block_bitmap_idx = 0;     // 用来记录block对应于block_bitmap中的索引
   int32_t block_lba = -1;
/* 为目录分配一个块,用来写入目录.和.. */
   block_lba = block_bitmap_alloc(cur_part);
   if (block_lba == -1) {
      printk("sys_mkdir: block_bitmap_alloc for create directory failed\n");
      rollback_step = 2;
      goto rollback;
   }
   new_dir_inode.i_sectors[0] = block_lba;
   /* 每分配一个块就将位图同步到硬盘 */
   block_bitmap_idx = block_lba - cur_part->sb->data_start_lba;
   ASSERT(block_bitmap_idx != 0);
   bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);
   
   /* 将当前目录的目录项'.'和'..'写入目录 */
   memset(io_buf, 0, SECTOR_SIZE * 2);	 // 清空io_buf
   struct dir_entry* p_de = (struct dir_entry*)io_buf;
   
   /* 初始化当前目录"." */
   memcpy(p_de->filename, ".", 1);
   p_de->i_no = inode_no ;
   p_de->f_type = FT_DIRECTORY;

   p_de++;
   /* 初始化当前目录".." */
   memcpy(p_de->filename, "..", 2);
   p_de->i_no = parent_dir->inode->i_no;
   p_de->f_type = FT_DIRECTORY;
   ide_write(cur_part->my_disk, new_dir_inode.i_sectors[0], io_buf, 1);

   new_dir_inode.i_size = 2 * cur_part->sb->dir_entry_size;

   /* 在父目录中添加自己的目录项 */
   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);	 // 清空io_buf
   if (!sync_dir_entry(parent_dir, &new_dir_entry, io_buf)) {	  // sync_dir_entry中将block_bitmap通过bitmap_sync同步到硬盘
      printk("sys_mkdir: sync_dir_entry to disk failed!\n");
      rollback_step = 2;
      goto rollback;
   }

   /* 父目录的inode同步到硬盘 */
   memset(io_buf, 0, SECTOR_SIZE * 2);
   inode_sync(cur_part, parent_dir->inode, io_buf);

   /* 将新创建目录的inode同步到硬盘 */
   memset(io_buf, 0, SECTOR_SIZE * 2);
   inode_sync(cur_part, &new_dir_inode, io_buf);

   /* 将inode位图同步到硬盘 */
   bitmap_sync(cur_part, inode_no, INODE_BITMAP);

   sys_free(io_buf);

   /* 关闭所创建目录的父目录 */
   dir_close(searched_record.parent_dir);
   return 0;

/*创建文件或目录需要创建相关的多个资源,若某步失败则会执行到下面的回滚步骤 */
rollback:	     // 因为某步骤操作失败而回滚
   switch (rollback_step) {
      case 2:
	 bitmap_set(&cur_part->inode_bitmap, inode_no, 0);	 // 如果新文件的inode创建失败,之前位图中分配的inode_no也要恢复 
      case 1:
	 /* 关闭所创建目录的父目录 */
	 dir_close(searched_record.parent_dir);
	 break;
   }
   sys_free(io_buf);
   return -1;
}

接受路径pathname
(1)先确认新目录名在文件系统不存在
(2)为新目录创建inode,
(3)为新目录分配一个扇区存储目录项
(4)在该扇区创建“。”、“。。”两个目录项
(5)父目录中添加新目录的目录项
以上涉及同步的资源有:inode_table中的新目录的inode、block_bitmp、分配的目录扇区、inode_table中的父目录的inode。

main.c

int main(void) {
   put_str("I am kernel\n");
   init_all();

   intr_enable();
   process_execute(u_prog_a, "u_prog_a");
   process_execute(u_prog_b, "u_prog_b");   
	thread_start("k_thread_a", 31, k_thread_a, "argA ");
	thread_start("k_thread_b", 31, k_thread_b, "argB ");
	
	printf("/dir1/subdir1 create %s!\n",\
	sys_mkdir("/dir1/subdir1") == 0 ? "done" : "fall");	
	
	printf("/dir1 create %s!\n", sys_mkdir("/dir1")== 0 ? "done" : "fall");	
	
	printf("now /dir1/subdir1 create %s\n", \	
	sys_mkdir("/dir1/subdir1") == 0 ? "done": "fall");

	int fd = sys_open("/dir1/subfir1/file2", O_CREAT|O_RDWR);
	if(fd != -1){
		printf("dir1/subdir1/file2/ create done!\n");
		sys_write(fd, "Catch me if you can!\n", 21);
		sys_lseek(fd, 0, SEEK_SET);
		char buf[32] = {0};
		sys_read(fd, buf, 21);
		printf("/dir1/subdir1/file2 says:\n%s", buf);
		sys_close(fd);
	}
	while(1);
   return 0;
}

实验结果

在这里插入图片描述
main.c对之前功能来了个小集合

sys_mkdir:先创建/dir1/subdir1,但/dir1不存在故失败。
然后创建/dir1,再创建/dir1/subdir1成功。
sys_open:参数O_CREAT和O_RDWR相或,会过case O_CREAT。创建file2
然后就是sys_write写入file2
sys_lseek调正读写指针
sys_read读出写的玩意。

3. 遍历目录

fs.c 之 sys_opendir和sys_closedir


/* 目录打开成功后返回目录指针,失败返回NULL */
struct dir* sys_opendir(const char* name) {
   ASSERT(strlen(name) < MAX_PATH_LEN);
   /* 如果是根目录'/',直接返回&root_dir */
   if (name[0] == '/' && (name[1] == 0 || name[0] == '.')) {
      return &root_dir;
   }

   /* 先检查待打开的目录是否存在 */
   struct path_search_record searched_record;
   memset(&searched_record, 0, sizeof(struct path_search_record));
   int inode_no = search_file(name, &searched_record);
   struct dir* ret = NULL;
   if (inode_no == -1) {	 // 如果找不到目录,提示不存在的路径 
      printk("In %s, sub path %s not exist\n", name, searched_record.searched_path); 
   } else {
      if (searched_record.file_type == FT_REGULAR) {
	 printk("%s is regular file!\n", name);
      } else if (searched_record.file_type == FT_DIRECTORY) {
	 ret = dir_open(cur_part, inode_no);
      }
   }
   dir_close(searched_record.parent_dir);
   return ret;
}

/* 成功关闭目录dir返回0,失败返回-1 */
int32_t sys_closedir(struct dir* dir) {
   int32_t ret = -1;
   if (dir != NULL) {
      dir_close(dir);
      ret = 0;
   }
   return ret;
}

sys_opendir
根据路径name,要么返回根目录,要么调用dir_open返回dir,其中dir_open调用了inode_open,
当目录未打开的时候,inode_open回从磁盘把inode拷贝到内存,加入打开文件表,打开队列,并返回inode。
当目录已经打开,inode_open会直接从i结点打开队列找到inode返回。
sys_closedir调用dir_close,dir_close自带判断根目录逻辑。

main.c

int main(void) {
   put_str("I am kernel\n");
   init_all();

   intr_enable();
   process_execute(u_prog_a, "u_prog_a");
   process_execute(u_prog_b, "u_prog_b");   
	thread_start("k_thread_a", 31, k_thread_a, "argA ");
	thread_start("k_thread_b", 31, k_thread_b, "argB ");
	
	struct dir* p_dir = sys_opendir("/dir1/subdir1");
	if (p_dir){
		printf("/dir1/subdir1 open done!\n");		
		if(sys_closedir(p_dir) == 0){
			printf("/dir1/subdir1 close done!\n");	
		}else{
			printf("/dir1/subdir1 close fail!\n");	
		}	
	}else{
		printf("/dir1/subdir1 open fail!\n");
	}	

	while(1);
   return 0;
}

实验结果

读取一个目录项 dir.c之dir_read


/* 读取目录,成功返回1个目录项,失败返回NULL */
struct dir_entry* dir_read(struct dir* dir) {
   struct dir_entry* dir_e = (struct dir_entry*)dir->dir_buf;
   struct inode* dir_inode = dir->inode; 
   uint32_t all_blocks[140] = {0}, block_cnt = 12;
   uint32_t block_idx = 0, dir_entry_idx = 0;
   while (block_idx < 12) {
      all_blocks[block_idx] = dir_inode->i_sectors[block_idx];
      block_idx++;
   }
   if (dir_inode->i_sectors[12] != 0) {	     // 若含有一级间接块表
      ide_read(cur_part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1);
      block_cnt = 140;
   }
   block_idx = 0;

   uint32_t cur_dir_entry_pos = 0;	  // 当前目录项的偏移,此项用来判断是否是之前已经返回过的目录项
   uint32_t dir_entry_size = cur_part->sb->dir_entry_size;
   uint32_t dir_entrys_per_sec = SECTOR_SIZE / dir_entry_size;	 // 1扇区内可容纳的目录项个数
   /* 因为此目录内可能删除了某些文件或子目录,所以要遍历所有块 */
   while (block_idx < block_cnt) {
      if (dir->dir_pos >= dir_inode->i_size) {
	 return NULL;
      }
      if (all_blocks[block_idx] == 0) {     // 如果此块地址为0,即空块,继续读出下一块
	 block_idx++;
	 continue;
      }
      memset(dir_e, 0, SECTOR_SIZE);
      ide_read(cur_part->my_disk, all_blocks[block_idx], dir_e, 1);
      dir_entry_idx = 0;
      /* 遍历扇区内所有目录项 */
      while (dir_entry_idx < dir_entrys_per_sec) {
	 if ((dir_e + dir_entry_idx)->f_type) {	 // 如果f_type不等于0,即不等于FT_UNKNOWN
	    /* 判断是不是最新的目录项,避免返回曾经已经返回过的目录项 */
	    if (cur_dir_entry_pos < dir->dir_pos) {
	       cur_dir_entry_pos += dir_entry_size;
	       dir_entry_idx++;
	       continue;
	    }
	    ASSERT(cur_dir_entry_pos == dir->dir_pos);
	    dir->dir_pos += dir_entry_size;	      // 更新为新位置,即下一个返回的目录项地址
	    return dir_e + dir_entry_idx; 
	 }
	 dir_entry_idx++;
      }
      block_idx++;
   }
   return NULL;
}

dir_read接受一个dir,返回dir中dir_pos指向的的一个目录项。
当调用sys_opendir时,该目录的inode被保存于dir,dir的dir_pos也会被置为0。
所以第一次调用dir_read的时候,返回的就是第一个有效目录项,返回前dir_pos +=dir_entry_size,第二次调用dir_read后,就返回dir_pos处的下一个有效目录项。
有效是因为:目录的索引不连续,有些索引为0,即使不为0的索引,里面的目录项也是断断续续,所以dir_pos记录的是有效目录项偏移。

fs.c之sys_readdir


/* 读取目录dir的1个目录项,成功后返回其目录项地址,到目录尾时或出错时返回NULL */
struct dir_entry* sys_readdir(struct dir* dir) {
   ASSERT(dir != NULL);
   return dir_read(dir);
}

/* 把目录dir的指针dir_pos置0 */
void sys_rewinddir(struct dir* dir) {
   dir->dir_pos = 0;
}

与dir_read一样的。为了系统调用内核实现部分命名统一。

main.c

int main(void) {
   put_str("I am kernel\n");
   init_all();

   intr_enable();
   process_execute(u_prog_a, "u_prog_a");
   process_execute(u_prog_b, "u_prog_b");   
	thread_start("k_thread_a", 31, k_thread_a, "argA ");
	thread_start("k_thread_b", 31, k_thread_b, "argB ");
	
	struct dir* p_dir = sys_opendir("/dir1/subdir1");
	if (p_dir){
		printf("/dir1/subdir1 open done!\ncontent:\n");		
		char* type =NULL;
		struct dir_entry* dir_e = NULL;
		while((dir_e = sys_readdir(p_dir))){
			if(dir_e->f_type = FT_REGULAR){
				type = "regular";
			}else{
				type = "directory";
			}
			printf("	%s   %s\n",type, dir_e->filename);
								
		}		
		if(sys_closedir(p_dir) == 0){
			printf("/dir1/subdir1 close done!\n");	
		}else{
			printf("/dir1/subdir1 close fail!\n");	
		}	
	
	}else{
		printf("/dir1/subdir1 open fail!\n");
	}	

	while(1);
   return 0;
}

while((dir_e = sys_readdir(p_dir)))
依次遍历目录所有目录项,遍历到最后一个就会返回NULL。

实验结果

在这里插入图片描述

标签:真象,遍历,idx,实验,dir,entry,inode,目录,block
来源: https://blog.csdn.net/mxy990811/article/details/122740223

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有