实验25.创建文件
已完成实验
已完成实验链接
简介
实验 25. 创建文件
总结
inode 就是文件
- i_no 就是 inode 号
- i_sectors 是块地址数组,表示这个文件的内容是那些块构成的.
- 如果是文件,那么块的内容是文件的内容
- 如果是目录,那么这些块的内容是一个个目录项
dir_entry 目录项
- 是目录文件的数据块的内容
文件描述符如何转换为 inode
- 首先在内核有一个 所有线程打开的文件表 file_tbale
struct file file_table[MAX_FILE_OPEN];
- 这个表的每一项是一个文件结构体
/// @brief 文件结构
struct file {
uint32_t fd_pos; // 记录当前文件操作的偏移地址
uint32_t fd_flag;
struct inode\* fd_inode;
};
- 然后每个线程或进程 pcb 有一个文件描述符数组
struct task_struct {...int32_t fd_table[MAX_FILES_OPEN_PER_PROC];...
};
- 其中 索引 0 标准输入 索引 1 标准输出 索引 2 标准错误
- 其他文件描述符假如是 4,那么假设文件描述符数组 fd_table[4] = 7
- 那么拿着 7 去找所有线程打开的文件表 file_tbale[7]的 file 结构体
- file 结构体就能定位到 inode,也可以 fseek,也能判断读写 flags
主要代码
thread.h
+#define MAX_FILES_OPEN_PER_PROC 8
+/// @brief 进程或线程的pcb process control block 4096字节/// 一个pcb包含1个中断栈,1个线程栈,struct task_struct {uint32_t* self_kstack; // pcb中线程栈的地址pid_t pid;enum task_status status; // 状态char name[16]; //uint8_t priority; // 线程优先级uint8_t ticks; // 每次在处理器上执行的时间嘀嗒数uint32_t elapsed_ticks; // 处理器上执行的时间嘀嗒总数+ int32_t fd_table[MAX_FILES_OPEN_PER_PROC]; // 文件描述符数组
+struct list_elem general_tag; // 放入就绪队列struct list_elem all_list_tag; // 放入全部队列// 用户进程使用 内核线程不使用uint32_t* pgdir; // 指向用户的页目录项struct virtual_addr userprog_vaddr; // 用户进程的虚拟地址池struct mem_block_desc u_block_desc[DESC_CNT]; // 用户进程内存块描述符uint32_t stack_magic; // pcb魔数,用于检测栈的溢出};
thread.c
/// @brief 初始化pcb/// @param pthread pcb/// @param name 线程名/// @param prio 优先级void init_thread(struct task_struct* pthread, char* name, int prio) {// 清0memset(pthread, 0, sizeof(*pthread));pthread->pid = allocate_pid(); // 设置pid// 设置状态if (pthread == main_thread) { // 如果是主线程pcbpthread->status = TASK_RUNNING;} else {pthread->status = TASK_READY;}// 初始化名字、优先级、时钟、总时钟、魔数strcpy(pthread->name, name);pthread->priority = prio;pthread->ticks = prio;pthread->elapsed_ticks = 0;pthread->pgdir = NULL;pthread->stack_magic = 0x19870916; // 自定义的魔数+ // 文教描述符数组
+ pthread->fd_table[0] = 0; // 标准输入
+ pthread->fd_table[1] = 1; // 标准输出
+ pthread->fd_table[2] = 2; // 标准错误输出
+ uint8_t fd_idx = 3; // 剩下都是-1
+ while (fd_idx < MAX_FILES_OPEN_PER_PROC) {
+ pthread->fd_table[fd_idx] = -1;
+ fd_idx++;
+ }
+// self_kstack是线程自己在内核态下使用的栈顶地址pthread->self_kstack = (uint32_t*)((uint32_t)pthread + PG_SIZE);}
inode.h
/// @brief inode结构
struct inode {uint32_t i_no; // inode编号// 当此inode是文件时,i_size是指文件大小,// 若此inode是目录,i_size是指该目录下所有目录项大小之和uint32_t i_size;uint32_t i_open_cnts; // 记录此文件被打开的次数bool write_deny; // 写文件不能并行,进程写文件前检查此标识// 数据块地址数组: i_sectors[0-11]是直接块, i_sectors[12]用来存储一级间接块指针// 里面的每一个数据块都是数据,文件就是本身数据,目录就是一个个目录项uint32_t i_sectors[13];struct list_elem inode_tag;
};
inode.c
/// @brief inode位置信息
struct inode_position {bool two_sec; // inode是否跨扇区uint32_t sec_lba; // inode所在的扇区号uint32_t off_size; // inode在扇区内的字节偏移量
};/// @brief 打开inode
/// 先从分区已打开inode中找,再从硬盘找,找到后inode的打开次数+1
/// @param part 分区
/// @param inode_no inode号
/// @return inode的数据结构指针 注意:这个指针指向的结构体在堆内存
struct inode* inode_open(struct partition* part, uint32_t inode_no);/// @brief 同步inode
/// 把内存中inode的信息同步到硬盘
/// @param part 分区
/// @param inode inode指针
/// @param io_buf 从硬盘读出的扇区数据缓存
void inode_sync(struct partition* part, struct inode* inode, void* io_buf);/// @brief 关闭inode
/// 如果打开多次,那么次数-1,如果次数为0,那么删除在内存中的indoe
/// @param inode inode指针
void inode_close(struct inode* inode);/// @brief 删除inode
/// 把硬盘中inode表中的inode结构体全部归0
/// 感觉不太需要,只要把inode位图中的位给0就可以代表了
/// @param part 分区
/// @param inode_no inode号
/// @param io_buf 从硬盘读出的扇区数据缓存
void inode_delete(struct partition* part, uint32_t inode_no, void* io_buf);/// @brief 释放inode
/// 在硬盘inode位图中标志该inode为空闲,并在块位图中标记他的所有块为空闲
/// @param part 分区
/// @param inode_no inode号
void inode_release(struct partition* part, uint32_t inode_no);/// @brief 初始化inode
/// 除了inode号全部默认值
/// @param inode_no inode号
/// @param new_inode inode指针
void inode_init(uint32_t inode_no, struct inode* new_inode);
dir.h
#define MAX_FILE_NAME_LEN 16 // 最大文件名长度/// @brief 目录结构
struct dir {struct inode* inode;uint32_t dir_pos; // 记录在目录内的偏移uint8_t dir_buf[512]; // 目录的数据缓存
};/// @brief 目录项结构
struct dir_entry {char filename[MAX_FILE_NAME_LEN]; // 普通文件或目录名称uint32_t i_no; // 普通文件或目录对应的inode编号enum file_types f_type; // 文件类型
};
dir.c
struct dir root_dir; // 根目录/// @brief 打开根目录
/// 打开inode0
/// @param part
void open_root_dir(struct partition* part);/// @brief 打开目录
/// 打开inode号的目录,返回目录指针
/// 堆内存申请的结构体,返回这个结构体的指针
/// @param part 分区
/// @param inode_no 目录inode号
/// @return 目录指针
struct dir* dir_open(struct partition* part, uint32_t inode_no);/// @brief 关闭目录
/// 关闭inode号的目录,释放目录结构体
/// @param dir
void dir_close(struct dir* dir);/// @brief 寻找文件
/// 在part分区内的pdir目录内寻找名为name的文件或目录
/// @param part 分区
/// @param pdir 目录
/// @param name 文件或目录名
/// @param dir_e 保存结果
/// @return 找到后返回true并将其目录项存入dir_e,否则返回false
bool search_dir_entry(struct partition* part, struct dir* pdir, const char* name, struct dir_entry* dir_e);/// @brief 创建目录项
/// 赋值目录项指针指向的目录项的文件名,inode号,文件类型
/// @param filename 文件名
/// @param inode_no inode号
/// @param file_type 文件类型
/// @param p_de 目录项指针
void create_dir_entry(char* filename, uint32_t inode_no, uint8_t file_type, struct dir_entry* p_de);/// @brief 同步目录项
/// 在硬盘中将目录项p_de写入父目录parent_dir的数据块中,io_buf由主调函数提供
/// @param parent_dir 父目录指针
/// @param p_de 目录项指针
/// @param io_buf 缓冲区由外部提供
/// @return 成功返回true,失败返回false
bool sync_dir_entry(struct dir* parent_dir, struct dir_entry* p_de, void* io_buf);
file.h
/// @brief 文件结构
struct file {uint32_t fd_pos; // 记录当前文件操作的偏移地址,以0为起始,最大为文件大小-1uint32_t fd_flag;struct inode* fd_inode;
};/// @brief 标准输入输出描述符
enum std_fd {stdin_no, // 0 标准输入stdout_no, // 1 标准输出stderr_no // 2 标准错误
};/// @brief 位图类型
enum bitmap_type {INODE_BITMAP, // inode位图BLOCK_BITMAP // 块位图
};#define MAX_FILE_OPEN 32 // 系统可打开的最大文件数
file.c
/// @brief 全局打开的文件表
struct file file_table[MAX_FILE_OPEN];/// @brief 从全局打开的文件表中获取一个空闲位置
/// @return 成功返回下标,失败返回-1
int32_t get_free_slot_in_global(void)/// @brief 分配一个描述符索引
/// 将全局描述符下标安装到进程或线程自己的文件描述符数组fd_table中
/// @param globa_fd_idx 全局描述符下标
/// @return 成功返回下标,失败返回-1
int32_t pcb_fd_install(int32_t globa_fd_idx)/// @brief 在块位图中分配一个位
/// @param part 分区
/// @return 扇区地址
int32_t block_bitmap_alloc(struct partition* part)/// @brief 同步内存位图bit_idx比特位所在的扇区中到硬盘
/// @param part 分区
/// @param bit_idx 比特偏移
/// @param btmp_type 位图类型
void bitmap_sync(struct partition* part, uint32_t bit_idx, uint8_t btmp_type)/// @brief 创建文件
/// @param parent_dir 父目录
/// @param filename 文件名
/// @param flag 打开文件的选项
/// @return 若成功则返回文件描述符,否则返回-1
int32_t file_create(struct dir* parent_dir, char* filename, uint8_t flag)
fs.h
#define MAX_FILES_PER_PART 4096 // 每个分区所支持最大创建的文件数
#define BITS_PER_SECTOR 4096 // 每扇区的位数 512字节 = 4096比特
#define SECTOR_SIZE 512 // 扇区字节大小
#define BLOCK_SIZE SECTOR_SIZE // 块字节大小#define MAX_PATH_LEN 512 // 路径最大长度/// @brief 文件类型
enum file_types {FT_UNKNOWN, // 不支持的文件类型FT_REGULAR, // 普通文件FT_DIRECTORY // 目录
};/// @brief 打开文件的选项
enum oflags {O_RDONLY, // 只读O_WRONLY, // 只写O_RDWR, // 读写O_CREAT = 4 // 创建
};/// @brief 用来记录查找文件过程中已找到的上级路径,也就是查找文件过程中"走过的地方"
struct path_search_record {char searched_path[MAX_PATH_LEN]; // 查找过程中的父路径struct dir* parent_dir; // 文件或目录所在的直接父目录enum file_types file_type; // 找到的是普通文件还是目录,找不到将为未知类型(FT_UNKNOWN)
};
fs.c
/// @brief 将最上层路径名称解析出来
/// @param pathname 路径 /a/b/c
/// @param name_store a
/// @return /b/c
static int search_file(const char* pathname, struct path_search_record* searched_record)/// @brief 查找文件
/// @param pathname 文件名
/// @param searched_record 保存结果
/// @return 若找到则返回其inode号,否则返回-1
static int search_file(const char* pathname, struct path_search_record* searched_record)/// @brief 打开或创建文件
/// @param pathname 文件路径
/// @param flags
/// @return 成功后,返回文件描述符,否则返回-1
int32_t sys_open(const char* pathname, uint8_t flags)/// @brief 在磁盘上搜索文件系统,若没有则格式化分区创建文件系统
void filesys_init() {...// 确定默认操作的分区char default_part[8] = "sdb1";// 挂载分区list_traversal(&partition_list, mount_partition, (int)default_part);// 将当前分区的根目录打开open_root_dir(cur_part);// 初始化文件表uint32_t fd_idx = 0;while (fd_idx < MAX_FILE_OPEN) { file_table[fd_idx++].fd_inode = NULL; }
}
init.c
// 文件: init.c
// 时间: 2024-07-22
// 来自: ccj
// 描述: 内核所有初始化操作#include "init.h"
#include "print.h"
#include "interrupt.h"
#include "timer.h"
#include "memory.h"
#include "thread.h"
#include "keyboard.h"
#include "console.h"
#include "tss.h"
#include "syscall-init.h"
#include "ide.h"
#include "fs.h"/// @brief 内核所有初始化
void init_all() {put_str("init all\n");idt_init(); // 初始化中断timer_init(); // 调快时钟、注册时钟中断来调度线程mem_init(); // 初始化内存管理系统thread_init(); // 初始化线程console_init(); // 控制台初始化最好放在开中断之前keyboard_init(); // 键盘初始化tss_init(); // tss初始化syscall_init(); // 初始化系统调用intr_enable(); // 打开中断ide_init(); // 初始化硬盘filesys_init(); // 初始化文件系统
}
main.c
int main(void) {put_str("I am kernel\n");init_all();process_execute(u_prog_a, "user_prog_a");process_execute(u_prog_b, "user_prog_b");thread_start("k_thread_a", 31, k_thread_a, "argA ");thread_start("k_thread_b", 31, k_thread_b, "argB ");sys_open("/file1", O_CREAT);while (1);return 0;
}