实验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) {
     // 清0
     memset(pthread, 0, sizeof(*pthread));
     pthread->pid = allocate_pid();  // 设置pid

     // 设置状态
     if (pthread == main_thread) {  // 如果是主线程pcb
         pthread->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为起始,最大为文件大小-1
    uint32_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;
}

在这里插入图片描述
在这里插入图片描述

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值