操作系统真象还原:创建文件系统

14.2 创建文件系统

14.2.1 创建超级块、i结点、目录项

超级块

/** @Author: Adward-DYX 1654783946@qq.com* @Date: 2024-05-07 10:18:02* @LastEditors: Adward-DYX 1654783946@qq.com* @LastEditTime: 2024-05-07 11:24:50* @FilePath: /OS/chapter14/14.2/fs/super_block.h* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE*/
#ifndef __FS_SUPER_BLOCK_H
#define __FS_SUPER_BLOCK_H
#include "global.h"
#include "stdint.h"/*超级块*/
struct super_block{uint32_t magic; //用来标识文件系统类型,支持多文件系统的操作系统通过此标志来识别文件系统的类型uint32_t sec_cnt;   //本分区总共的扇区数uint32_t inode_cnt; //本分区中ino数量uint32_t part_lba_base; //本分区的起始lba地址uint32_t block_bitmap_lba;  //块位图本身起始扇区地址uint32_t block_bitmap_sects; //扇区位图本身所占用的扇区数量uint32_t inode_bitmap_lba;  //i结点位图起始扇区lba地址uint32_t inode_bitmap_sects;    //i结点位图占用的扇区数量uint32_t inode_table_lba;   //i结点表起始扇区lba地址uint32_t inode_table_sects; //i结点表占用的扇区数量uint32_t data_start_lba;    //数据区开始的第一个扇区号uint32_t root_inode_no;    //根目录所在的i节点号uint32_t dir_entry_size;    //目录项大小uint8_t pad[460];   //加上460字节,凑足512字节1扇区的大小
}__attribute__ ((packed));#endif // !__FS_SUPER_BLOCK_H

inode节点

/** @Author: Adward-DYX 1654783946@qq.com* @Date: 2024-05-07 10:26:42* @LastEditors: Adward-DYX 1654783946@qq.com* @LastEditTime: 2024-05-07 10:35:50* @FilePath: /OS/chapter14/14.2/fs/inode.h* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE*/
#ifndef __FS_INODE_H
#define __FS_INODE_H#include "stdint.h"
#include "list.h"/*inode结构*/
struct inode{uint32_t i_no;  //inode编号/*当次inode是文件时,i_size是指文件大小,若是目录,则是指该目录下所有目录项大小之和*/uint32_t i_size;uint32_t i_open_cnts;   //记录此文件被打开的次数bool write_deny;    //写文件不能并行,进程写文件前检查此标识/* 咱们的块大小就是 1 扇区 ,i_sectors[0-11]是直接块,i_sectors[12]用来存储一级间接块指针*//*不过稍微不同的是扇区大小是 512 字节,并且块地址用 4 字节来表示,因此咱们支持的一级间接块数量是 128 个,即咱们总共支持 128+12= 140 个块(扇区)*/uint32_t i_sectors[13];/*由于 inode 是从硬盘上保存的 , 文件被打开时, 肯定是先要从硬盘上载入其 inode,但硬盘比较慢, 为了避免下次再打开该文件时还要从硬盘上重复载入 inode,应该在该文件第一次被打开时就将其 inode加入到 内存缓存中,每次打开一个文件时,先在此缓冲中查找相关的 inode , 如果有就直接使用, 否则再从硬盘上读取 inode,然后再加入此缓存。 这个内存缓存就是“己打开的 inode 队列”*/struct list_elem inode_tag;
};#endif // !__FS_INODE_H

目录

/** @Author: Adward-DYX 1654783946@qq.com* @Date: 2024-05-07 10:35:58* @LastEditors: Adward-DYX 1654783946@qq.com* @LastEditTime: 2024-05-07 15:41:12* @FilePath: /OS/chapter14/14.2/fs/dir.h* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE*/
#ifndef _FS_DIR_H
#define _FS_DIR_H
#include "stdint.h"
#include "inode.h"
#include "fs.h"
#include "global.h"#define MAX_FILE_NAME_LEN 16    //最大文件名长度/*目录结构*/
struct dir{struct inode* inode;uint32_t dir_pos;   //记录在目录内的偏移uint8_t dirr_buf[512];  //目录的数据缓存
};/*目录项结构*/
struct dir_entry{char filename[MAX_FILE_NAME_LEN];   //普通文件或目录名称uint32_t i_no;  //普通文件或目录对应的inode编号enum file_types f_type; //文件类型
};#endif // !_FS_DIR_H

注:目录的数据结构struct dir只会存在于内存之中,因为它管理的是对一个目录文件的操作(比如打开一个目录文件,就会在内存中创建这样一个结构体)。

文件类型的定义

/** @Author: Adward-DYX 1654783946@qq.com* @Date: 2024-05-07 11:06:32* @LastEditors: Adward-DYX 1654783946@qq.com* @LastEditTime: 2024-05-07 15:29:03* @FilePath: /OS/chapter14/14.2/fs/fs.h* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE*/
#ifndef _FS_FS_H
#define _FS_FS_H
#include "stdint.h"#define MAX_FILES_PER_PART 4096 //每个分区所支持最大创建的文件数
#define BITS_PER_SECTOR 4096    //每扇区的位数
#define SECTOR_SIZE 512 //扇区字节大小
#define BLOCK_SIZE SECTOR_SIZE  //块字节大小/*文件类型*/
enum file_types{FT_UNKNOWN, //不支持的文件类型FT_REGULAR, //普通文件FT_DIRECTORY    //目录
};void filesys_init(void);
#endif // !_FS_FS_H
14.2.2 创建文件系统

这里书上的创建的文件系统有一定的问题,我这里直接是学习的另外一个博主的地址如下:《操作系统真象还原》 第十四章 文件系统-CSDN博客

其代码如下:

#include "stdint.h"
#include "fs.h"
#include "inode.h"
#include "ide.h"
#include "memory.h"
#include "super_block.h"
#include "dir.h"
#include "stdio-kernel.h"
#include "string.h"/* 格式化分区,也就是初始化分区的元信息,创建文件系统 */
static void partition_format(struct partition* part) {/* 为方便实现,一个块大小是一扇区 */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);	   // I结点位图占用的扇区数.最多支持4096个文件uint32_t inode_table_sects = DIV_ROUND_UP(((sizeof(struct inode) * MAX_FILES_PER_PART)), SECTOR_SIZE);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;  /************** 简单处理块位图占据的扇区数 ***************/uint32_t now_total_free_sects = free_sects; // 定义一个现在总的可用扇区数uint32_t prev_block_bitmap_sects = 0; // 之前的块位图扇区数uint32_t block_bitmap_sects = DIV_ROUND_UP(now_total_free_sects, BITS_PER_SECTOR); // 初始估算uint32_t block_bitmap_bit_len;while (block_bitmap_sects != prev_block_bitmap_sects) {prev_block_bitmap_sects = block_bitmap_sects;/* block_bitmap_bit_len是位图中位的长度,也是可用块的数量 */block_bitmap_bit_len = now_total_free_sects - block_bitmap_sects;block_bitmap_sects = DIV_ROUND_UP(block_bitmap_bit_len, BITS_PER_SECTOR);}/*********************************************************//* 超级块初始化 */struct super_block sb;sb.magic = 0x19590318;sb.sec_cnt = part->sec_cnt;sb.inode_cnt = MAX_FILES_PER_PART;sb.part_lba_base = part->start_lba;sb.block_bitmap_lba = sb.part_lba_base + 2;	 // 第0块是引导块,第1块是超级块sb.block_bitmap_sects = block_bitmap_sects;sb.inode_bitmap_lba = sb.block_bitmap_lba + sb.block_bitmap_sects;sb.inode_bitmap_sects = inode_bitmap_sects;sb.inode_table_lba = sb.inode_bitmap_lba + sb.inode_bitmap_sects;sb.inode_table_sects = inode_table_sects; sb.data_start_lba = sb.inode_table_lba + sb.inode_table_sects;  //数据区的起始就是inode数组的结束sb.root_inode_no = 0;sb.dir_entry_size = sizeof(struct dir_entry);printk("%s info:\n", part->name);printk("   magic:0x%x\n   part_lba_base:0x%x\n   all_sectors:0x%x\n   inode_cnt:0x%x\n   block_bitmap_lba:0x%x\n   block_bitmap_sectors:0x%x\n   inode_bitmap_lba:0x%x\n   inode_bitmap_sectors:0x%x\n   inode_table_lba:0x%x\n   inode_table_sectors:0x%x\n   data_start_lba:0x%x\n", sb.magic, sb.part_lba_base, sb.sec_cnt, sb.inode_cnt, sb.block_bitmap_lba, sb.block_bitmap_sects, sb.inode_bitmap_lba, sb.inode_bitmap_sects, sb.inode_table_lba, sb.inode_table_sects, sb.data_start_lba);struct disk* hd = part->my_disk;/******************************** 1 将超级块写入本分区的1扇区 *******************************/ide_write(hd, part->start_lba + 1, &sb, 1);printk("   super_block_lba:0x%x\n", part->start_lba + 1);/* 找出数据量最大的元信息,用其尺寸做存储缓冲区*/uint32_t buf_size = (sb.block_bitmap_sects >= sb.inode_bitmap_sects ? sb.block_bitmap_sects : sb.inode_bitmap_sects);buf_size = (buf_size >= sb.inode_table_sects ? buf_size : sb.inode_table_sects) * SECTOR_SIZE;uint8_t* buf = (uint8_t*)sys_malloc(buf_size);	// 申请的内存由内存管理系统清0后返回/*************************************** 2 将块位图初始化并写入sb.block_bitmap_lba **************************************//* 初始化块位图block_bitmap */buf[0] |= 0x01;       // 第0个块预留给根目录,位图中先占位uint32_t block_bitmap_last_byte = block_bitmap_bit_len / 8; //计算出块位图最后一字节的偏移uint8_t  block_bitmap_last_bit  = block_bitmap_bit_len % 8; //计算出块位图最后一位的偏移uint32_t last_size = SECTOR_SIZE - (block_bitmap_last_byte % SECTOR_SIZE);	     // last_size是位图所在最后一个扇区中,不足一扇区的其余部分/* 1 先将位图最后一字节到其所在的扇区的结束全置为1,即超出实际块数的部分直接置为已占用*/memset(&buf[block_bitmap_last_byte], 0xff, last_size);/* 2 再将上一步中覆盖的最后一字节内的有效位重新置0 */uint8_t bit_idx = 0;while (bit_idx < block_bitmap_last_bit) {buf[block_bitmap_last_byte] &= ~(1 << bit_idx++);}ide_write(hd, sb.block_bitmap_lba, buf, sb.block_bitmap_sects);/**************************************** 3 将inode位图初始化并写入sb.inode_bitmap_lba ****************************************//* 先清空缓冲区*/memset(buf, 0, buf_size);buf[0] |= 0x1;      // 第0个inode分给了根目录/* 由于inode_table中共4096个inode,位图inode_bitmap正好占用1扇区,* 即inode_bitmap_sects等于1, 所以位图中的位全都代表inode_table中的inode,* 无须再像block_bitmap那样单独处理最后一扇区的剩余部分,* inode_bitmap所在的扇区中没有多余的无效位 */ide_write(hd, sb.inode_bitmap_lba, buf, sb.inode_bitmap_sects);/**************************************** 4 将inode数组初始化并写入sb.inode_table_lba ****************************************//* 准备写inode_table中的第0项,即根目录所在的inode */memset(buf, 0, buf_size);  // 先清空缓冲区bufstruct inode* i = (struct inode*)buf; i->i_size = sb.dir_entry_size * 2;	 // .和..i->i_no = 0;   // 根目录占inode数组中第0个inodei->i_sectors[0] = sb.data_start_lba;	     // 由于上面的memset,i_sectors数组的其它元素都初始化为0 ide_write(hd, sb.inode_table_lba, buf, sb.inode_table_sects);/**************************************** 5 将根目录初始化并写入sb.data_start_lba***************************************//* 写入根目录的两个目录项.和.. */memset(buf, 0, buf_size);struct dir_entry* p_de = (struct dir_entry*)buf;/* 初始化当前目录"." */memcpy(p_de->filename, ".", 1);p_de->i_no = 0;p_de->f_type = FT_DIRECTORY;p_de++;/* 初始化当前目录父目录".." */memcpy(p_de->filename, "..", 2);p_de->i_no = 0;   // 根目录的父目录依然是根目录自己p_de->f_type = FT_DIRECTORY;/* sb.data_start_lba已经分配给了根目录,里面是根目录的目录项 */ide_write(hd, sb.data_start_lba, buf, 1);printk("   root_dir_lba:0x%x\n", sb.data_start_lba);printk("%s format done\n", part->name);sys_free(buf);
}

下面再写一个函数filesys_init遍历所有分区,如果该分区没有文件系统就调用partition_format来创建文件系统

/*在磁盘上搜索文件系统,若没有则格式化分区创建文件系统*/
void filesys_init(void){uint8_t channel_no = 0, dev_no, part_idx = 0;/*sb_buf用来存储从硬盘上读入的超级块*/struct super_block* sb_buf = (struct super_block*)sys_malloc(SECTOR_SIZE);if(sb_buf == NULL){PANIC("alloc memory failed!");}printk("searching  filesystem.......\n");while(channel_no < channel_cnt){dev_no = 0;while(dev_no<2){if(dev_no == 0){    //跨过裸盘hd60M.imgdev_no++;continue;}struct disk* hd = &channels[channel_no].devices[dev_no];struct partition* part = hd->prim_parts;while(part_idx < 12){   //4个主分区+8个逻辑分区if(part_idx == 4){  //开始处理逻辑分区part = hd->logic_parts;}/*** channels数组是全局变量,默认值为0,disk属于其嵌套结构,* partition又为disk的嵌套结构,因此partition中的成员默认也为0* 若partition未初始化,则partition中的成员扔为0* 下面处理存在的分区*/if(part->sec_cnt!=0){    //如果分区存在memset(sb_buf,0,SECTOR_SIZE);/*独处分区的超级块,更具魔数是否正确判断是否存在文件系统*/ide_read(hd,part->start_lba+1,sb_buf,1);/*只支持自己的文件系统,若磁盘上已经有文件系统就不在格式化了*/if(sb_buf->magic == 0x19590318){printk("%s has filesystem\n",part->name);}else{  //其他文件系统不支持,一律按无文件系统处理printk("formatting %s`s partition %s........\n",hd->name,part->name);partition_format(part);}}part_idx++;part++; //下一分区}dev_no++;//下一磁盘}channel_no++;//下一通道}sys_free(sb_buf);/*确定默认操作的分区*/char default_part[8] = "sdb1";/*挂载分区*/list_traversal(&partition_list,mount_partition,(int)default_part);
}
14.2.3 挂载分区

Linux 内核所在的分区是默认分区,自系统启动后就以该分区为默认分区,该分区的根目录是固定存在的,要想使用其他新分区的话,需要用 mount 命令手动把新的分区挂载到默认分区的某个目录下,这就是上面所说的“拿”出来。尽管其他分区都有自己的根目录,但是默认分区的根目录才是所有分区的父目录,因此挂载分区之后,整个路径树就像一串葡萄。分区不用的时候还可以通过 umount 命令卸载,这就是上面所说的“收”起来。

挂载分区的实质是把该分区文件系统的元信息从硬盘上读出来加载到内存中,这样硬盘资源的变化都用内存中元信息来跟踪,如果有写操作,及时将内存中的元信息同步写入到硬盘以持久化 。

struct partition* cur_part; //默认情况下操作的是那个分区/*在分区链表中找到名为part_name的分区,并将其赋值给cur_part*/
static bool mount_partition(struct list_elem* pelem, int arg){char* part_name = (char*)arg;struct partition* part = elem2entry(struct partition, part_tag, pelem);if(!strcmp(part->name,part_name)){cur_part = part;struct disk* hd = cur_part->my_disk;/*sb_buf用来存储从硬盘上读入的超级块*/struct super_block* sb_buf = (struct super_block*)sys_malloc(SECTOR_SIZE);/*在内存中创建分区cur_part的超级块*/cur_part->sb = (struct super_block*)sys_malloc(sizeof(struct super_block));if(cur_part->sb == NULL){PANIC("alloc memory failed!");}/*读入超级块*/memset(sb_buf, 0, SECTOR_SIZE);ide_read(hd, cur_part->start_lba+1,sb_buf,1);/*把sb_buf中的超级块的信息复制到分区的超级块sb中*/memcpy(cur_part->sb,sb_buf,sizeof(struct super_block));/********************* 将硬盘上的块位图读入到内存中 ***************************************/cur_part->block_bitmap.bits = (uint8_t*)sys_malloc(sb_buf->block_bitmap_sects * SECTOR_SIZE);if(cur_part->block_bitmap.bits == NULL)PANIC("alloc memory failed!");cur_part->block_bitmap.btmp_bytes_len = sb_buf->block_bitmap_sects * SECTOR_SIZE;/*从硬盘上读入块位图到分区的block_bitmao.bits*/ide_read(hd,sb_buf->block_bitmap_lba,cur_part->block_bitmap.bits,sb_buf->block_bitmap_sects);/********************************************************************************************//********************* 将硬盘上的inode位图读入到内存中 ***************************************/cur_part->inode_bitmap.bits = (uint8_t*)sys_malloc(sb_buf->inode_bitmap_sects * SECTOR_SIZE);if(cur_part->inode_bitmap.bits == NULL)PANIC("alloc memory failed!");cur_part->inode_bitmap.btmp_bytes_len = sb_buf->inode_bitmap_sects * SECTOR_SIZE;/*从硬盘上读入inode位图到分区的inode_bitmap.bits*/ide_read(hd,sb_buf->inode_bitmap_lba,cur_part->inode_bitmap.bits,sb_buf->inode_bitmap_sects);/********************************************************************************************/list_init(&cur_part->open_inodes);printk("mount %s done!\n", part->name);/*** 此处返回true是为了迎合主调函数list_traversal的实现* 与函数本身功能无关* 只有返回true时list_traversal才会停止遍历* 减少了后面元素无意义的遍历*/return true;}return false;   //使得list_traversal继续遍历
}

结果如下:
在这里插入图片描述

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

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

相关文章

WPF学习(6) -- WPF命令和通知

一 、WPF命令 1.ICommand代码 创建一个文件夹和文件 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input;namespace 学习.Command {public class MyCommand : ICommand{Acti…

CCSI: 数据无关类别增量学习的持续类特定印象| 文献速递-基于深度学习的多模态数据分析与生存分析

Title 题目 CCSI: Continual Class-Specific Impression for data-free class incremental learning CCSI: 数据无关类别增量学习的持续类特定印象 01 文献速递介绍 当前用于医学影像分类任务的深度学习模型表现出令人鼓舞的性能。这些模型大多数需要在训练之前收集所有的…

中间件——Kafka

两个系统各自都有各自要去做的事&#xff0c;所以只能将消息放到一个中间平台&#xff08;中间件&#xff09; Kafka 分布式流媒体平台 程序发消息&#xff0c;程序接收消息 Producer&#xff1a;Producer即生产者&#xff0c;消息的产生者&#xff0c;是消息的入口。 Brok…

[Vulnhub] Sedna BuilderEngine-CMS+Kernel权限提升

信息收集 IP AddressOpening Ports192.168.8.104TCP:22, 53, 80, 110, 111, 139, 143, 445, 993, 995, 8080, 55679 $ nmap -p- 192.168.8.104 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 6.6.1p1 Ubuntu 2ubuntu2 …

在RHEL9.4上启用SFTP服务

FTP存在的不足&#xff1a; 明文传输 FTP传输的数据&#xff08;包括用户名、密码和文件内容&#xff09;都是明文的&#xff0c;这意味着数据可以被网络上的任何人截获并读取。没有内置的加密机制&#xff0c;容易受到中间人攻击。 被动模式下的端口问题 FTP的被动模式需要…

读人工智能全传12人工智能导致的问题1

1. 人工智能会导致什么问题 1.1. 人工智能是一门通用技术&#xff1a;它的应用仅仅受限于我们的想象 1.1.1. 所有的技术都可能产生意想不到的效果&#xff0c;未来几十年甚至几百年内都存在可能性 1.2. 所有的技术都可能被滥用 1.2.1. 我们的无名氏祖先率先用上了火&#x…

编写商品列表和商品编辑和商品新增页面

addvue <template><!-- 传过来的id --> <!-- {{ $route.query.id }} --> <el-formref"FormRef"style"max-width: 600px":model"FormData":rule"rules"status-iconlabel-width"auto"class"demo-r…

Golang | Leetcode Golang题解之第232题用栈实现队列

题目&#xff1a; 题解&#xff1a; type MyQueue struct {inStack, outStack []int }func Constructor() MyQueue {return MyQueue{} }func (q *MyQueue) Push(x int) {q.inStack append(q.inStack, x) }func (q *MyQueue) in2out() {for len(q.inStack) > 0 {q.outStack…

【web】-sql注入-login

根据网址提示打开如图&#xff1a; 查看源代码前台并没有过滤限制、扫描后台也没有发现特殊文件。看到标题显示flag is in database&#xff0c;尝试sql注入。 由于post,bp抓包如下&#xff1a; 运行python sqlmap.py -r 1.txt --dump 获取flag 42f4ebc342b6ed4af4aadc1ea75f…

昇思25天学习打卡营第20天 | 基于MindNLP+MusicGen生成自己的个性化音乐

基于MindNLPMusicGen生成个性化音乐 实验简介 MusicGen是Meta AI提出的音乐生成模型&#xff0c;能够根据文本描述或音频提示生成高质量音乐。该模型基于Transformer结构&#xff0c;分为三个阶段&#xff1a;文本编码、音频token预测和音频解码。此实验将演示如何使用MindSpo…

搞定ES6同步与异步机制、async/await的使用以及Promise的使用!

文章目录 同步和异步async/awaitPromisePromise的概念 同步和异步 ​ 同步&#xff1a;代码按照编写顺序逐行执行&#xff0c;后续的代码必须等待当前正在执行的代码完成之后才能执行&#xff0c;当遇到耗时的操作&#xff08;如网络请求等&#xff09;时&#xff0c;主线程会…

数据结构(初阶2.顺序表)

文章目录 一、线性表 二、顺序表 2.1 概念和结构 2.2 分类 2.2.1 静态顺序表 2.2.2 动态顺序表 2.3动态顺序表的实现 1.SeqList.h 2.SeqList.c 打印顺序表 初始化 销毁 增容 尾插 头插 在指定位置之前插入数据 尾删 头删 在指定位置删除数据 3.test.c 一、线性表 线性表&#…

如何解决VMware 安装Windows10系统出现Time out EFI Network...

一、问题描述 使用VMware 17 安装windows10出现如下图所示Time out EFI Network… Windows10镜像为微软官方下载的ISO格式镜像&#xff1b; 二、问题分析 VMware 17 默认的固件类型是UEFI(E)&#xff0c;而微软官网下载的Windows10 ISO格式镜像不支持UEFI(E)&#xff0c;支…

【中项第三版】系统集成项目管理工程师 | 第 4 章 信息系统架构④ | 4.7

前言 第4章对应的内容选择题和案例分析都会进行考查&#xff0c;这一章节属于技术相关的内容&#xff0c;学习要以教材为准。本章分值预计在4-5分。 目录 4.7 安全架构 4.7.1 安全威胁 4.7.2 定义与范围 4.7.3 整体架构设计 4.7.4 网络安全架构设计 4.7.5 数据库系统安…

C++基础语法:STL之迭代器

前言 "打牢基础,万事不愁" .C的基础语法的学习 引入 C基础:STL概述-CSDN博客 上一篇梳理了一些同STL有关的概念.同时对理解迭代器需要的类包含,内部类,链表等内容做了分析,这篇从<C Prime Plus> 6th Edition(以下称"本书")的P684,大标题16.4泛型编…

C++继承和多态

目录 继承 继承的意义 访问限定符、继承方式 赋值兼容规则&#xff08;切片&#xff09; 子类的默认成员函数 多继承 继承is a和组合has a 多态 什么是多态 形成多态的条件 函数重载&#xff0c;隐藏&#xff0c;重写的区别 override和final 多态原理 继承 继承的…

Algorithms,最全的Python算法仓库!

学习编程、学习Python最好的方式就是练习&#xff0c;哪怕是新手&#xff0c;只要不断地敲代码输出&#xff0c;肯定会有神效。 Python的练手项目很多&#xff0c;特别是Github上&#xff0c;建议不管新手、老司机都去看看。 这里推荐给大家一个Gitthub上练习的项目&#xff…

[C++]——同步异步日志系统(5)

同步异步日志系统 一、日志消息格式化设计1.1 格式化子项类的定义和实现1.2 格式化类的定义和实现 二、日志落地类设计2.1 日志落地模块功能实现与测试2.2 日志落地模块功能功能扩展 一、日志消息格式化设计 日志格式化模块的作用&#xff1a;对日志消息进行格式化&#xff0c…

深度学习工具和资源推荐:全面指南

今天我们来聊聊深度学习的工具和资源。要学好深度学习&#xff0c;除了理论知识&#xff0c;还需要掌握一些强大的工具和找到好的资源。以下是我在学习过程中发现的一些非常有用的工具和资源&#xff0c;希望对你们有帮助。 目录 工具推荐 1. Python编程语言 2. TensorFlow…

接口测试返回参数的自动化对比!

引言 在现代软件开发过程中&#xff0c;接口测试是验证系统功能正确性和稳定性的核心环节。接口返回参数的对比不仅是确保接口功能实现的手段&#xff0c;也是测试过程中常见且重要的任务。为了提高对比的效率和准确性&#xff0c;我们可以通过自动化手段实现这一过程。本文将…