实验25.创建文件

news2024/11/14 21:51:44

已完成实验

已完成实验链接

简介

实验 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;
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1993836.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

思科CCIE最新考证流程

CCIE CCIE&#xff0c;全称Cisco Certified Internetwork Expert,是美国Cisco公司于1993年开始推出的专家级认证考试。被全球公认为IT业最权威的认证&#xff0c;是全球Internetworking领域中最顶级的认证证书。 CCIE方向 CCIE主要有六大方向&#xff1a;企业基础架构Enterp…

JDK源码——Atomic包(一)

包介绍 JDK的atomic包提供了一组原子类&#xff0c;用于多线程环境中的原子操作&#xff0c;确保线程安全和高性能。 Atomic包是Java在并发编程中的重要工具&#xff0c;它利用CAS&#xff08;Compare-And-Swap&#xff09;机制保证操作的原子性&#xff0c;同时避免了重量级…

3.Redis数据类型(二)

LIST List 是一个简单的双向链表&#xff0c;支持从两端进行插入和删除操作。 常用命令&#xff1a; lpush/rpush/lrange lpush 插入一个或多个元素到列表的左端。 rpush 插入一个或多个元素到列表的右端。 lrange key start stop 获取元素&#xff08;前闭后闭&#xff0…

构建可刷卡手持终端,思路与必备元素剖析-SAAS 本地化及未来之窗行业应用跨平台架构

构建可刷卡手持终端&#xff0c;思路与必备元素剖析 一、终端开发必要性 1.终端携带方便&#xff0c;适合空间小&#xff0c;外出 2.可供电&#xff0c;外带设备比较方便 3.大多数终端可以不需要网络独立使用&#xff0c;适合特殊场景 二、终端软件爱基本功能 1.便捷的终端…

Java重修笔记 第三十天 异常

异常的分类 1. Error&#xff08;错误&#xff09;&#xff1a;Java虚拟机无法解决的致命问题&#xff0c;例如StackOverflowError[栈溢出] 2. Exception&#xff08;异常&#xff09;&#xff1a;其它因编程错误或偶然的外在因素导致的一般性问题&#xff0c;可以使用针对性…

【iOS多线程(四)】线程安全+13种锁

线程安全13种锁 线程安全1. 为什么要线程安全出现线程安全的原理解决方法 2. 自旋锁和互斥锁自旋锁(Spin lock)互斥锁两种锁的加锁原理对比两种锁的应用 3. 13种锁1. OSSpinLock (已弃用&#xff09;2. os_unfair_lock3.pthread_mutex 4. NSLock5. NSRecursiveLock6. NSConditi…

C++ IOStream

IOStream 类流特性 不可赋值和复制缓冲重载了<< >> 状态位 示例 状态位操作函数coutcin getget(s,n)/get(s,n,d):getline otherif(!fs)/while(cin) operator void*()与 operator!()代码示例 File Stream open 函数 文件打开方式 文件读写 读写接口 一次读一个字符…

SpringBoot学习之EasyExcel解析合并单元格(三十九)

本解析主要采用反射来修改EasyExcel 返回的默认数据结构实现。 一、待解析表格 二、依赖 全部pom.xml文件如下,仅作参考: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLo…

LLM - 使用 HuggingFace + Ollama 部署最新大模型 (GGUF 格式 与 Llama 3.1)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/141028040 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 Ollama…

创建一个自己的列表窗口

文章目录 背景&#xff1a;在QT的设计中&#xff0c;对于控件库提供的控件满足不了项目的需求&#xff0c;就像自定义一些控件&#xff0c;本文是自定义一个列表窗口。效果展示 一、创建基本的QT模板&#xff1a;1.创建mainwindow2.创建VerticalTextDelegate 二&#xff1a; 插…

零拷贝的发展历程

零拷贝 零拷贝是指计算机执行 IO 操作时&#xff0c;CPU 不需要将数据从一个存储区域复制到另一个存储区域&#xff0c;从而可以减少上下文切换以及 CPU的拷贝时间。它是一种I/O 操作优化技术。 传统IO的执行流程&#xff1a;传统的 IO 流程&#xff0c;包括 read 读 和 write…

2024.8.08(python)

一、搭建python环境 1、检查是否安装python [rootpython ~]# yum list installed | grep python [rootpython ~]# yum list | grep python3 2、安装python3 [rootpython ~]# yum -y install python3 安装3.12可以使用源码安装 3、查看版本信息 [rootpython ~]# python3 --vers…

C++(类和对象.下)

类型转换 先给出以下代码&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> using namespace std; class B { private:int _a1; public:B(int a){_a1 a;}void Print() {cout << _a1<< endl;} }; int main() {//实例化对象1B b(1);b.Pri…

video-retalking部署安装,在服务器Ubuntu22.04系统下

video-retalking部署安装&#xff0c;在服务器Ubuntu22.04系统下 一、ubuntu基本环境配置1.更新包列表&#xff1a;2. 安装英伟达显卡驱动2.1 使用wget在命令行下载驱动包2.2 更新软件列表和安装必要软件、依赖2.2 卸载原有驱动2.3 安装驱动2.4 安装CUDA2.5 环境变量配置 二、安…

基于YOLOv10深度学习的交通信号灯检测识别系统【python源码+Pyqt5界面+数据集+训练代码】红绿灯检测、目标检测、人工智能

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

易语言窗口1以及其它子窗口在任务栏显示的方法以及源代码

大家都知道在启动子程序中载入窗口里&#xff0c;窗口就不在任务栏中显示了 用什么办法可以解决这个问题呢 本例程教源码教你如何解决

零拷贝:原理与实现

1. 简介 I/O 或输入/输出通常意味着中央处理器 (CPU) 与外部设备&#xff08;如磁盘、鼠标、键盘等&#xff09;之间的读写。在深入研究零拷贝之前&#xff0c;有必要指出磁盘 I/O&#xff08;包括磁盘设备和其他块导向设备&#xff09;和网络 I/O 之间的区别。 磁盘 I/O 的常…

Vue3简单介绍和快速体验

目录 前言 1. Vue3介绍 1.1 Vue的两个核心功能&#xff1a; 1.2 Vue作者介绍 2. Vue3快速体验(非工程化方式) 2.1 所有代码 2.2 导入js 2.3 一些基本指令 2.4 app对象的创建和挂载 ​ 前言 在学习Vue3之前,自己已经学习过的vue2了,在学习Vue3后还是能感觉到2和3在使用…

媒体资讯视频数据采集-yt-dlp-python实际使用-下载视频

对于视频二创等一些业务场景&#xff0c;可能要采集youtube等的相关媒体视频资源&#xff0c;使用[yt-dlp](https://github.com/yt-dlp/yt-dlp)是一个不错的选择&#xff0c;提供的命令比较丰富&#xff0c;场景比较全面yt-dlp 是一个用 Python 编写的命令行工具&#xff0c;主…

数据结构和算法|递归算法那些事(递归算法的时间复杂度和尾递归优化)

对于文章的第一部分&#xff0c;递归算法的时间复杂度&#xff0c;来自于代码随想录文章:通过一道面试题目&#xff0c;讲一讲递归算法的时间复杂度&#xff01; 对于第二节尾递归优化来自于B站&#xff1a;尾递归优化&#xff1a;你的递归调用是如何被优化的&#xff1f; 关于…