《操作系统导论》第16章读书笔记:分段

news2024/11/28 22:07:39

《操作系统导论》第16章读书笔记:分段

效果图

效果图

—— 杭州 2024-03-31 夜


文章目录

  • 《操作系统导论》第16章读书笔记:分段
    • 0.前言
    • 1.分段:泛化的基址/界限
    • 2.我们引用哪个段?
    • 3.栈怎么办
    • 4.支持共享
    • 5.细粒度与粗粒度的分段、操作系统支持
    • 6.小结
    • 7.补充笔记:地址空间和进程空间的关系
    • 8.补充笔记:一个CPU有几个基址寄存器和界限寄存器?一个CPU有几个地址空间和进程空间?一个CPU有几个MMU?

0.前言

到目前为止,我们一直假设将所有进程的地址空间完整地加载到内存中。利用基址和界限寄存器,操作系统很容易将不同进程重定位到不同的物理内存区域。但是,对于这些内存区域,你可能已经注意到一件有趣的事:栈和堆之间,有一大块“空闲”空间。

从图 16.1 中可知,如果我们将整个地址空间放入物理内存,那么栈和堆之间的空间并没有被进程使用,却依然占用了实际的物理内存。因此,简单的通过基址寄存器和界限寄存器实现的虚拟内存很浪费。另外,如果剩余物理内存无法提供连续区域来放置完整的地址空间,进程便无法运行。这种基址加界限的方式看来并不像我们期望的那样灵活。因此:

关键问题:怎样支持大地址空间?
怎样支持大地址空间,同时栈和堆之间(可能)有大量空闲空间?在之前的例子里,地址空间非常小,所以这种浪费并不明显。但设想一个 32 位(4GB)的地址空间,通常的程序只会使用几兆的内存,但需要整个地址空间都放在内存中。

在这里插入图片描述

1.分段:泛化的基址/界限

  • 分段:这个想法很简单,在MMU 中引入不止一个基址和界限寄存器对,而是给地址空间内的每个逻辑段(segment)一对。一个段只是地址空间里的一个连续定长的区域在典型的地址空间里有3个逻辑不同的段:代码、栈和堆。分段的机制使得操作系统能够将不同的段放到不同的物理内存区域,从而避免了虚拟地址空间中的未使用部分占用物理内存。

  • 稀疏地址空间,sparse address spaces:如图16.2所示,64KB的物理内存中放置了3个段(为操作系统保留16KB)。从图中可以看到,只有已用的内存才在物理内存中分配空间,因此可以容纳巨大的地址空间,其中包含大量未使用的地址空间(有时又称为稀疏地址空间,sparse address spaces)。

  • 段错误:段错误指的是在支持分段的机器上发生了非法的内存访问。有趣的是,即使在不支持分段的机器上
    这个术语依然保留。

  • 段异常(segmentation violation)或段错误(segmentation fault):如果我们试图访问非法的地址,例如7KB,它超出了堆的边界呢?你可以想象发生的情况:硬件会发现该地址越界,因此陷入操作系统,很可能导致终止出错进程。这就是每个C 程序员都感到恐慌的术语的来源。

// 假设:基址寄存器值为 32KB(即 32768 字节),界限寄存器值为 2KB(即 2048 字节)

// 虚拟地址转换为物理地址的函数
function 虚拟地址转物理地址(虚拟地址, 基址寄存器, 界限寄存器) {
    // 越界检查
    if (虚拟地址 >= 界限寄存器) {
        抛出异常("段错误"); // 越界访问
    } else {
        // 计算物理地址并返回
        物理地址 = 虚拟地址 + 基址寄存器;
        return 物理地址;
    }
}

// 示例 1: 虚拟地址 100 的转换
物理地址 = 虚拟地址转物理地址(100, 32768, 2048);
// 物理地址应该是 32868

// 示例 2: 堆中的地址 4200 的转换
// 假设堆基址寄存器值为 34KB(即 34816 字节),界限寄存器值为 2KB
物理地址 = 虚拟地址转物理地址(4200 - 4096, 34816, 2048); // 偏移量是 4200 - 4096
// 物理地址应该是 34920

// 越界检查示例
try {
    虚拟地址转物理地址(4400, 32768, 2048); // 假设这是一个非法地址,因为它超出了界限
} catch (异常 e) {
    打印("异常: " + e); // 捕获到越界异常
}

在这里插入图片描述

在这里插入图片描述

2.我们引用哪个段?

  • 硬件在地址转换时使用段寄存器。它如何知道段内的偏移量,以及地址引用了哪个段?
  • 一种常见的方式,有时称为显式(explicit)方式,就是用虚拟地址的开头几位来标识不同的段。
  • 在我们的例子中,如果前两位是00,硬件就知道这是属于代码段的地址,因此使用代码段的基址和界限来重定位到正确的物理地址。如果前两位是01,则是堆地址,对应地,使用堆的基址和界限。
  • 前两位(01)告诉硬件我们引用哪个段。剩下的12 位是段内偏移:0000 0110 1000(即十六进制0x068 或十进制104)。因此,硬件就用前两位来决定使用哪个段寄存器,然后用后12位作为段内偏移。偏移量与基址寄存器相加,硬件就得到了最终的物理地址。
  • 请注意,偏移量也简化了对段边界的判断。我们只要检查偏移量是否小于界限,大于界限的为非法地址
  • 上面使用两位来区分段,但实际只有3个段(代码、堆、栈),因此有一个段的地址空间被浪费。因此有些系统中会将堆和栈当作同一个段,因此只需要一位来做标识。
  • 硬件还有其他方法来决定特定地址在哪个段。在隐式(implicit)方式中,硬件通过地址产生的方式来确定段。例如,如果地址由程序计数器产生(即它是指令获取),那么地址在代码段。如果基于栈或基址指针,它一定在栈段。其他地址则在堆段。
  • 如果基址和界限放在数组中(每个段一项),为了获得需要的物理地址,硬件会做下面这样的事:
1    // 获取14位虚拟地址(VA)的前2位
2    Segment = (VirtualAddress & SEG_MASK) >> SEG_SHIFT
3    
4    // 现在获取偏移量
5    Offset  = VirtualAddress & OFFSET_MASK
6    
7    // 如果偏移量超出了段的界限
8    if (Offset >= Bounds[Segment])
9        RaiseException(PROTECTION_FAULT) // 抛出保护故障异常
10   else
11       PhysAddr = Base[Segment] + Offset // 计算物理地址
12       Register = AccessMemory(PhysAddr) // 从物理地址访问内存,并将数据存入寄存器

在这里插入图片描述

3.栈怎么办

  • 它反向增长,地址转换必须有所不同。
  • 需要一点硬件支持。除了基址和界限外,硬件还需要知道段的增长方向(用一位区分,比如1代表自小而大增长,0 反之)。
  • 例程1:
# 定义段寄存器的值,这里仅以栈的信息为例
# 假设栈的基址为28KB,大小为2KB
SEGMENT_BASE_STACK = 28 * 1024  # 栈段的基址(28KB转换为字节)
SEGMENT_SIZE_STACK = 2 * 1024   # 栈段的大小(2KB转换为字节)

# 虚拟地址转换函数
def convert_stack_virtual_to_physical(virtual_address):
    # 如果虚拟地址不在栈的界限内,触发保护故障异常
    if virtual_address >= SEGMENT_SIZE_STACK:
        raise Exception("保护故障:虚拟地址超出了栈的界限")

    # 计算物理地址:基址 + 虚拟地址(偏移量)
    physical_address = SEGMENT_BASE_STACK + virtual_address
    return physical_address

# 示例:对栈内的虚拟地址进行转换
try:
    # 假设要转换的栈虚拟地址为1500字节偏移量
    virtual_address = 1500

    # 调用函数进行转换
    physical_address = convert_stack_virtual_to_physical(virtual_address)

    # 打印转换后的物理地址
    print("物理地址:", physical_address)
except Exception as e:
    # 捕获并打印异常信息
    print(str(e))
  • 例程2:
// 假设:每个段有一个基址(Base)和界限(Limit)寄存器,以及一个反向增长标志(GrowDown)

// 段寄存器的数组,每个段一个寄存器,包含基址、界限和反向增长标志
SegmentRegisters = [
    { Base: 32KB, Limit: 2KB, GrowDown: false }, // 代码段
    { Base: 34KB, Limit: 2KB, GrowDown: false }, // 堆段
    { Base: 28KB, Limit: 2KB, GrowDown: true }   // 栈段
]

// 虚拟地址转换为物理地址的函数
function 虚拟地址转物理地址(虚拟地址, 段寄存器) {
    // 计算段内偏移量
    偏移量 = 虚拟地址 % 段的大小
    // 根据段是否反向增长调整偏移量
    if (段寄存器.GrowDown) {
        偏移量 = 段寄存器.Limit - 偏移量 - 1
    }
    // 越界检查
    if (偏移量 < 0 || 偏移量 >= 段寄存器.Limit) {
        抛出异常("段错误"); // 越界访问
    } else {
        // 计算物理地址并返回
        物理地址 = 段寄存器.Base + 偏移量
        return 物理地址
    }
}

// 示例: 虚拟地址 100 的转换(假设在代码段)
物理地址 = 虚拟地址转物理地址(100, SegmentRegisters[代码段])
// 物理地址应该是 100 + 32KB = 32868

// 示例: 虚拟地址 4200 的转换(假设在堆段)
物理地址 = 虚拟地址转物理地址(4200 - 4KB, SegmentRegisters[堆段])
// 物理地址应该是 104 + 34KB = 34920

// 越界检查示例
try {
    虚拟地址转物理地址(4400, SegmentRegisters[堆段]) // 假设这是一个非法地址,因为它超出了界限
} catch (异常 e) {
    打印("异常: " + e) // 捕获到越界异常
}

在这里插入图片描述

4.支持共享

  • 具体来说,要节省内存,有时候在地址空间之间共享(share)某些内存段是有用的。尤其是,代码共享很常见,今天的系统仍然在使用。
  • 保护位(protection bit):为了支持共享,需要一些额外的硬件支持,这就是保护位(protection bit)。基本为每个段增加了几个位,标识程序是否能够读写该段,或执行其中的代码。通过将代码段标记为只读,同样的代码可以被多个进程共享,而不用担心破坏隔离。虽然每个进程都认为自己独占这块内存,但操作系统秘密地共享了内存,进程不能修改这些内存,所以假象得以保持。
  • 表16.3 展示了一个例子,是硬件(和操作系统)记录的额外信息。可以看到,代码段的权限是可读和可执行,因此物理内存中的一个段可以映射到多个虚拟地址空间。
  • 有了保护位,前面描述的硬件算法也必须改变。除了检查虚拟地址是否越界,硬件还需要检查特定访问是否允许。如果用户进程试图写入只读段,或从非执行段执行指令,硬件会触发异常,让操作系统来处理出错进程。

在这里插入图片描述

5.细粒度与粗粒度的分段、操作系统支持

  • 到目前为止,我们的例子大多针对只有很少的几个段的系统(即代码、栈、堆)。我们可以认为这种分段是粗粒度的(coarse-grained),因为它将地址空间分成较大的、粗粒度的块。但是,一些早期系统(如Multics[CV65,DD68])更灵活,允许将地址空间划分为大量较小的段,这被称为细粒度(fine-grained)分段

  • 支持许多段需要进一步的硬件支持,并在内存中保存某种段表(segment table)。这种段表通常支持创建非常多的段,因此系统使用段的方式,可以比之前讨论的方式更灵活。例如,像Burroughs B5000 这样的早期机器可以支持成千上万的段,有了操作系统和硬件的支持,编译器可以将代码段和数据段划分为许多不同的部分[RK68]。当时的考虑是,通过更细粒度的段,操作系统可以更好地了解哪些段在使用哪些没有,从而可以更高效地利用内存。

  • 系统运行时,地址空间中的不同段被重定位到物理内存中。与我们之前介绍的整个地址空间只有一个基址/界限寄存器对的方式相比,大量节省了物理内存。具体来说,栈和堆之间没有使用的区域就不需要再分配物理内存,让我们能将更多地址空间放进物理内存。

  • 然而,分段也带来了一些新的问题。我们先介绍必须关注的操作系统新问题。第一个是老问题:操作系统在上下文切换时应该做什么?你可能已经猜到了:各个段寄存器中的内容必须保存和恢复。显然,每个进程都有自己独立的虚拟地址空间,操作系统必须在进程运行前,确保这些寄存器被正确地赋值。

  • 第二个问题更重要,即管理物理内存的空闲空间。新的地址空间被创建时,操作系统需要在物理内存中为它的段找到空间。之前,我们假设所有的地址空间大小相同,物理内存可以被认为是一些槽块,进程可以放进去。现在,每个进程都有一些段,每个段的大小也可能不同。

  • 一般会遇到的问题是,物理内存很快充满了许多空闲空间的小洞,因而很难分配给新的段,或扩大已有的段。这种问题被称为·外部碎片(external fragmentation)[R69],如图16.3(左边)所示。

  • 在这个例子中,一个进程需要分配一个20KB 的段。当前有24KB 空闲,但并不连续(是3个不相邻的块)。因此,操作系统无法满足这个20KB 的请求。

  • 该问题的一种解决方案是紧凑(compact)物理内存,重新安排原有的段。例如,操作系统先终止运行的进程,将它们的数据复制到连续的内存区域中去,改变它们的段寄存器中的值,指向新的物理地址,从而得到了足够大的连续空闲空间。这样做,操作系统能让新的内存分配请求成功。但是,内存紧凑成本很高,因为拷贝段是内存密集型的,一般会占用大量的处理器时间。图16.3(右边)是紧凑后的物理内存。

  • 一种更简单的做法是利用空闲列表管理算法,试图保留大的内存块用于分配。相关的算法可能有成百上千种,包括传统的最优匹配(best-fit,从空闲链表中找最接近需要分配空间的空闲块返回)、最坏匹配(worst-fit)、首次匹配(first-fit)以及像伙伴算法(buddy algorithm)[K68]这样更复杂的算法。Wilson等人做过一个很好的调查[W+95],如果你想对这些算法了解更多,可以从它开始,或者等到第17章,我们将介绍一些基本知识。但遗憾的是,无论算法多么精妙,都无法完全消除外部碎片,因此,好的算法只是试图减小它。

在这里插入图片描述

6.小结

分段解决了一些问题,帮助我们实现了更高效的虚拟内存。不只是动态重定位,通过避免地址空间的逻辑段之间的大量潜在的内存浪费,分段能更好地支持稀疏地址空间。它还很快,因为分段要求的算法很容易,很适合硬件完成,地址转换的开销极小。分段还有一个附加的好处:代码共享如果代码放在独立的段中,这样的段就可能被多个运行的程序共享

但我们已经知道,在内存中分配不同大小的段会导致一些问题,我们希望克服。首先,是我们上面讨论的外部碎片由于段的大小不同,空闲内存被割裂成各种奇怪的大小,因此满足内存分配请求可能会很难。用户可以尝试采用聪明的算法[W+95],或定期紧凑内存,但问题很根本,难以避免。

第二个问题也许更重要,分段还是不足以支持更一般化的稀疏地址空间。例如,如果有一个很大但是稀疏的堆,都在一个逻辑段中,整个堆仍然必须完整地加载到内存中。换言之,如果使用地址空间的方式不能很好地匹配底层分段的设计目标,分段就不能很好地工作。因此我们需要找到新的解决方案。你准备好了吗?

在这里插入图片描述

7.补充笔记:地址空间和进程空间的关系

地址空间和进程空间是两个相关但不同的概念,它们在操作系统中以不同的方式被管理和使用。

  1. 地址空间

    地址空间通常指的是一个处理器可以寻址的全部内存范围。在32位系统中,地址空间通常是4GB(2的32次方),在64位系统中则远远大于此数值。地址空间包括了所有可能的地址,这些地址可以是物理内存地址,也可以是虚拟内存地址。

    • 物理地址空间:是指CPU通过其地址引脚直接访问的内存(RAM、ROM、映射的I/O等)的范围。
    • 虚拟地址空间:是指通过虚拟内存管理,操作系统提供给应用程序和进程的一套地址,这些地址会通过MMU映射到物理地址空间中的实际位置。
  2. 进程空间

    进程空间(有时称为进程的虚拟地址空间)是指分配给单个进程的内存区域,它是进程可见的地址空间。每个进程都有自己的独立的虚拟地址空间,操作系统和MMU负责将此虚拟地址空间映射到物理地址空间上。这意味着两个不同的进程可以使用相同的虚拟地址指向不同的物理内存或者映射到相同的物理地址但是拥有不同的权限和属性。

地址空间和进程空间的关系

  • 隔离性:每个进程拥有独立的虚拟地址空间,这样即使它们的虚拟地址相同,也不会相互影响,因为这些地址映射到物理地址空间的不同部分。这提供了良好的安全性和稳定性,因为进程不能直接访问其他进程的内存。

  • 映射:操作系统的内存管理系统负责将进程的虚拟地址空间映射到物理地址空间。这通常通过页表完成,页表存储了虚拟地址到物理地址的映射信息。

  • 共享:尽管每个进程有自己独立的虚拟地址空间,但是有时候不同的进程需要共享内存。操作系统可以将同一块物理内存映射到多个进程的虚拟地址空间中,这通常用于进程间通信或共享库的映射。

因此,地址空间是处理器级别的内存寻址概念,而进程空间是操作系统级别的内存隔离和管理概念。操作系统通过虚拟内存管理技术,为每个进程创建了一种错觉,使其认为自己拥有连续的、独立的内存区域,而实际上它们是在共享同一个物理地址空间。

特性地址空间进程空间
定义CPU可以寻址的全部内存范围,包括物理和虚拟内存。分配给单个进程的虚拟内存区域,是进程可见和可操作的内存范围。
类型物理地址空间和虚拟地址空间。每个进程的虚拟地址空间。
用途CPU访问内存和其他资源的能力。隔离进程,确保进程内存访问的独立性和安全性。
管理由CPU和MMU负责物理和虚拟地址的映射。由操作系统负责管理,确保每个进程有独立的地址空间。
隔离性无(物理地址空间是共享的)。高(每个进程的虚拟地址空间彼此隔离)。
共享物理内存可以被多个进程共享(如通过共享内存)。虚拟地址空间通常是独立的,但可以有映射到相同物理内存的部分。
映射关系虚拟地址空间到物理地址空间的映射由页表或段表管理。进程的虚拟地址空间到物理地址空间的映射通过操作系统的内存管理器控制。
大小取决于CPU架构(32位、64位等)。取决于操作系统和系统配置,但通常每个进程的虚拟空间都有一个上限。
多样性通常一个系统中物理和虚拟地址空间是统一的。每个进程的进程空间可以不同,取决于其内存需求和操作系统的分配。

8.补充笔记:一个CPU有几个基址寄存器和界限寄存器?一个CPU有几个地址空间和进程空间?一个CPU有几个MMU?

CPU的基址寄存器和界限寄存器的数量,地址空间和进程空间的数量,以及MMU(内存管理单元)的数量,这些都不是固定的,而是取决于具体的CPU设计和它的用途。

  1. 基址寄存器和界限寄存器
    基址寄存器和界限寄存器主要用于分段内存管理。在分段内存管理系统中,每个段(segment)通过一对基址(base)和界限(limit)寄存器来描述。这样的寄存器对的数量取决于CPU的架构和设计。例如,较旧的x86架构的处理器可能只有几个用于分段的基址和界限寄存器(如CS, DS, ES, FS, GS, SS等),而更现代或者专用的处理器可能有更多或根本不使用分段内存管理。

  2. 地址空间和进程空间
    一个CPU通常管理一个地址空间,即它可以通过其地址线访问的全部内存范围。但这个地址空间可以通过不同的机制(如分页或分段)被映射到多个进程空间。每个进程空间可以想象成一个独立的地址空间,它是操作系统通过虚拟内存管理功能提供给单个进程的内存视图。理论上,一个操作系统可以支持任意多个进程空间,但实际上这受限于系统的物理内存、虚拟内存限制和操作系统设计。

  3. MMU(内存管理单元)
    大多数现代CPU会有一个内存管理单元(MMU)。MMU负责虚拟地址到物理地址的转换,并且通常支持分页或分段机制。一般情况下,每个CPU核心会有一个MMU。在多核心处理器中,每个核心可能有自己的MMU,或者有些设计可能共享一个MMU。

在讨论具体的CPU时,需要参考该CPU的技术手册或架构文档来获取准确的寄存器和硬件特性信息。

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

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

相关文章

从vrrp、bfd、keepalived到openflow多控制器--理论篇

vrrp 在一个网络中&#xff0c;通常会使用vrrp技术来实现网关的高可用。 vrrp&#xff0c;即Virtual Router Redundancy Protocol&#xff0c;虚拟路由冗余协议。 应用场景 典型的如下面这个例子&#xff1a; 当Router故障后&#xff0c;将会导致HostA-C都无法连接外部的I…

嵌入式linux学习之opencv交叉编译

1.下载opencv源码 OpenCV官方源码下载链接为https://opencv.org/releases/&#xff0c;选择3.4.16版本下载。放在ubuntu系统~/opencv文件夹中&#xff0c;解压缩&#xff0c;opencv文件夹中新建build和install文件夹用于存放编译文件和安装文件&#xff1a; 2. 安装编译工具…

springboot配置文件application.properties,application.yml/application.yaml

application.properties Springboot提供的一种属性配置方式&#xff1a;application.properties 初始时配置文件中只有一行语句。 启动时&#xff0c;比如tomcat的端口号默认8080&#xff0c;路径默认。如果我们要改变端口号及路径之类的可以在application.properties中配置。…

【C语言环境】Sublime中运行C语言时MinGW环境的安装

要知道&#xff0c;GCC 官网提供的 GCC 编译器是无法直接安装到 Windows 平台上的&#xff0c;如果我们想在 Windows 平台使用 GCC 编译器&#xff0c;可以安装 GCC 的移植版本。 目前适用于 Windows 平台、受欢迎的 GCC 移植版主要有 2 种&#xff0c;分别为 MinGW 和 Cygwin…

【算法竞赛进阶指南】0x11 栈

0x11 栈 基础应用 两个栈维护前面区间的最小元素 41. 包含min函数的栈 - AcWing题库 可以开个小根堆维护最小值&#xff0c;但是这样时间是logN的。如果只用一个变量来存&#xff0c;一旦出现了出栈操作就无从下手了。因此用一个线性结构来保存每个历史时刻的最小值。 开两个…

vue3封装Element分页

配置当前页 配置每页条数 页面改变、每页条数改变都触发回调 封装分页 Pagination.vue <template><el-paginationbackgroundv-bind"$attrs":page-sizes"pageSizes"v-model:current-page"page"v-model:page-size"pageSize":t…

常用字符串函数及模拟实现

1.strlen() 用法:求字符串长度 size_t strlen(const char *Str) 参数:字符串的起始地址,可以传地址也可以直接传一个字符串 返回值:字符串长度 模拟实现: 自己实现求字符串长度的函数(三种方法)-CSDN博客 2.strcpy() 用法:拷贝字符串 char* strcpy(char*_Destination,co…

【PduR路由】IPduM模块详细介绍

目录 1.IpduM功能简介 2.IpduM模块依赖的其他模块 2.1RTE (BSW Scheduler) 2.2PDU Router 2.3COM 3.IpduM功能详解 3.1 功能概述 3.2 I-PDU多路复用I-PDU Multiplexing 3.2.1 Definitions and Layout 3.2.2通用功能描述 General 3.2.3模块初始化 Initialization 3.…

fpga_awb

色温: sesor原始图像中的白色如果不经AWB处理&#xff0c;在高色温(如阴天)下偏蓝&#xff0c;在低色温下偏黄。 引入白平衡算法 而AWB的核心就是调整图像色温&#xff0c;使得摄像头采集的图像更加真实&#xff0c;达到人眼观察的效果。 白平衡一般通过调节传感器输出图像RGB…

CCF-CSP19<2020-06>-第1/2题

202006-1 线性分类器 题目&#xff1a;202006-1 题目分析&#xff1a; 给定n个点&#xff0c;并标记为AB两类&#xff0c;问给定直线是否能将其分为两个点集。 简单数学知识&#xff0c;点在直线上满足axbyc0&#xff0c;点在直线割平面所得的上下其值会正负相反。 AC代码…

信息安全技术基础知识总结

一、信息安全基础知识 信息安全基本要素&#xff1a; 1. 机密性&#xff08;C&#xff09;&#xff1a;确保信息不暴露给未授权的实体或进程 2. 完整性&#xff08;I&#xff09;&#xff1a;只有得到允许的人才能修改数据&#xff0c;并且能够判别出数据是否已被篡改 3. 可用性…

大话设计模式之外观模式

外观模式&#xff08;Facade Pattern&#xff09;是一种软件设计模式&#xff0c;旨在提供一个简单的接口&#xff0c;隐藏系统复杂性&#xff0c;使得客户端能够更容易地使用系统。这种模式属于结构型模式&#xff0c;它通过为多个子系统提供一个统一的接口&#xff0c;简化了…

docker无脑部署zabbix6.0+agent

文章目录 前言一、安装docker-compose二、部署Zabbix Server 6.0agent1.创建父目录2.拉取镜像3.编辑docker-compose.yml文件 二、浏览器访问Zabbix1.url栏输入http://ip/2.修改主机配置 总结 前言 随着监控的不断发展&#xff0c;zabbix这门技术也越来越重要&#xff0c;很多人…

PVE设置显卡直通(二:Linux显卡直通,以及Linux系统下安装cuda库)

PVE设置显卡直通(一:硬件设置) 本文仅记录PVE关于Linux下的显卡直通步骤 例程不过多阐述 ps: 无直通经验的同学,先参阅 PVE设置显卡直通(一:硬件设置),再参阅本博文 参阅完成 PVE设置显卡直通(一:硬件设置)后,直接在PVE面板中添加显卡硬件到自己的主机即可,此文中…

【Java与数学】若不等式x^2-a*x+a<0的解集中恰有3个整数,求a的范围?

【分析】 既然不等式存在解集&#xff0c;说明一元二次方程x^2-a*xa0有解&#xff1b; 解之间横跨三个整数&#xff0c;说明Δ大于0&#xff1b; 三个整数必然是连续的&#xff0c;因为f(x)x^2-a*xa最多只与x轴存在两个交点&#xff0c;这是题设里的隐含条件。 【传统解法】…

C++项目——集群聊天服务器项目(十)点对点聊天业务

本节来实现C集群聊天服务器项目中的点对点聊天业务&#xff0c;一起来试试吧 一、点对点聊天业务 聊天服务器中一个重要的功能就是实现点对点聊天&#xff0c;客户端发送的信息包含聊天业务msgid、自身 的id和姓名、聊天对象的id号以及聊天信息&#xff0c;例如&#xff1a; …

C语言TCP服务器模型 : select + 多线程与双循环单线程阻塞服务器的比较

观察到的实验现象: 启动三个客户端: 使用双循环阻塞服务器:只能accept后等待收发,同时只能与一个客户端建立连接,必须等已连接的客户端多次收发 明确断开后才能与下个客户端连接 使用IO多路复用select:可以同时接收所有的连接请求,并且连接状态一直是存活的,直到客户端关闭连…

离散数学--谓词逻辑之复习与前束范式与谓词演算的推理理论

引子&#xff1a;在命题演算中&#xff0c;常常要化成规范形式&#xff0c;对于谓词的演算&#xff0c;可以化成与他等价的范式&#xff01; 前束范式定义&#xff1a; 一个公式&#xff0c;如果量词均非否定地在全式的开头&#xff0c;它们的作用域延伸到整个公式的末尾&…

八、大模型之Fine-Tuning(1)

1 什么时候需要Fine-Tuning 有私有部署的需求开源模型原生的能力不满足业务需求 2 训练模型利器Hugging Face 官网&#xff08;https://huggingface.co/&#xff09;相当于面向NLP模型的Github基于transformer的开源模型非常全封装了模型、数据集、训练器等&#xff0c;资源…

工作究竟是谁的?

在近两年的就业环境中&#xff0c;普遍存在着挑战与不确定性&#xff0c;许多人追求的是一种稳定的工作和收入来源。在这样的背景下&#xff0c;我们来探讨一个核心问题&#xff1a;工作的归属是谁的&#xff1f; 根据《穷爸爸富爸爸》中提出的ESBI四象限理论&#xff0c;我们可…