DP读书:《openEuler操作系统》(七)FSCK与VFS虚拟文件系统

10min速通FSCK、原子操作与VFS

  • 文件系统检查器
    • 1.检查inode表
      • 1) 遍历所有inode
      • 2) 修复多次引用数据块
    • 2.检查目录结构
    • 3.检查目录的连接
      • 1) 检查根目录确保存在
      • 2) 遍历所有目录的inode,有问题的连接到`/lost+found`
    • 4.检查引用次数
    • 5.检查位图一致性
  • 日志
    • 1.主要的数据结构
      • 1) 原子操作描述符
      • 2) 事务结构
      • 3) 日志结构
    • 2.原子操作的生成
      • 1) 获取原子操作描述符
      • 2) 将元数据缓冲区纳入管理
      • 3) 获取原数据缓冲区的更新
      • 4) 将更新操作加入当前原子操作描述符中
    • 3.事务提交
    • 4.崩溃的恢复
  • 虚拟文件系统
    • 简介

  • 写了三天,理解了FSCK的底层原理,原子操作和VFS,所以就把笔记发了出来。

在不少的情况下,由于计算机软硬件或人为原因断电、崩溃(crash),如果此时系统正在更新硬盘数据,则可能出现数据一致性问题——崩溃一致性问题(crash-consistency problem)

图1 数据块 索引点 位图关系

节点更新
元数据状态不一致
位图尚未更新

数据块索引点位图关系

然而,磁盘在同一时间只能进行一次I/O操作,硬件层面无法实现,所以文件系统需要在软件层面原子性的从一个状态更新到一个新的状态。

本节将介绍两种解决方案:文件系统检查器日志

文件系统检查器

F i l e S y s t e m C h e c k e r = F S C K File System Checker=FSCK FileSystemChecker=FSCK
文件系统检查器(File System Checker,FSCK)是解决崩溃一致性问题的一种简单方案。

主要用于解决由于各种原因导致的文件系统不一致性问题,例如系统崩溃、断电、文件系统损坏等。

在处理崩溃一致性问题时,FSCK提供了一种简单而有效的方法来检查和修复文件系统。当文件系统遭受损坏或出现不一致性时,FSCK可以扫描整个文件系统并检测到潜在的错误。一旦检测到错误,FSCK会尝试自动修复它们,以便文件系统恢复到一致状态。

使用FSCK可以确保文件系统的完整性,从而避免数据丢失或文件损坏。它通常在操作系统启动时自动运行,或者可以手动启动以检查特定的文件系统。

在openEuler中,这个工具被称为 e2fsck(以下简称FSCK) ,可以用来检测Ext2/3/4文件系统。

e 2 f s c k e2fsck e2fsck

1.检查inode表

1) 遍历所有inode

这是检查inode表的第一步。inode是文件系统中的一个重要数据结构,用于存储文件或目录的元数据。遍历所有inode是为了检查它们的状态和完整性。

  • 检查inode类型字段i_mode是否合法
  • 检查inode记录的文件大小(i_size_lo i_size_high
  • 检查数据块是否被另一个inode重复引用

2) 修复多次引用数据块

如果一个数据块被多个inode引用,这可能会导致数据不一致或损坏。修复这种情况可以确保数据的一致性和完整性。

  • 再次扫描所有inode指向的数据块
  • 遍历文件系统树结构
  • 对文件系统进行修复

2.检查目录结构

目录是文件系统中组织文件和子目录的方式。检查目录结构可以发现并修复任何目录相关的问题,例如丢失或重复的目录项。

  • rec_len 的值至少为8B,并且不能大于该目录所在数据块中剩余空间大小
  • name_len 值小于rec_len-8
  • inode编号指定正在使用
  • 目录第一项为.(当前目录)
  • 目录第二项为..(父目录)
  • 收集每个目录的inode编号

3.检查目录的连接

这涉及到检查目录项之间的连接关系,确保它们正确地链接在一起,没有断裂或循环引用。

1) 检查根目录确保存在

2) 遍历所有目录的inode,有问题的连接到/lost+found

4.检查引用次数

在文件系统中,某些数据结构或数据块可能被多个地方引用。检查引用次数可以确保这些数据结构或数据块被正确地使用和释放,避免内存泄漏或其他问题。

  • 多数inode为未分配,引用次数为0
  • 已分配引用次数为1
  • 文件目录多链接引用次数大于1

5.检查位图一致性

位图是文件系统中用于跟踪空闲空间的数据结构。检查位图一致性可以确保文件系统能够正确地分配和回收空间。

  • 非常耗时,特别是在检索文件系统中的所有索引节点
    E x t 4 Ext4 Ext4
  • Ext4 对未分配的 inode 做了标记,使 FSCK在检查磁盘时将其整块的忽略掉,大大缩短磁盘检测时间

日志

j o u r n a l i n g \textcolor{#4183c4}{journaling} journaling
日志(journaling)机制是现代文件系统(如Linux Ext3/Ext4、Windows NTFS)解决崩溃一致性问题的主流解决方案,借鉴于数据库系统的设计思想,日志系统将将文件系统的更新组织为事务(transaction),使得文件系统的状态为完成或未完成,日志机制将文件系统的状态划分为三种状态:

  • 1.日志的写入
  • 2.事务的提交
  • 3.检查点的添加
日志的写入
事务的提交
检查点的添加

日志机制

  1. 日志的写入

    • 在日志机制中,每次对文件系统的修改都会首先被写入到一个专门的日志文件中。这个日志文件记录了所有的更改操作,包括数据的读、写、删除等。即对元数据(索引节点、位图等)和数据块做待做更新以日志形式写入磁盘。 实质上,日志是一个无名字的文件,对应inode号为8
    • 这种写入操作确保了即使在系统突然崩溃的情况下,文件系统也可以通过这个日志来恢复到一致的状态。因为即使某些修改尚未完全应用到实际的文件系统中,它们在日志中仍然被记录下来,从而可以在恢复过程中重新应用。
  2. 事务的提交
    t r a n s a c t i o n \textcolor{#4183c4}{transaction} transaction

    • 在数据库管理中,一个事务是一系列的操作,这些操作要么全部成功,要么全部失败,以确保数据的一致性。在文件系统中,日志机制同样支持 事务(transaction)的概念。
    • 当一系列的修改操作被写入日志后,它们被组织成一个事务。只有当这个事务的所有修改都被成功记录到日志中时,才会被称为事务已提交(committed),意味着这些修改现在可以被应用到实际的文件系统中。
    • 如果在事务提交之前系统崩溃,由于这些更改已经被记录在日志中,文件系统可以在重启后重新应用这些更改,确保数据的一致性。硬件扇区只保证单位(512B)写入为原子的,为了保证原子性,所以每一个事务结束标志的大小应小于512B。
  3. 检查点的添加
    c h e c k p o i n t \textcolor{#4183c4}{check point} checkpoint

    • 检查点(check point)是在文件系统中的某一特定时间点,所有的事务都已经被提交的状态。在日志机制中,检查点起到了一个重要的作用,因为它标记了一个时间点,从那时起,即使发生崩溃,文件系统也可以从这个检查点开始恢复,而不是从头开始。
    • 当系统运行时,会定期添加检查点。这意味着如果系统在检查点之后崩溃,重启后恢复过程可以从这个检查点开始,而不是从头开始。这大大减少了恢复时间。

通过这三种状态的管理和操作,日志机制为现代文件系统提供了强大的崩溃一致性保护,确保了数据的完整性和可靠性。
j b d 2 = J o u r n a l i n g B l o c k D r i v e r \textcolor{#4183c4}{jbd2=Journaling Block Driver} jbd2=JournalingBlockDriver
在Ext4文件系统中,日志机制在 jbd2(Journaling Block Driver) 模块中实现,该模块用于管理缓冲区。


Ext4 文件数据系统

块缓冲区
数据或恢复时写入
缓冲被管理
数据记录
正常读写
正常读
内核
元数据缓冲
jbd2
数据缓冲
日志区
磁盘

ext4文件数据系统

引入jbd2之后,文件系统中的数据在内核、块缓冲区(buffer cache)、磁盘之间的流动如上Mermaid状态图所示。将内核对 缓冲(buffer) 的更新记录到磁盘中的日志空间中

数据记录
jbd2
管理缓冲区
块缓冲区

jbd2 数据传递过程

jbd2在管理过程中,Ext4系统将日志系统分为三类:Journal、Ordered、Writeback三种模式。

  • journal模式:用户数据、元数据(可靠、两次写入、较慢)
  • Ordered模式:元数据更新,写入顺序(先写磁盘再写日志、高性能)
  • Writeback模式:记录元数据更新,不保证顺序(可靠性最低、性能最高)

模式选择上,Ordered模式在性能和可靠性上取得了较好的平衡,所以Ordered模式是Ext4文件系统中的缺省模式。

1.主要的数据结构

1) 原子操作描述符

h a n d l e − t = 原子操作描述符 = 一个原子操作 \textcolor{#4183c4}{handle _ - t=原子操作描述符=一个原子操作} handlet=原子操作描述符=一个原子操作
文件系统需要保证其与更新相关的系统调用(如write)以原子的方式进行处理,而这些系统调用通常由多个I/O此操作组成——原子操作。

每个原子操作的大小到达(阈值)内核操作里的nblocks值时,后续I/O操作将添加到后i一个原子操作。

jbd2中,原子操作的数据结构为struct jbd2_journal_handle,主要成员如下图所示

    //源文件:include/linux/jbd2.hstruct jbd2_journal_handle{union{transaction_t    * h_transaction;    /*属于哪个事务*/journal_t    *h_journal;             /*属于哪个日志*/};int  h_total_credits;                    /*允许添加到日志的剩余缓冲区数目*/...};typedef struct jbd2_journal_handle handle_t;  /*原子操作描述符*/
  • struct jbd2_journal_handle的部分成员

jbd2_journal_handle_t 是 Linux内核中用于处理日志块设备的结构。它是一个原子句柄,用于收集单个高级原子更新期间发生的所有低级更改。原子句柄确保高级更新要么发生,要么不发生,因为实际对文件系统的更改只有在将原子句柄记录在日志中后才会被刷新。此外jbd2_journal_handle_t
还可以将多个原子句柄分组成单个事务,然后在固定时间段后或者日志中没有足够的空间来容纳它时将事务写入日志。事务具有多种状态,包括运行中、锁定、刷新、提交和完成。基于事务状态,JBD能够确定需要重新应用到文件系统的事务。

2) 事务结构

s t r u c t t r a n s a t i o n − s \textcolor{#4183c4}{struct ~transation_-s} struct transations
为了提高读写效率,确保数据的完整性和一致性,jbd2 将若干原子操作组成一个事务,以事务作为单位进行日志读写,具有生命周期性。这些操作要么全部完成,要么全部不完成,以确保数据的一致性。

在jbd2中,事务具有五个状态,分别是:

  1. 运行状态(Running):事务开始执行,尚未提交或回滚。
  2. 准备提交状态(Ready to commit):事务的所有操作都已完成,并准备好提交。
  3. 提交状态(Committing):事务正在被提交。一旦提交成功,事务就会从数据库中删除。
  4. 回滚状态(Rolling back):事务因为某种原因需要回滚,即撤销已经执行的操作。
  5. 完成状态(Done):事务已经成功提交或回滚,不再对数据库进行任何操作。

这种事务结构允许jbd2在处理大量数据时保持高效,同时确保数据的完整性和一致性。通过将多个原子操作组合成一个事务,jbd2可以减少日志的I/O操作次数,提高系统的性能。同时,事务的生命周期性使得jbd2可以有效地管理事务的状态和转换,以适应不同的情况和需求。

//源文件 : include/linux/jbd2.h
struct transaction_s {int state; // 事务状态// 其他成员变量
};

只有处于运行状态的事务可以进行原子操作

这些成员变量定义了结构体的属性和行为

这些成员变量定义了struct transaction_s结构体的属性和行为。

3) 日志结构

s t r u c t j o u r n a l − s \textcolor{#4183c4}{struct ~journal_-s} struct journals
在Linux内核中,日志结构的主要数据结构是struct journal_s,定义在include/linux/jbd2.h文件中。在这个数据结构中,你可以找到关于日志的详细信息,包括日志的初始化、加载、清除、事务处理等操作。这个数据结构在Linux内核中扮演着非常重要的角色,用于管理文件系统的日志功能。

//源文件 : include/linux/jbd2.h
struct journal_s {int timestamp;char message[100];int logLevel;
};
/*在这个示例中,struct journal_s 包含了一个整数类型的时间戳 timestamp,
一个长度为100的字符数组 message 用于存储消息内容,
以及一个整数类型的 logLevel 用于表示日志级别。这个结构体可以根据实际需求进行扩展和修改。*/

2.原子操作的生成

O r d e r e d \textcolor{#4183c4}{Ordered} Ordered
在 Ordered 模式下,一个原子操作涉及若干个缓冲区的更新,为了将元数据缓冲区中的数据及时更新生成一个原子操作,jbd2将对这些元数据缓冲区进行管理。内核在jbd2 中获取对将要更新的元数据缓冲区的写权限,以防止覆盖仍处于提交状态的事务中同一缓冲区中的内容。

由此,当内核更新元数据缓冲区时,jbd2 能及时地获取这些更新操作,并将这些更新操作加入原子操作描述符中,以生成新的原子操作。原子操作的生成过程如下图所示,具体分四个步骤。
原子操作的生成过程

1) 获取原子操作描述符

调用 ext4_journal_start()
参数nblocks指定原子操作包含更新缓冲区域数阈值

2) 将元数据缓冲区纳入管理

e x t 4 − j o u r n a l − g e t − w r i t e − a c c e s s ( ) \textcolor{#4183c4}{ext4_-journal_-get_-write_-access()} ext4journalgetwriteaccess()
内核通过调用函数ext4_journal_get_write_access ,获取磁盘元数据块(除数据位图外)所映射缓冲区的写权限。由于数据位图的更新涉及数据块的分配和释放,因此其所映射的缓冲区具有更严苛的写权限。内核通过调用函数ext4_journal_get_undo_access(),获取该类缓冲区的写权限。此外,内核通过调用函数ext4_journal_get_create_access(),获取新建元数据缓冲区的写权限。这三个函数在执行时,都先通过调用函数jbd2_journal_add_journal_head(),将当前元数据缓冲区纳人jbd2 的管理。

3) 获取原数据缓冲区的更新

在元数据缓冲区完成更新操作后,内核通过调用函数jbd2_journal_dirty_metadata(),通知jbd2 发生了元数据缓冲区更新操作。

4) 将更新操作加入当前原子操作描述符中

达到阈值,jbd2生成一个原子操作。

3.事务提交

当生成的原子操作数量达到阙值,jbd2将事务推人锁定状态,并将其加入等待提交的队列中。openEuler 专门维护了一个内核线程提交事务(每个Ext4 文件系统都对应一个kjournald 内核线程)。该线程每隔一定的时间在等待提交队列中选择一个事务,并通过调用函数 kjournald2(),把该事务提交到磁盘的日志区。事务提交主要由函数 kjournald2()调用的函数 jbd2_journal_commit_transaction()完成。
j b d 2 − j o u r n a l − c o m m i t − t r a n s a c t i o n ( ) \textcolor{#4183c4}{ jbd2_-journal_-commit_-transaction()} jbd2journalcommittransaction()
(1)调用函数 jbd2_journal_commit_transaction(),将当前事务中元数据所关联的数据缓冲区中的内容写人磁盘(第5行)。
(2)调用函数 jbd2_journal_write_metadata_buffer(),将元数据缓冲区中的数据写人磁盘的日志区中。具体地,jbd2先创建一个新的元数据缓冲区,然后将当前元数据缓冲区的内容复制至这个新的元数据缓冲区( 第28行),再将新的元数据缓冲区映射到日志区的某个数据块,最后完成写人。

源文件:fs/jbd2/commit.c
void jbd2_journal_commit_transaction(journal_t * journal) {
//将数据缓冲区的内容写人磁盘
err = journal_ submit_data_buffers(journal, commit_transaction);
while (commit_ transaction - › t_buffers) /
jh = commit_ transaction - > t_ buffers;
//将元数据缓冲区写人日志区
Flags = jbd2_ journal
_write_metadata_buffer(
commit_ transaction, jh, &wbuf [bufs], blocknr) ;
//源文件:Es/jbd2/journal.c
int jba2_ journal
_write_metadata_buffer(transaction_t * transaction,
struct journal
head
*jh_
in,struct buffer_head
** bh out,
sector_t blocknr) {
//创建新元数据缓冲区
new_bh = alloc_buffer_head (GEP_NOFS__GEP NOFAIL) ;
init_buffer(new_bh, NULL, NULL);
1/初始化新元数据缓冲区
new_jh = journal_add_journal_head(new_bh); //纳人 jbd2 的管理
new _page = jh2bh(jh_ in) - > b_ page;
//当前元数据缓冲区对应的物理页
mapped_data = kmap_atomic(new_page) ;
//获得该页起始地址
//页内容复制到 tmp
memcpy tmp, mapped_data + new_offset, bh_ in - > b_size) ;
new_page = virt_to_page (tmp) ;
//将 tmp 转换成新页 new_page
/将新页设为新元数据缓冲区对应页
set_bh_page( new_bh,new_page, new_offset);
//新元数据缓冲区映射到日志区中块号blocknr 的数据块new_bh->b_blocknr = blocknr;
set_ buffer_dirty (new_bh) ;
//将 new_bh 标记为脏

4.崩溃的恢复

d o − o n e − p a s s ( ) \textcolor{#4183c4}{ do_-one_-pass()} doonepass()

崩溃的恢复流程如下:
崩溃恢复流程

/源文件:fs/jbd2/recovery. c
1/当传人参数 PASS.
SCAN 时
unsigned int
first_commit_ ID, next_commit_ ID;
1/临时记录起点与终点
sb = journal ->j_superblock;
next_commit_ ID = be32_to_ cpu(sb - > s_ sequence) ;
first_commit_ ID = next_ commit_ID;
/起点为目志区超级块记录的起始数据失易
if(pass == PASS_SCAN)info->start_transaction = first_commit_ID;
//遍历日志区,查看哪些日志区数据块需要恢复到磁盘,以此找到日志区数据块的终点
done:
if (pass == PASS_SCAN)
info - > end
_transaction = next_commit_ ID;
//当传入参数 PASS_REPLAY 时
while (1) {
1/遍历日志区的数据块
err = jread(&bh, journal, next_log_ block) ;
//读下一个数据块到bh中
tagp = &bh - > b_ data[ sizeof (journal_header_t)];//找到描述符块
//根据描述符块记录的映射关系逐一处理数据块
while((tagp - bh->b_data + tag_bytes)
<= journal - > j_blocksize - descr_csum_size) /
/读其中一个日志区数据块到 obh 中
err = jread(&obh,journal,io_block);
1/从描述符块获得该日志区数据应被写入的磁盘数据块号
blocknr = read _tag_block(journal, tag)

函数 do_one_pass()将日志数据写回磁盘原始位置的关键代码

  1. jbd2 在首次调用函数 do_one_pass() 时,以PASS_SCAN 作为输入参数,用于方便日志区的回收和遍历。函数会读取超级块获取起点,并遍历日志区获取终点。
  2. 当 jbd2 再次调用函数 do_one_pass() 时,以PASS_REPLAY 作为输入参数。函数会遍历数据块并获取与磁盘中数据块的映射关系,然后将日志中的数据块内容读入缓冲区,并标记为 obh 。接着找到磁盘中该数据块在内存中对应的缓冲区,标记为 nbh,并将 obh 中的内容复制到 nbh 中。最后将 nbh 标记为脏,脏的缓冲区将被自动写回磁盘中。
obh
nbh
  1. 日志机制相对于 FSCK 而言,可以大大缩短磁盘崩溃恢复时间,从而在崩溃并重新启动后加快恢复速度。
  2. 日志机制有效地保证了文件系统元数据的一致性。在更新元数据时,首先将元数据写入日志区域,然后再更改目标位置的元数据。这样,无论是在写入日志还是写入元数据时系统发生崩溃,都不会对文件系统造成严重影响。
6. 当传入参数为 `PASS_SCAN` 时:* 获取日志区的起点和终点。* 记录起点为日志区超级块记录的起始数据块。* 如果传入的参数是 `PASS_SCAN`,则将起点设置为第一个提交的ID。* 遍历日志区,查看哪些日志区数据块需要恢复到磁盘,以此找到日志区数据块的终点。如果传入的参数是 `PASS_SCAN`,则将终点设置为下一个提交的ID。
7. 当传入参数为 `PASS_REPLAY` 时:* 遍历日志区的数据块。* 读取下一个数据块到 `bh` 中。* 找到描述符块,并根据描述符块记录的映射关系逐一处理数据块。* 读取其中一个日志区数据块到 `obh` 中。* 从描述符块获得该日志区数据应被写入的磁盘数据块号。根据上述梳理,这段代码似乎是在处理文件系统日志的恢复过程,特别是当系统崩溃后,需要从磁盘中恢复数据时。它首先通过 `PASS_SCAN` 参数确定日志区的起点和终点,然后根据这些信息在 `PASS_REPLAY` 参数下恢复数据。
传人参数
参数类型
PASS_SCAN
PASS_REPLAY
获取起点和终点
遍历日志区的数据块
读取数据块到缓冲区
根据描述符块处理数据块
从描述符块获取磁盘数据块号

虚拟文件系统

简介

虚拟文件系统( V i r t u a l F i l e S y s t e m , V F S ) 虚拟文件系统(Virtual File System,VFS) 虚拟文件系统(VirtualFileSystemVFS
现代操作系统支持同时使用多种文件系统。

在计算机科学中,虚拟文件系统(Virtual File System,VFS)是一个抽象层,它允许应用程序和操作系统与各种不同类型的物理文件系统进行交互。VFS 隐藏了物理文件系统的底层细节,使应用程序和操作系统能够以统一、标准化的方式来访问和管理文件和目录。

通过使用 VFS,操作系统可以同时支持多种不同的文件系统,例如EXT4、NTFS、FAT32等。这样做的目的是为了提供更大的灵活性和可扩展性,因为新的文件系统或对现有文件系统的改进可以很容易地添加到系统中,而无需对应用程序或操作系统进行任何修改。

此外,VFS 还提供了一些额外的功能和优点,包括:

  1. 可移植性:应用程序可以在不同的操作系统或平台上运行,只需进行少量修改或无需修改,因为 VFS 隐藏了底层文件系统的差异。
  2. 性能优化:VFS 可以根据需要缓存文件数据和元数据,从而提高访问速度。
  3. 安全性:VFS 可以提供额外的安全层,例如权限控制和加密,以确保文件系统的安全性和完整性。
  4. 集成和互操作性:VFS 可以与其他软件组件和服务集成,例如网络文件共享和分布式文件系统。
可移植性
性能优化
安全性
集成和互操作性

数据结构先欠着,等大家可以先投票~
open Euler

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

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

相关文章

一文了解GeoTrust SSL证书

在当今互联网的高度连接世界中&#xff0c;确保网站安全性至关重要。SSL证书是保护网站和用户数据的关键组成部分。GeoTrust证书在SSL证书市场上享有盛誉&#xff0c;被许多网站所有者和企业所信赖。JoySSL将深入探讨GeoTrust证书的特点&#xff0c;帮助大家了解该品牌并做出更…

lua使用resty.http做nginx反向代理(https请求,docker容器化部署集群),一个域名多项目转发

下载使用 链接&#xff1a;https://pan.baidu.com/s/1uQ7yCzQsPWsF6xavFTpbZg 提取码&#xff1a;htay –来自百度网盘超级会员V5的分享 在根目录下执行: # 从 github 上下载文件 git clone https://github.com/ledgetech/lua-resty-http.git # 将 lua-resty-http/lib/ 下的 r…

计算机网络——数据链路层-媒体接入控制-静态划分信道(频分复用FDM、时分复用TDM、波分复用WDM、码分复用CDM)

目录 频分复用FDM 时分复用TDM 波分复用WDM 码分复用CDM 练习1 码分多址的应用举例 练习2 本篇我们介绍媒体接入控制的其中一类方法——静态划分信道 首先介绍信道复用的基本概念&#xff0c; 复用&#xff08;Multiplexing&#xff09;是通信技术中的一个重要概念&a…

SpringBoot中整合MybatisPlus快速实现Mysql增删改查和条件构造器

场景 Mybatis-Plus(简称MP)是一个Mybatis的增强工具&#xff0c;只是在Mybatis的基础上做了增强却不做改变&#xff0c;MyBatis-Plus支持所有Mybatis原生的特性&#xff0c; 所以引入Mybatis-Plus不会对现有的Mybatis构架产生任何影响。MyBatis 增强工具包&#xff0c;简化 C…

强化学习入门

强化学习是指智能体通过不断试错的方式进行学习&#xff0c;利用与环境进行交互时获得的奖励或惩罚来指导行为 试错学习 尝试&#xff08;决策-decision&#xff09;错误结果&#xff1a;每次尝试无论产生什么样的结果&#xff0c;都会对下一次结果产生影响 奖励&#xff08;…

Springboot 子工程构建完后无法找到springboot依赖

问题: 构建完子工程后无法找到SpringBootTest 解决方案: 最好用这个构建 https://www.cnblogs.com/he-wen/p/16735239.html 1.先观察项目目录 是否正确 2.观察子工程目录 3.看pom.xml中是否引用springboot依赖 4.检查代码 查看父项目是否包含子模块 查看子模块的父项目是否…

vscode 中配置 python 虚拟环境

vscode 中配置 python 虚拟环境 Start 在编写代码的过程中&#xff0c;我们经常会用到一些第三方依赖&#xff0c;帮助我们快速完成功能。在 Python 中&#xff0c;默认情况都是统一安装在全局环境中&#xff0c;但是这样伴随着电脑项目越来越多&#xff0c;不同项目对依赖的…

Intel Quartus II IP之DP1.4 工程的创建与使用

前述&#xff1a; Win10电脑安装了Quartus 21.4&#xff0c;这可以满足绝大多数情况&#xff0c;但是对于创建DMI/DP IP的设计demo工程时会报错&#xff0c;因为还需要Eclipse与WSL&#xff08;Windows Subsystem for Linux&#xff09;。 具体安装方法参考&#xff1a; Int…

redis7部署集群:包含主从模式、哨兵模式、Cluster集群模式等三种模式

前言&#xff1a; redis部署集群常见的一般有三种模式&#xff1a;主从模式&#xff0c;Sentinel&#xff08;哨兵模式&#xff09;&#xff0c;Redis Cluster&#xff08;高可用Cluster集群&#xff09;&#xff0c;根据不同的需求可自定义选择部署方式。 Redis 主从模式&…

c# 视频播放之Vlc.DotNet.Forms

先说下优缺点 优点&#xff1a;与电脑无关&#xff0c;能播放主流编码格式视频。 缺点&#xff1a;只能播放本地视频&#xff0c;网络视频播放不了。 下面是具体操作和代码 1. 安装Vlc.DotNet.Forms 和 VideoLAN.LibVLC.Windows Vlc.DotNet.Forms 是播放库&#xff0c;Vid…

三网话费余额查询的API系统,基于thinkphp6.0框架,附带搭建教程

系统用的是Thinkphp6.0框架 PHP版本需要用8.2 搭建教程 1、源码上传后&#xff0c;吧运行目录改为 /public 2、然后吧 数据库文件.sql 这个文件导入到数据库内 3、打开/config/database.php目录文件&#xff0c;然后进行配置数据库信息 4、挂任务计划&#xff0c;用处&…

未来的NAS:连接您的数字生活

未来的NAS&#xff1a;连接您的数字生活 引言 网络附加存储&#xff08;Network Attached Storage&#xff0c;简称NAS&#xff09;是一种通过网络连接的存储设备&#xff0c;用于集中存储和共享数据。传统的NAS设备通常包含一个或多个硬盘驱动器&#xff0c;可以通过局域网连…

【python】学习笔记01

一、基础语法 1. 字面量 - 什么是字面量&#xff1f; 在代码中&#xff0c;被写下来的的固定的值&#xff0c;称之为字面量。 - 常用的值类型 Python中常用的有6种值&#xff08;数据&#xff09;的类型。 666 13.14 "程序员"print(666) print(13.14) print(&qu…

服务器变矿机,该如何应对?

开始 恶意的挖矿程序会导致服务器cpu的异常占用&#xff0c;很让人讨厌。起初&#xff0c;我只是使用top命令显示出占用cpu不正常的进程&#xff0c;发现其中一个进程占用了百分之九十九点几&#xff0c;然后通过kill -9 <PID>命令干掉它。但总是过不了几天&#xff0c;…

C++三剑客之std::variant(二):深入剖析

目录 1.概述 2.辅助类介绍 2.1.std::negation 2.2.std::conjunction 2.3.std::is_destructible 2.4.std::is_object 2.5.is_default_constructible 2.6.std::is_trivially_destructible 2.7.std::in_place_type和std::in_place_index 3.原理分析 3.1.存储分析 3.2.…

Jira 宣布Data Center版涨价5%-15%,6年内第8次提价

近日&#xff0c;Atlassian官方面向合作伙伴发布2024年涨价通知&#xff1a; 自2024年2月15日起&#xff0c;旗下核心产品Jira Software、Confluence、Jira Service Management的DC版本&#xff08;Data Center版本&#xff09;价格提高5%-15%&#xff08;涨幅与坐席数阶梯相关…

Django中使用MySQL的视图View

文章目录 一、MySQL创建视图二、Django模型定义 一、MySQL创建视图 首先确定好自己需要链接的两张表及其对应的字段&#xff0c;视图的意义是将两张表数据联合一起变成一张新的表方便后续查询数据。 源Django两张表模型 class ProjectTaskRange(BaseModel, TimeModel):class …

IOS-高德地图SDK接入-Swift

申请key 这个要前往高德开发平台注册成为个人开发者然后在控制台创建一个应用&#xff1a; 高德开发平台 注册步骤就不写了&#xff0c;写一下创建应用的步骤&#xff1a; 1、点击应用管理——>我的应用 2、点击右上角的创建新应用 3、输入内容&#xff1a; 4、点击添加ke…

新定义51单片机(RD8G37)实现测距测速仪

本文描述用新定义51单片机&#xff08;RD8G37&#xff09;超声波一体测距传感器实现简单的测距测速仪。 测距仪演示效果 新定义RD8G37Q48RJ开发板 超声波测距模块&#xff1a; 8位并口屏 1、main.c unsigned short timeConsuming0; unsigned int oldDistance;void rectClearS…

阿里云国外服务器价格表

阿里云国外服务器优惠活动「全球云服务器精选特惠」&#xff0c;国外服务器租用价格24元一个月起&#xff0c;免备案适合搭建网站&#xff0c;部署独立站等业务场景&#xff0c;阿里云服务器网aliyunfuwuqi.com分享阿里云国外服务器优惠活动&#xff1a; 全球云服务器精选特惠…