【操作系统】操作系统知识点总结(秋招篇)
文章目录
- 前言
- 操作系统主要做了哪些工作?
- 进程 线程 协程之间的区别
- 进程的组成部分
- 介绍一下进程的PCB
- 讲一下进程的五态 以及它们的状态转移
- 用户态和内核态是什么?
- 进程在用户态和内核态之间是如何切换的
- 讲一下进程之间的通信方式
- 讲一下进程调度的三个层次
- 介绍一下七转态模型
- 什么时候能进程调度 什么时候不能?
- 讲一下常见的进程调度算法
- 讲一下进程调度算法的评价指标
- 各个进程调度算法优缺点
- 进程的饥饿性是什么意思
- 讲一下进程的同步和互斥
- 什么是临界区
- 介绍一下进程同步与互斥经典问题(生产者-消费者问题等)
- 什么是死锁 如何解决
- 进程同步这里问题较少,是因为在网络编程中已经出现过多次了,这里不再重复写了
- 逻辑地址和物理地址的区别
- 对于x=x+1指令 这条指令在计算机如何执行的
- 讲一下程序放到内存运行的全过程 (编译,链接,载入)
- 指令的逻辑地址如何转为物理地址(指令装入内存的三种方式)
- 讲一下链接的三种方式
- 操作系统对于内存管理的主要工作是哪些?
- 内存保护的两种方式
- 讲一下内存扩容中的覆盖技术和交换技术
- 介绍一下磁盘空间的文件区和对换区
- 介绍一下内存连续分配的三种方式(单一连续分配 固定分区分配 动态分区分配)
- 内存连续分配的三种方式的优缺点
- 介绍一下动态分区分配的四种算法(首次适应算法、最佳适应算法、最坏适应算法、临近适应算法)
- 讲一下内部碎片和外部碎片
- 介绍一下分页存储(大部分问题都在这段话中了我就不展开了)
- 引入快表的地址转换有和不同 快表的作用是?
- 介绍一下单级页表的问题 为何引出二级页表?
- 介绍一下基本分段存储管理
- 分段和分页的区别
- 介绍一下段页式存储管理
- 介绍一下请求分页存储管理
- 传统非连续分配(分页,分段,段页式)和虚拟内存分配(分页,分段,段页式)的区别
- 请求分页和基本分页的区别
- 介绍常见的页面置换算法
- 介绍一下 页面分配策略
- IO设备分类
- 介绍一下IO设备控制器的主要功能
- I/O设备有几种控制方式 介绍一下
- I/O软件层次结构
- 操作系统的核心子系统主要功能
- 文件管理和磁盘管理实在不想写了
前言
秋招笔记汇总篇之操作系统知识点总结
笔者是拿chatgpt写的,所以可能部分答案存在一定出路(3.5版本GPT有些缺陷),有出入的地方还望各位同学指出。
2023.8.3 首次更新,文件管理和磁盘管理部分暂时没写
参考:王道计算机考研
操作系统主要做了哪些工作?

进程 线程 协程之间的区别
进程(Process),线程(Thread),和协程(Coroutine)都是计算机程序并发执行的方式,但它们在概念和实现上有一些重要的区别。
进程(Process):
进程是计算机中的一个独立执行单位,拥有独立的内存空间和系统资源。
每个进程都有自己的代码、数据、文件资源等,相互之间不能直接访问对方的内部数据。
进程之间通常通过进程间通信(IPC,Inter-Process Communication)机制来进行数据交换和共享信息。
进程切换开销较大,因为涉及到切换内存空间和系统资源的操作。
线程(Thread):
线程是进程内的一个执行单元,多个线程共享同一个进程的内存空间和资源。
线程可以看作是轻量级的进程,它们共享同一个地址空间,因此可以更方便地进行数据共享和通信。
线程之间的切换开销较小,因为不需要切换内存空间,但仍然需要切换寄存器和执行栈。
线程通常更适合用于执行多个并发任务,如多线程编程。
协程(Coroutine):
协程是一种用户态的轻量级线程,由程序员手动控制调度和切换。
协程可以在一个线程内部同时存在多个,它们共享线程的执行上下文,但可以由程序员在合适的时机进行切换。
协程通常用于处理高并发的任务,例如网络编程、异步IO操作等,可以有效地避免线程切换的开销。
协程的切换是由程序员自己决定的,因此可以更灵活地控制执行流程。
总结:
进程是独立的执行单位,拥有独立的内存空间和资源,进程间通信开销较大。
线程是进程内的执行单元,共享同一个内存空间,切换开销较小,适合多任务并发。
协程是用户态的轻量级线程,由程序员手动控制调度和切换,适合处理高并发的任务。
进程的组成部分
进程是计算机科学中的一个重要概念,表示正在执行的程序的实例。一个进程可以包含多个线程,它们共享相同的代码和数据空间,但拥有各自的执行上下文。一个典型的进程通常包括以下几个组成部分:
程序代码(Code Segment): 这是进程所执行程序的指令集合,通常存储在可执行文件中。代码段是只读的,包含了程序的指令和逻辑。
数据段(Data Segment): 数据段存储了全局变量、静态变量以及常量数据。这些数据在程序的整个生命周期内保持不变。
进程控制块(Process Control Block,PCB): PCB 是操作系统内核用于管理进程的数据结构。它包含了进程的各种属性和状态信息,如进程标识符、程序计数器、寄存器状态、内存分配情况、打开文件描述符等。
这些组成部分一起构成了一个进程的完整结构,操作系统利用这些信息来管理和控制进程的执行、调度和资源分配。每个进程都是一个独立的执行单元,通过操作系统的调度机制来合理地分配CPU时间,以实现多任务并发执行。

介绍一下进程的PCB

进程控制块(Process Control Block,PCB)是操作系统内核中用于管理和控制进程的重要数据结构。每个正在运行或等待运行的进程都有一个对应的PCB,PCB包含了进程的各种属性、状态信息以及与进程相关的控制信息。PCB的内容在进程的生命周期中会不断变化,操作系统利用这些信息来管理进程的调度、资源分配和状态转换。以下是PCB通常包含的一些重要信息:
进程标识符(Process Identifier,PID): 唯一标识进程的数值,用于在操作系统中进行进程的识别和区分。
程序计数器(Program Counter,PC): 存储了进程当前执行的指令位置,即下一条要执行的指令地址。
寄存器状态(Register State): 包括CPU寄存器的内容,用于保存进程的上下文信息。在进程切换时,这些寄存器的状态需要保存和恢复。
进程状态(Process State): 描述进程当前所处的状态,如运行、就绪、阻塞等。操作系统根据进程状态来进行调度和资源分配。
程序代码和数据段信息: 包括代码段、数据段、堆和栈的起始地址和长度等信息。
资源分配信息: 记录了进程分配到的系统资源,如内存分配情况、打开的文件描述符、已分配的IO设备等。
进程优先级和调度信息: 描述了进程的优先级,以及与进程调度相关的信息,如时间片大小、调度策略等。
父子进程关系: 如果有父子进程关系,PCB中可能包含父进程的PID以及子进程的列表。
信号处理信息: 记录了进程注册的信号处理函数以及信号的状态。
进程计时器和计数器: 用于记录进程运行时间、等待时间等性能信息。
PCB的内容可能会因操作系统的设计和实现而有所不同,但上述信息是常见的PCB内容。操作系统通过管理PCB,可以有效地控制进程的调度、资源分配和状态转换,从而实现多任务并发的操作。当一个进程从运行态切换到就绪态或阻塞态时,其PCB的信息会相应地更新,以便操作系统能够正确地管理进程的状态和执行。
讲一下进程的五态 以及它们的状态转移
进程的五态是操作系统中用于描述进程状态的一种模型,它将进程的生命周期分为五个状态:运行(Running)、就绪(Ready)、阻塞(Blocked)、创建(New)、终止(Terminated)。这种状态模型可以帮助操作系统有效地管理进程的调度和资源分配。以下是这些状态的详细说明以及它们之间的状态转移:
创建(New): 这是进程被创建但尚未被操作系统接受执行的状态。在这个状态下,进程正在等待操作系统分配必要的资源和初始化PCB等。
就绪(Ready): 进程已经获得了所有必要的资源,只等待CPU的调度来执行。处于就绪状态的进程已经准备好运行,但由于CPU资源有限,操作系统需要在就绪进程之间进行选择以确定哪个进程会进入运行态。
运行(Running): 进程正在CPU上执行指令,处于此状态的进程会获得CPU的执行时间。只有一个进程能够在某个时刻处于运行态。当一个进程的时间片用完、IO操作等原因导致其无法继续执行时,它可能会从运行态转移到就绪态或阻塞态。
阻塞(Blocked): 进程在等待某个事件(例如IO操作、资源就绪)发生时会进入阻塞态。进程处于阻塞状态时不占用CPU时间,资源被保留,直到等待的事件发生。一旦等待的事件完成,进程会从阻塞态转移到就绪态,等待操作系统再次将其调度到运行态。
终止(Terminated): 进程已经完成了其执行任务,或者由于某种原因(如错误、用户请求)被终止。在这个状态下,进程的资源会被释放,包括释放占用的内存、文件描述符等。
状态之间的转移如下:
就绪 -> 运行: 当操作系统决定将CPU分配给一个就绪的进程时,这个进程从就绪态转移到运行态。
运行 -> 就绪: 当一个进程的时间片用完、发生了IO操作等情况时,它会从运行态转移到就绪态,等待下一次被操作系统调度。
运行 -> 阻塞: 当进程需要等待某个事件发生时,比如等待IO操作完成,它会从运行态转移到阻塞态。
阻塞 -> 就绪: 当进程等待的事件发生后,它会从阻塞态转移到就绪态,等待被操作系统调度。
用户态和内核态是什么?
用户态和内核态是操作系统中的两种运行模式。
用户态(User Mode)是指程序在普通用户权限下运行的状态。在用户态下,程序只能访问有限的资源和执行受限的操作,不能直接访问底层硬件或操作系统的关键部分。这是为了保证系统的稳定性和安全性。
内核态(Kernel Mode)是指操作系统内核运行的状态。在内核态下,操作系统具有最高的权限,可以访问所有资源和执行所有操作,包括底层硬件和操作系统的关键部分。内核态用于处理操作系统的核心功能,如管理进程、分配资源、处理中断等。
用户态到内核态的转换发生在程序需要执行特权操作时,例如访问受限资源、执行系统调用或处理中断。当程序需要执行这些操作时,它必须通过系统调用或中断请求进入内核态,操作系统会检查权限并执行相应的操作。完成后,程序再次返回用户态继续执行。
用户态到内核态再返回用户态的过程,可以看作是用户程序与操作系统之间的交互过程,通过这种方式实现了对系统资源的安全访问和管理。示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
用户态和内核态的概念可以与Linux操作系统中的root用户和普通用户对应起来。
进程在用户态和内核态之间是如何切换的
用户态 -> 内核态:
当前运行的进程需要执行一个只有内核态才能执行的操作,比如系统调用(如读写文件、创建进程等)、中断处理或异常处理。
进程触发一个系统调用或发生中断,导致进程从用户态切换到内核态。
这时,操作系统会保存当前进程的上下文(包括寄存器值、程序计数器等),并根据中断或系统调用的类型进行相应的处理。
内核态执行:
进入内核态后,操作系统会根据中断或系统调用的类型进行相应的处理,可能包括设备IO操作、资源分配、调度等。
执行完必要的操作后,操作系统会根据调度算法选择下一个要执行的进程。
内核态 -> 用户态:
操作系统从选中的下一个进程的上下文中恢复相关信息,包括寄存器值、程序计数器等。
恢复完毕后,操作系统将控制权转移到该进程,使其从内核态切换回用户态。
这时,进程可以继续执行其用户态下的指令。
需要注意的是,不是所有的进程切换都需要经过用户态到内核态的切换,因为有些操作可以在用户态下完成。但是,对于需要访问受限资源或执行特权操作的情况,进程切换通常会涉及用户态到内核态的切换。
讲一下进程之间的通信方式
AI:
进程之间的通信是操作系统中的重要概念,它使不同进程能够在同一系统中进行信息交换和合作。进程通信的主要方式包括共享通信、消息传递和管道通信。
1)共享通信(Shared Memory Communication): 共享通信是一种通过在多个进程之间共享一块内存区域来实现通信的方式。多个进程可以访问相同的内存区域,从而实现数据的共享。这种通信方式效率高,适用于进程之间需要频繁交换大量数据的情况。然而,由于多个进程同时访问共享内存可能引发同步和互斥问题,因此需要使用同步机制(如信号量、互斥锁)来确保数据的一致性和安全性。
2)消息传递(Message Passing): 消息传递是一种通过发送和接收消息来实现进程通信的方式。每个进程都有自己的内存空间,进程之间通过操作系统提供的通信机制发送和接收消息。消息传递可以是同步的(发送进程等待接收进程确认后才继续执行)或异步的(发送进程发送消息后即可继续执行)。消息传递适用于进程之间需要明确通信时机,或需要跨越不同物理计算机的分布式系统。
3)管道通信(Pipes): 管道通信是一种通过创建一个单向的、临时的数据通道来实现进程间通信的方式。一个进程可以将数据写入管道,而另一个进程可以从管道中读取这些数据。管道通信适用于具有父子进程关系的进程,通常用于在父子进程之间传递数据。管道可以是匿名管道(在进程创建时自动生成)或命名管道(基于文件系统的命名通道)。
这些通信方式在不同场景下有不同的优缺点,操作系统需要根据具体的需求选择适当的通信方式。共享通信适用于需要高效数据交换的情况,消息传递适用于需要明确通信时机的情况,而管道通信适用于父子进程之间的简单数据传递。在实际应用中,常常会使用多种通信方式来满足不同进程间的通信需求。
讲一下进程调度的三个层次
进程调度通常可以分为三个不同的层次,以便更好地管理和优化系统中的进程执行顺序。这三个层次分别是:
高级调度(Long-term Scheduling):
高级调度也被称为作业调度。在这个层次上,决定是否接受新的进程或作业进入系统执行。它关注的是系统资源的整体利用和作业的吞吐量。高级调度的目标是保持系统在负载均衡和资源利用方面的高效性。高级调度负责选择从外部提交的作业,并将其加载到内存中,为它们创建进程控制块(PCB),然后将其放入就绪队列中等待执行。
中级调度(Medium-term Scheduling):
中级调度也被称为内存调度。在这个层次上,决定哪些进程将保留在内存中,哪些进程将被置于休眠状态以释放内存资源。中级调度的目标是控制内存的使用,以确保系统的稳定性和性能。中级调度在进程间进行切换,将一些进程从内存中移出,以腾出足够的内存供其他进程使用。
低级调度(Short-term Scheduling):
低级调度也被称为进程调度。在这个层次上,决定哪个就绪队列中的进程将被分配给CPU执行。低级调度的目标是在进程之间实现公平的CPU时间共享,最大程度地减少等待时间和响应时间。这是操作系统中最频繁的调度活动,通常通过时间片轮转、优先级、多级反馈队列等算法来实现。
这三个层次的进程调度在不同层面上管理和控制进程的执行,以确保系统的高效性、稳定性和公平性。

介绍一下七转态模型

什么时候能进程调度 什么时候不能?

讲一下常见的进程调度算法
1)FCFS (First-Come, First-Served) 先来先服务:
这是最简单的调度算法之一,按照进程到达的顺序进行调度。先到达的进程将首先被执行,直到它完成或者阻塞。这可能导致长作业的等待时间较长,即使有一些短作业在队列中等待。
2)SJF (Shortest Job First) 短作业优先:
这个算法选择下一个最短执行时间的进程来执行。这可以最小化平均等待时间,但需要提前知道每个进程的执行时间,这在实际中可能并不总是可行。
3)HRRN (Highest Response Ratio Next) 高响应比优先:
HRRN算法考虑了等待时间和服务时间的比值,选择具有最高响应比的进程来执行。响应比可以通过以下公式计算:响应比 = (等待时间 + 服务时间) / 服务时间。这个算法有助于避免长作业等待太久。
4)时间片轮转调度算法:
这是一种基于时间分片的调度算法,每个进程被分配一个小的时间片来执行,然后轮流切换到下一个进程。这可以使多个进程在一段时间内平等地分享CPU时间,但可能导致上下文切换开销增加。
5)优先级调度算法:
这种算法为每个进程分配一个优先级,优先级较高的进程将被优先执行。这可以根据进程的重要性或紧急性来分配CPU时间。然而,可能会导致低优先级进程永远无法执行(优先级反转问题)。
6)多级反馈队列调度算法:
这是一种综合性的调度算法,将就绪队列划分为多个优先级队列,每个队列具有不同的时间片大小。新到达的进程首先进入最高优先级队列,如果它在时间片内没有完成,它将降低到较低的优先级队列。这可以在考虑长短作业的同时,也允许长作业最终被执行。
讲一下进程调度算法的评价指标
进程调度算法的评价指标包括以下几个方面:
CPU利用率(CPU Utilization): 表示CPU在一段时间内的有效工作时间占总时间的比例。高CPU利用率意味着CPU得到了充分利用,系统资源得到了有效利用。
系统吞吐量(System Throughput): 表示在单位时间内完成的进程数量。高吞吐量意味着系统能够处理更多的进程,系统的生产率较高。
周转时间(Turnaround Time): 表示从进程提交到执行完成所经历的时间总量。它包括等待时间和执行时间。较短的周转时间表示系统能够快速响应作业并将其完成。
等待时间(Waiting Time): 表示进程在就绪队列中等待执行的时间总量。减少等待时间有助于避免饥饿,提高作业的执行效率。
响应时间(Response Time): 表示从提交进程到系统首次产生响应的时间。对于交互式系统而言,较低的响应时间是至关重要的,可以提供更好的用户体验。
不同的调度算法在这些评价指标上表现可能会有所不同。例如,时间片轮转算法可能会在响应时间和饥饿性方面表现良好,但在周转时间和等待时间上可能不如其他算法。而优先级调度算法可以根据不同进程的重要性来灵活调度,但可能导致低优先级进程饥饿。
综合考虑这些评价指标,选择适合特定系统需求的调度算法是重要的,因为不同的应用场景可能会对不同的指标有不同的重视程度。
CPU利用率(CPU Utilization):
公式: CPU利用率 = (CPU执行时间 / 总时间) * 100%
例子: 假设CPU执行了60秒,而总时间是120秒,则 CPU利用率 = (60 / 120) * 100% = 50%
系统吞吐量(System Throughput):
公式: 系统吞吐量 = 完成的作业数 / 总时间
例子: 在一段时间内,系统完成了30个作业,总时间是300秒,则系统吞吐量 = 30 / 300 = 0.1 作业/秒
周转时间(Turnaround Time):
公式: 周转时间 = 完成时间 - 到达时间
例子: 一个作业到达时间是T0,完成时间是T5,则周转时间 = T5 - T0
等待时间(Waiting Time):
公式: 等待时间 = 周转时间 - 执行时间
例子: 一个作业的周转时间是10秒,而它实际执行了8秒,则等待时间 = 10 - 8 = 2秒
响应时间(Response Time):
公式: 响应时间 = 开始执行时间 - 到达时间
例子: 一个作业到达时间是T0,开始执行时间是T2,则响应时间 = T2 - T0
各个进程调度算法优缺点
这里我将详细解释每种进程调度算法在抢占性、饥饿性以及优缺点方面的区别:
FCFS (First-Come, First-Served) 先来先服务:
抢占性:非抢占式,即一旦进程开始执行,直到完成或者阻塞才会释放CPU。
饥饿性:不会
优缺点:简单易实现,没有优先级反转问题,但可能导致短作业饥饿,不适合多样的作业。
SJF (Shortest Job First) 短作业优先:
抢占性:可以是抢占式或非抢占式,取决于实现。
饥饿性:可能导致长作业饥饿,因为短作业总是被优先执行。
优缺点:能够最小化平均等待时间,但需要准确的作业执行时间,可能导致长作业饥饿。
HRRN (Highest Response Ratio Next) 高响应比优先:
抢占性:非抢占式
饥饿性:能够避免长作业饥饿,但可能导致新到达的短作业等待时间较长。
优缺点:考虑了等待时间和服务时间,有助于避免长作业优先问题。
时间片轮转调度算法:
抢占性:抢占式,每个进程在一个时间片内执行,然后切换到下一个进程。
饥饿性:能够确保所有就绪进程都能获得一定的CPU时间,避免饥饿。
优缺点:公平地分配CPU时间,但可能引入上下文切换开销,不适用于长时间的计算密集型任务。
优先级调度算法:
抢占性:可以是抢占式或非抢占式,取决于实现。
饥饿性:可能导致低优先级进程饥饿,因为高优先级进程会一直占用CPU。
优缺点:能够根据作业的重要性进行调度,但可能导致低优先级进程饥饿或优先级反转问题。
多级反馈队列调度算法:
抢占性:通常是抢占式的,因为进程可以根据优先级降低或提高。
饥饿性:会
优缺点:适用于多样的作业,能够平衡长短作业的执行,但需要调整合适的队列参数。
王道考研截图:


进程的饥饿性是什么意思
进程的"饥饿性"指的是某个进程长时间无法获得所需的资源(通常是CPU时间),导致它无法执行或者执行时间极短,从而无法充分完成其任务。这种情况可能会影响系统的公平性和性能。
讲一下进程的同步和互斥
进程的同步和互斥是操作系统中重要的概念,用于控制多个进程之间的访问和操作,以确保数据的一致性和正确性。这些概念对于避免竞态条件(Race Condition)、死锁(Deadlock)等并发问题非常关键。
进程的同步(Process Synchronization): 进程的同步是指协调多个进程的执行顺序,使它们按照一定的规则或顺序来访问共享资源,以避免数据不一致或其他问题。同步机制确保在一个进程访问共享资源时,其他进程不会同时进行相同操作,从而保证数据的正确性。常用的进程同步机制包括信号量(Semaphore)、互斥锁(Mutex)、条件变量(Condition Variable)等。
互斥(Mutual Exclusion): 互斥是一种同步机制,它确保同一时刻只有一个进程可以访问某个共享资源。互斥是为了避免多个进程同时修改相同的数据而引发的问题,比如竞态条件。互斥锁是最常用的实现互斥的工具,一个进程在访问共享资源之前会尝试获取互斥锁,如果获取成功则可以访问资源,如果获取失败则需要等待。
什么是临界区
临界区(Critical Section)是指在多进程或多线程程序中,一段代码或一段代码区域,在任意时刻只允许一个进程(或线程)访问和执行。临界区的目的是确保共享资源在任何时刻都只被一个进程访问,以避免竞态条件(Race Condition)等并发问题。
在并发程序中,多个进程或线程可能同时访问共享资源,如果没有合适的同步机制来保护这些共享资源,就会产生不确定的结果。临界区的引入可以解决这个问题,确保一次只有一个进程可以进入临界区,从而避免数据的不一致性和错误。
通常,为了实现临界区的保护,需要使用同步机制,如互斥锁(Mutex)、信号量(Semaphore)、条件变量(Condition Variable)等。当一个进程进入临界区时,会尝试获取互斥锁或信号量等同步对象,如果成功获取,就可以执行临界区的代码;而其他进程如果要访问相同的临界区,必须等待前一个进程释放同步对象。
介绍一下进程同步与互斥经典问题(生产者-消费者问题等)
这些经典问题是在并发编程中用来展示进程同步和互斥概念的典型案例。它们帮助人们理解在多个进程或线程之间共享资源时可能出现的问题,并展示如何使用同步机制来解决这些问题。
1)生产者-消费者问题: 在生产者-消费者问题中,有一块有限的缓冲区,生产者进程可以往缓冲区中放入数据,而消费者进程可以从缓冲区中取出数据。问题在于,缓冲区满时生产者应该等待,缓冲区空时消费者应该等待,以避免数据的竞争和错误。
2)多生产者-多消费者问题: 这个问题是生产者-消费者问题的扩展,其中有多个生产者和多个消费者共享同一个缓冲区。需要确保生产者和消费者之间的协调,避免数据丢失或竞争问题。
3)吸烟者问题: 这个问题涉及到三个吸烟者和一个供应者,每个吸烟者需要三种资源中的两种才能吸烟。供应者持有所有的三种资源,并随机提供两种给等待的吸烟者。问题在于,要确保每个吸烟者都能正确获取所需资源才能吸烟。
4)读者-写者问题: 在读者-写者问题中,有多个读者和一个写者可以访问共享数据。读者可以同时访问数据,但写者必须独占地访问,且在写者访问时不允许读者访问。问题在于,需要平衡读者和写者之间的访问,防止写者被长时间阻塞。
5)哲学家进餐问题: 这个问题描述了五位哲学家围坐在一张圆桌旁,每个人面前有一碗饭和一只叉子。哲学家思考和进餐,但进餐需要用两只叉子。问题在于,如何安排哲学家的进餐,以避免死锁和资源竞争。
这些问题都涉及到资源共享和进程间的协调,要求合适的同步机制来解决并发问题,如使用互斥锁、信号量、条件变量等。通过解决这些经典问题,人们可以更好地理解多进程或多线程编程中的挑战,并学习如何应用不同的同步和互斥技术来保障程序的正确性和稳定性。
什么是死锁 如何解决
死锁是指在多进程或多线程系统中,每个进程(或线程)都在等待一个事件(如获取一个资源或释放一个资源),导致所有进程(或线程)都无法继续执行的一种状态。简而言之,死锁就是多个进程或线程互相等待,无法继续向前推进,从而导致系统陷入僵局。
死锁发生的条件通常包括以下四个:
互斥条件(Mutual Exclusion): 至少有一个资源在任何时刻只能被一个进程(线程)占用。
占有并等待(Hold and Wait): 至少有一个进程(线程)持有一个资源并在等待获取其他资源。
没有抢占(No Preemption): 资源只能由持有者主动释放,其他进程(线程)不能强制夺取资源。
循环等待(Circular Wait): 进程(线程)之间形成一个循环等待资源的关系。

**比如读写锁(Read-Write Lock)是一种并发编程中的同步机制,用于解决读者-写者问题中可能出现的死锁情况。**网络编程中常用
进程同步这里问题较少,是因为在网络编程中已经出现过多次了,这里不再重复写了
逻辑地址和物理地址的区别
逻辑地址和物理地址是计算机体系结构中的两个重要概念,它们用于处理内存管理和地址转换。
逻辑地址(Logical Address):
逻辑地址也称为虚拟地址(Virtual Address),是由CPU生成的地址。在程序执行过程中,CPU使用逻辑地址来访问内存中的指令和数据。逻辑地址是相对于进程的虚拟地址空间而言的,这个地址空间是每个进程独立拥有的,它可以超过实际物理内存的大小。通过使用逻辑地址,操作系统可以为每个进程提供一个独立的虚拟地址空间,从而实现了进程的隔离和保护。
物理地址(Physical Address):
物理地址是实际存在于计算机内存中的地址。计算机的内存由一系列的存储单元组成,每个存储单元都有一个唯一的物理地址。当CPU发出一个逻辑地址时,操作系统的内存管理单元(MMU)负责将逻辑地址转换为相应的物理地址,从而找到实际存储数据的位置。
区别:
范围:逻辑地址是相对于进程的虚拟地址空间,范围是进程独立的,可以超过实际物理内存大小;而物理地址是实际存在于内存中的地址,范围是计算机内存的实际大小。
映射:逻辑地址和物理地址之间的映射是通过操作系统的内存管理单元MMU 进行的。MMU负责将逻辑地址转换为对应的物理地址,从而实现虚拟内存管理。
访问:CPU使用逻辑地址来访问内存中的指令和数据,而实际的访问操作是在物理地址上进行的。
逻辑地址和物理地址的使用使得计算机系统可以在更高效、灵活和安全的方式下管理内存,同时使得每个进程可以拥有独立的地址空间,互不干扰。这对于多道程序设计和操作系统的内存管理至关重要。
对于x=x+1指令 这条指令在计算机如何执行的
1)读取:首先,CPU会从内存中读取变量"x"的当前值。这个值将被加载到CPU的寄存器中,以便进行计算和操作。
2)计算:在CPU的寄存器中,将当前"x"的值加1,得到新的结果。
3)存储:最后,将计算得到的新值存储回内存中的变量"x"的地址,以更新"x"的值。
讲一下程序放到内存运行的全过程 (编译,链接,载入)
程序从源代码到最终在内存中运行的全过程可以分为编译、链接和载入三个主要阶段。以下是这些阶段的详细解释:
1)编译阶段:
编译是将源代码(比如C/C++、Java等)翻译成机器代码的过程。编译器会将源代码转换成中间代码或汇编代码,这些代码是面向特定平台的低级代码。编译阶段主要的目标是进行语法检查、语义分析和生成可执行代码的中间结果。
2)链接阶段:
链接是将编译阶段产生的多个目标文件(object files)以及可能需要的库文件合并成一个可执行文件的过程。在编译阶段,如果程序调用了其他函数或使用了外部库,编译器并不知道这些函数和库的具体实现位置。链接器的任务是解决这些未知的引用,将函数调用关联到正确的函数实现,使得程序能够正确地执行。链接还包括对代码和数据的地址重定位,以确保最终生成的可执行文件能够正确地加载和运行。
3)载入阶段:
载入是将可执行文件加载到内存中,让操作系统能够执行它的过程。当用户运行一个程序时,操作系统负责将可执行文件从硬盘或其他存储介质中加载到内存中的适当位置。操作系统会解析可执行文件的文件头,确定代码段、数据段、堆栈等部分的大小和位置,然后为这些段分配合适的内存空间。最终,程序的代码和数据被加载到内存,并开始执行。

指令的逻辑地址如何转为物理地址(指令装入内存的三种方式)
当程序被加载到内存中执行时,涉及到一些地址的处理和调整,其中静态装入、静态重定位和动态重定位是与地址相关的概念。
静态装入(Static Loading):静态装入是指在程序被加载到内存之前,所有的地址都是在编译时确定的。在静态装入的情况下,程序的地址空间布局是固定的,不会发生变化。这意味着程序在每次执行时都会被加载到相同的内存地址,并且不需要进行地址的调整。
静态重定位(Static Relocation):静态重定位是指在程序被加载到内存之前,程序中的绝对地址会根据加载地址进行调整。当程序中存在绝对地址时,需要通过静态重定位来调整这些地址,使得程序能够正确地访问内存中的数据。静态重定位一般是通过修改可执行文件的地址相关字段来实现的。
动态重定位(Dynamic Relocation):动态重定位是指在程序执行过程中,根据需要对程序的地址进行调整。与静态重定位不同,动态重定位是在程序运行时根据实际的内存布局和地址空间的情况进行调整。动态重定位通常由操作系统的内存管理单元(MMU)或链接器(Linker)来完成。
总结来说,静态装入是指在程序加载到内存之前,所有地址都是在编译时确定的;静态重定位是在程序加载到内存之前,对程序中的绝对地址进行调整;而动态重定位是在程序执行过程中,根据实际的内存布局和地址空间情况进行调整。
讲一下链接的三种方式
当涉及到链接和库的使用时,静态链接、装入时动态链接和运行时动态链接是三种常见的链接方式。
1)静态链接(Static Linking):
静态链接是将所有编译后的模块和库函数的代码复制到一个单独的可执行文件中。在编译时,编译器将程序代码与所需的库函数代码合并成一个单一的可执行文件。这意味着最终生成的可执行文件包含了所有依赖的代码,可以独立运行,不需要依赖外部的库文件。优点是程序的执行速度较快,因为所有代码都是直接嵌入在可执行文件中。但缺点是可执行文件的大小可能较大,并且多个程序如果使用相同的库函数,会导致代码的冗余。
2)装入时动态链接(Load-time Dynamic Linking):
装入时动态链接是一种介于静态链接和运行时动态链接之间的方式。在装入时动态链接中,编译后的模块和库函数的引用保存在可执行文件中,但在程序加载到内存时,操作系统会进行动态链接,将外部库函数的代码合并到可执行文件中。这样,程序在运行之前就获得了所有的代码依赖,不再依赖外部库文件。装入时动态链接的优点是减少了代码冗余,并且不依赖外部库文件,但仍然可以在运行时共享库函数。
3)运行时动态链接(Run-time Dynamic Linking):
运行时动态链接是在程序执行过程中,根据需要对库函数进行动态链接。在编译时,可执行文件只包含对库函数的引用,而不包含库函数的实际代码。在程序运行时,当遇到对库函数的调用时,操作系统会加载所需的库文件,并将库函数的代码链接到程序中,使程序能够正确地调用库函数。这样的设计允许多个程序共享同一个库的实例,减少了代码冗余和内存的使用。优点是可执行文件较小,多个程序可以共享库函数,但缺点是程序依赖于外部的库文件,如果库文件缺失或版本不匹配,会导致程序运行失败。
总结来说,静态链接在编译时将所有代码合并成一个可执行文件,装入时动态链接在程序加载到内存时进行动态链接,而运行时动态链接在程序执行过程中根据需要进行动态链接。不同的链接方式在可执行文件的大小、内存占用、运行速度和依赖性等方面有所差异,选择合适的链接方式取决于具体的应用场景和需求。
操作系统对于内存管理的主要工作是哪些?
1)内存分配与回收:操作系统负责将可用的物理内存分配给进程和程序,并在进程不再需要内存时回收释放这些内存资源。
2)虚拟内存(内存扩充):操作系统通过虚拟内存管理,将进程的虚拟地址转换为物理地址,并在需要时将数据从磁盘交换到内存中,以扩展可用的地址空间。
3)地址转换:在使用虚拟内存时,操作系统负责将进程的虚拟地址(逻辑地址)转换为实际的物理地址,这样进程可以访问正确的内存位置。
4)内存保护:操作系统确保不同的进程不能访问彼此的内存空间,以防止恶意程序或错误的操作破坏其他程序的内存。为了实现内存保护,操作系统会将每个进程的内存空间隔离,并为每个进程分配独立的地址空间。
内存保护的两种方式
关于"上下限寄存器"和"重定位寄存器",它们主要在一些早期的计算机系统中使用,具体作用如下:
1)在CPU中设置一对上、下限寄存器,存放进程的上、下限地址。进程的指令要访问某个地址时,CPU检查是否越界。
2)采用重定位寄存器(又称基址寄存器)和界地址寄存器(又称限长寄存器)进行越界检查。重定位寄存器中存放的是进程的起始物理地址。界地址寄存器中存放的是进程的最大逻辑地址。

总之,在现代操作系统中,更常见的内存保护方法是使用分页机制、访问权限位等来确保进程之间的内存隔离和安全性。
讲一下内存扩容中的覆盖技术和交换技术
覆盖技术和交换技术是早期计算机系统中常用的两种解决方案,用于处理程序过大而无法一次性加载到内存中的问题。
1)覆盖技术:
覆盖技术是一种将程序划分为多个独立的部分(称为覆盖段),每次只加载需要执行的部分到内存中的方法。在程序执行期间,当需要切换到另一个覆盖段时,当前段会被从内存中移出,新的覆盖段会被加载进来。这样,虽然整个程序可能很大,但是每次只需要加载一小部分到内存,可以充分利用有限的内存资源。
覆盖技术的缺点是需要手动对程序进行分割,将其划分成逻辑上独立的片段,而且在切换覆盖段时,需要处理数据的传输和状态保存与恢复,因而可能导致较大的开销。这种技术主要在早期内存较小的计算机系统中使用。
2)交换技术:
交换技术是一种将整个进程从内存中移出并暂存到辅助存储设备(例如硬盘)上,以释放内存空间给其他进程使用的方法。当再次需要执行该进程时,可以将其重新调入内存并继续执行。
(交换技术涉及到进程调度。当一个进程被暂时交换到辅助存储设备(如硬盘)上时,其对应的内存空间就会被释放出来,从而为其他进程提供了更多的内存空间。这样的操作需要由操作系统负责调度和管理。)
交换技术相较于覆盖技术,更为灵活,因为整个进程被移出内存,不需要对程序进行手动分割。但是,交换过程涉及到数据的大规模传输,因而可能导致较长的读写时间,影响了程序执行的速度。
随着计算机硬件技术的发展,内存容量不断扩大,覆盖技术和交换技术逐渐被更高级的内存管理技术(如虚拟内存和分页机制)所取代,使得程序的加载和执行更加高效和方便。现代操作系统可以动态地将进程的部分数据存放在内存中,而将其余部分存储在磁盘上,实现了更灵活的内存管理和优化程序执行。
介绍一下磁盘空间的文件区和对换区
磁盘空间在计算机系统中通常被划分为文件区和交换区(也称为交换文件或交换分区),它们在内存管理和进程调度中起着不同的作用。
文件区(File Area):
文件区是磁盘空间的一部分,用于存储用户创建的文件和应用程序数据。这些文件可以是文本文件、图像、音频、视频等。当用户保存文件或应用程序时,它们会被存储在文件区中,并保留在磁盘上,即使计算机关机或重新启动,这些文件也不会丢失。文件区允许持久性存储,这意味着数据将一直保存在磁盘上,直到用户主动删除它们。
交换区(Swap Area):
交换区是用于实现虚拟内存和支持交换技术的一部分磁盘空间。在计算机的内存(RAM)不足时,操作系统可以将不常用的进程或进程的一部分数据暂时交换到交换区,以释放内存空间给其他更需要执行的进程。这样,虽然进程在内存中被暂时置换出去,但是在进程需要执行时,操作系统可以将其重新调入内存,并继续执行。(对换区IO速度快)
交换区的大小通常是预先设置的,并且与计算机的RAM大小有关。较大的交换区可以支持更多的进程并减少频繁的内存交换操作,但也会占用更多的磁盘空间。较小的交换区可能导致频繁的进程交换,影响系统性能。
介绍一下内存连续分配的三种方式(单一连续分配 固定分区分配 动态分区分配)
内存连续分配是指将计算机的物理内存划分成若干块,并按照一定规则将进程加载到这些内存块中。下面介绍三种常见的内存连续分配方式:
1)单一连续分配(Monoprogramming):
单一连续分配是最简单的内存管理方式。内存分为系统区和用户区,在这种模式下,整个计算机系统中只有一个程序在内存中运行,这个程序占用全部可用的内存空间。当该程序运行完成或终止时,其他程序才能被加载并执行。这种方式在早期计算机系统中很常见,但显然效率较低,因为系统资源得不到充分利用。
2)固定分区分配(Fixed Partitioning):
固定分区分配将内存划分为几个固定大小的区域,每个区域称为一个分区。每个分区可以容纳一个进程,当进程需要执行时,它被加载到一个空闲的分区中,并在执行期间不会移动。这种方式适用于多道程序设计,即在内存中同时运行多个进程,每个进程独占一个分区。然而,固定分区分配存在内存浪费的问题,因为如果一个进程的内存需求小于分区大小,该分区的剩余部分将无法被其他进程使用。(分区大小可能不同)
这种分配说明需要依靠分区说明表(一种数据结构)

3)动态分区分配(Dynamic Partitioning):
动态分区分配是一种更为灵活的内存管理方式。在这种模式下,内存不会预先划分内存分区。当一个进程需要执行时,系统会在合适的空闲分区中为其分配足够的内存,并且内存分区可以根据进程的大小进行动态调整。当一个进程终止时,它占用的分区将被释放出来,成为新进程的可用内存。
内存连续分配的三种方式的优缺点
当比较这三种内存连续分配方式时,可以考虑它们的优点和缺点,具体如下:
1)单一连续分配:
内存分为系统区和用户区
优点:
简单易实现,适用于早期计算机系统。
不涉及内存分区和调度,避免了内存碎片问题。(这里说的是避免了外部碎片,内部碎片还是存在的,因为分配给进程的内存肯定有些没用完)
缺点:
效率低下,系统资源得不到充分利用,一次只能运行一个进程。
无法支持多道程序设计,无法在内存中同时运行多个进程。
(因为内存中只有一个程序)
2)固定分区分配:
优点:
易于实现,比单一连续分配能够更好地支持多道程序设计。
可以避免内存碎片问题,因为每个分区固定大小,不会产生碎片。
缺点:
内存利用效率低,因为每个分区的大小固定,如果一个进程的大小小于分区,分区中的剩余空间将浪费无法被其他进程使用。
不适用于进程大小变化较大的情况,可能导致内存浪费或无法容纳大型进程。(依然是避免了外部碎片,内部碎片严重)
3)动态分区分配:
优点:
更灵活高效地利用内存资源,适用于不同大小的进程。
支持多道程序设计,能在内存中同时运行多个进程。
可以动态调整分区大小,避免了固定分区分配的内存浪费问题。
缺点:
可能导致内存碎片,随着进程的创建和销毁,内存中可能会产生一些碎片,使得某些进程无法装入内存中。(无内部碎片 都是外部碎片)
动态分配和调整内存分区需要更复杂的管理算法,可能会增加系统开销。
综上所述,单一连续分配方式过于简单且效率低下,固定分区分配方式虽然简单但存在内存浪费问题,动态分区分配方式更灵活高效,但需要更复杂的内存管理算法来处理内存碎片。在实际应用中,通常使用动态分区分配方式,根据具体场景和需求选用合适的内存管理算法。
介绍一下动态分区分配的四种算法(首次适应算法、最佳适应算法、最坏适应算法、临近适应算法)
动态分区分配是一种灵活的内存管理方式,它可以根据进程的大小动态分配内存,并根据不同的算法选择合适的分区来放置进程。下面介绍四种常见的动态分区分配算法及其优缺点:
1)首次适应算法(First Fit):
首次适应算法从内存的起始位置开始搜索,找到第一个能够满足进程大小的空闲分区,并将进程放入该分区中。这是最简单的分配算法之一。
优点:
简单快速,适用于实时应用或对响应时间要求较高的场景。
能够快速找到满足进程大小的空闲分区,尽可能减少内存碎片。
缺点:
可能产生大量小的内存碎片,导致部分空间无法利用。
容易导致内存的碎片化,影响内存利用率。
2)最佳适应算法(Best Fit):
最佳适应算法从所有空闲分区中选择最小且能够容纳进程的分区,以保留尽可能小的碎片。
优点:
能够最小化内存碎片,使得内存利用率相对较高。
在长期运行中,有可能表现出最好的性能。
缺点:
搜索最佳空闲分区可能耗费较长时间,影响分配效率。
可能会导致较大的剩余空闲分区,无法容纳较大的进程。
3)最坏适应算法(Worst Fit):
最坏适应算法选择最大的空闲分区来存放进程,这样可以尽量减少剩余空闲分区的大小。
优点:
可以减少剩余空闲分区的大小,降低内存碎片。
缺点:
导致较大的内部碎片,可能导致一些进程无法装入内存中。
对于较小的进程,可能会找不到合适的分区。
4)临近适应算法(Next Fit):
临近适应算法类似于首次适应算法,但它在上一次分配的位置继续搜索下一个合适的空闲分区。这样可以减少搜索的范围,提高分配效率。
优点:
比首次适应算法略有改进,可以减少搜索范围,提高分配效率。
缺点:仍然可能产生大量小的内存碎片,影响内存利用率。
讲一下内部碎片和外部碎片
内部碎片和外部碎片是与内存分配和管理相关的两种碎片化现象。
内部碎片:
内部碎片是指已经分配给进程的分区中,有部分空间没有被利用,导致浪费。这种浪费是由于进程所需的内存大小与分配给它的分区大小不完全匹配造成的。例如,如果一个进程需要80KB的内存空间,而系统只能提供100KB的分区给它,那么有20KB的内存空间是未被利用的,这部分未被利用的空间即为内部碎片。
内部碎片主要出现在固定大小的分区管理中,例如,静态分区分配中,将内存划分为固定大小的块供进程使用,而进程可能需要的大小不一定刚好等于分区的大小。这种情况下,每个分区都可能会有一定程度的内部碎片。
外部碎片:
外部碎片是指在内存中有大量的零散空闲区域,这些零散的空闲区域加起来的总大小足够容纳一个进程,但由于这些空闲区域不连续或分布零散,因此无法被利用,导致无法满足某些进程的内存需求。
外部碎片主要出现在动态分区管理中,例如,在动态分区分配中,内存会被分成不同大小的可变分区,这样可以更加灵活地分配给不同大小的进程。然而,随着进程的加载和释放,可能会留下许多小的零散空闲区,这些空闲区合起来的大小足够满足某个进程的需求,但由于不连续的分布,无法被利用,造成了外部碎片。
内部碎片和外部碎片都会影响内存的有效利用率。解决碎片化问题是内存管理的重要目标之一,常见的解决方法包括紧缩内存、内存整理、分区合并、使用更高级的内存管理算法等。
介绍一下分页存储(大部分问题都在这段话中了我就不展开了)
分页存储是一种计算机内存管理技术,用于将进程的逻辑地址空间划分为固定大小的页(page),并将物理内存也划分为相同大小的块,称为页框(page frame)。通过这种方式,将进程的逻辑地址映射到物理地址,实现了虚拟内存的概念。
分页存储的工作原理如下:
分页:
将进程的逻辑地址空间划分为大小相等的页,页面大小通常为固定大小的2的幂次方。这些页的大小由操作系统定义,典型的大小为4KB或者4MB。进程的逻辑地址空间被划分成连续的页号,每个页号对应一个页。
分页表:
操作系统维护一个称为分页表的数据结构,用于记录每个进程的逻辑页号与其对应的物理页框号(块号) 的映射关系。分页表可以存储在内存中,也可以使用硬件的快表(Translation Lookaside Buffer, TLB)进行缓存加速。当进程访问一个逻辑地址时,硬件会使用分页表来查找逻辑页号与物理页框号的映射。

逻辑地址转换:
当进程访问一个逻辑地址时,操作系统将逻辑地址分为两部分:页号和页内偏移。页号用于查找分页表,找到对应的物理页框号,然后将页内偏移添加到物理页框号中,得到实际的物理地址。这样,进程的逻辑地址就被映射到了物理地址。
内存访问保护:
分页存储可以实现内存访问的保护机制。在分页表中,可以设置每个页的访问权限,例如只读、读写或者禁止访问。如果进程试图访问一个没有合适权限的页,硬件或操作系统会拦截该访问,防止进程非法地访问内存。
优点:
分页存储可以实现虚拟内存,允许进程的逻辑地址空间大于物理内存大小,从而提供更大的内存空间。
可以实现内存的共享和保护,不同进程可以共享同一物理页框,但通过分页表的权限设置可以实现进程间的隔离和保护。
缺点:
分页存储需要维护分页表,这会占用一定的内存空间。
分页存储会引入一定的性能开销,因为地址转换需要查询分页表或快表。
分页存储是现代操作系统中广泛使用的内存管理技术,它充分利用了虚拟内存的概念,提高了内存的利用率和进程的灵活性。
引入快表的地址转换有和不同 快表的作用是?
引入快表(Translation Lookaside Buffer,TLB)的地址转换与传统的基本地址转换机构有所不同,快表是一种高速缓存,用于加速逻辑地址到物理地址的转换过程。它是一种存储最近或者最常用的页表项的高速缓存,可以显著提高地址转换的速度。
快表的作用主要包括以下几点:
1)加速地址转换:传统的基本地址转换需要从内存中读取页表项,这可能会导致访问延迟。而快表存储了最常用的页表项,使得常见的地址转换可以在高速缓存中完成,从而大大加速了地址转换过程。
2)减少内存访问次数:由于快表存储了部分页表项,当一个页表项在快表中找到时,就不需要访问内存来获取对应的页表项,从而减少了对内存的访问次数,降低了总体的访问延迟。
3)提高内存管理效率:快表可以有效地减轻操作系统对页表维护的负担,特别是在多任务操作系统中,多个进程的地址空间切换时,快表可以减少操作系统对页表的频繁更新。
4)支持并发访问:多核处理器和多线程环境下,多个处理器核心或线程可以独立地访问快表,从而提供更好的并发性能。
需要注意的是,快表的大小是有限的,因此它只能缓存有限数量的页表项。如果一个地址在快表中找不到,仍然需要访问主内存中的实际页表。因此,快表的设计需要考虑如何选择要缓存的页表项,以及如何处理快表未命中的情况。

介绍一下单级页表的问题 为何引出二级页表?
单机页表在虚拟地址空间非常大的情况下会遇到一些问题:
1)巨大的页表尺寸:在一个单一的页表中,需要存储整个虚拟地址空间和对应的物理地址的映射关系。当虚拟地址空间非常大时,页表会变得巨大,需要占用大量的内存空间。这会导致内存浪费,尤其在物理内存有限的情况下。
2)地址转换效率低下:由于页表很大,地址转换需要遍历整个页表才能找到对应的物理地址。这会导致地址转换的效率低下,增加内存访问的延迟。
为了解决这些问题,引入二级页表(或者更高级别的页表)可以提供更好的解决方案:
1)减小页表尺寸:二级页表将大的单一页表拆分成多个较小的页表,通过两级索引可以更有效地管理虚拟地址空间。这样就减小了单一页表的尺寸,节省了内存空间。
2)更高效的地址转换:由于二级页表采用两级索引来进行地址转换,访问页表的时间复杂度降低为O(log n)级别,相对于单级页表的O(n)级别更高效。快速地址转换可以加快内存访问速度,提高系统性能。
3)更好的扩展性:引入二级页表可以支持更大的虚拟地址空间,因为可以根据需要进一步增加层级来管理更大的地址空间。这种灵活性使得系统可以适应不同大小的虚拟地址空间需求。
总的来说,引入二级页表的目的是为了提高内存管理的效率和灵活性,减少内存浪费,支持更大的虚拟地址空间,并优化地址转换过程,从而提高系统性能。

介绍一下基本分段存储管理
基本分段存储管理是一种早期的内存管理技术,用于将程序和数据分成逻辑上独立的段,并将它们存储在内存的不同位置。每个段都有固定的大小,并且每个段都可以根据需要进行独立的扩展或缩小。每个段都有一个唯一的段标识符,以便于访问和管理。
基本分段存储管理的关键特点如下:
1)逻辑分离:程序和数据被划分为逻辑上独立的段,这样不同的功能模块可以被分别处理和管理,从而使得程序更容易维护和理解。
2)固定大小的段:每个段都有固定的大小,这样在段的扩展或缩小时,可以很容易地分配或回收内存空间,而无需整体移动其他段。
3)段表:为了实现分段存储管理,操作系统维护了一个段表(Segment Table),其中包含了每个段的信息,例如段的起始地址、段的长度等。段表中的每个表项对应一个段,通过段标识符来索引。
4)地址转换:在程序执行期间,CPU生成的虚拟地址由段标识符和段内偏移量组成。为了将虚拟地址转换为物理地址,CPU使用段标识符作为索引来查找段表,找到对应段的起始地址,并加上段内偏移量,得到最终的物理地址。
然而,基本分段存储管理也存在一些问题:
1)外部碎片:由于每个段有固定的大小,可能会导致内存中出现外部碎片。当需要分配一个比较大的段时,可能无法找到足够连续的内存块来满足分配需求。
2)段的长度变化:当段的长度发生变化时,需要调整段表和段之间的关系,可能会引起一定的开销。
3)不支持动态加载:基本分段存储管理不支持动态加载和分页的功能,因此无法灵活地适应不断变化的程序内存需求。
基本分段存储管理在早期计算机系统中得到广泛应用,但随着计算机系统的发展和内存管理技术的进步,逐渐被更高级的分页和虚拟内存管理技术所取代。虚拟内存管理技术更加灵活和高效,可以更好地解决内存碎片、动态加载等问题,成为现代计算机系统中主要采用的内存管理方式。
分段和分页的区别
分段和分页是两种不同的内存管理技术,它们分别用于将程序和数据组织在内存中。它们有不同的特点和适用场景,下面是它们的区别:
1)基本单位:
分段:分段将程序和数据划分为逻辑上独立的段,每个段具有固定大小,并且每个段可以根据需要进行独立的扩展或缩小。
分页:分页将程序和数据划分为固定大小的页,通常是4KB或其他大小。内存也被划分成与页相同大小的块,称为物理页框。
2)地址结构:
分段:虚拟地址由段标识符和段内偏移量组成。CPU使用段标识符来查找段表,找到对应段的起始地址,并加上段内偏移量,得到最终的物理地址。
分页:虚拟地址由页号和页内偏移量组成。CPU使用页号来查找页表,找到对应页的物理页框号,并加上页内偏移量,得到最终的物理地址。
3)内存碎片:
分段:由于每个段有固定的大小,可能会导致内存中出现外部碎片,即内存空闲块不足以满足分段的内存需求。
分页:分页技术可以有效避免外部碎片的问题,因为页的大小是固定的,内存分配只需要考虑空闲物理页框的连续性即可。
4)扩展性:
分段:分段技术可以支持不同大小的段,可以根据需要灵活地调整段的大小,但是每个段的大小是固定的。
分页:分页技术对于不同的页大小支持相对固定,一般由硬件决定。如果需要更大或更小的页大小,需要重新设置硬件或使用其他技术。
总的来说,分段和分页是两种不同的内存管理方式,每种方式都有其优势和局限性。在实际应用中,通常会综合考虑程序的特点、内存需求、硬件支持等因素来选择合适的内存管理技术。在现代计算机系统中,虚拟内存管理技术通常结合了分段和分页的优点,形成了更高级别的内存管理方式。
介绍一下段页式存储管理
段页式存储管理是一种复合型的内存管理技术,将分段和分页两种方式结合在一起,以克服各自单独使用时的局限性。它在现代计算机系统中被广泛应用,旨在提供更加灵活和高效的内存管理方式。
在段页式存储管理中,程序和数据被划分为逻辑上独立的段,并且每个段内部再进行分页处理。每个段的大小可以根据需要灵活地进行调整,而每个段内的页大小保持固定。这样,段页式存储管理结合了分段和分页的优点,同时克服了它们各自的缺点。
关键特点和步骤如下:
1)段划分:程序和数据被划分为逻辑上独立的段,每个段对应一个功能模块或数据类型,例如代码段、数据段、栈段等。每个段有一个唯一的段标识符和固定的大小。
2)分页:每个段内部被分成固定大小的页,通常是4KB或其他大小,与分页技术一样。每个段的页表管理着该段内部的页,实现分段内的虚拟地址到物理地址的转换。
3)段表:操作系统维护一个段表(Segment Table),其中包含了每个段的信息,例如段的起始地址、段的长度以及指向对应页表的指针。段表中的每个表项对应一个段,通过段标识符来索引。
4)页表:每个段的页表用于实现段内虚拟地址到物理地址的转换,类似于分页技术中的页表。CPU根据段内页表和虚拟地址的页号找到对应的物理页框号,并加上页内偏移量,得到最终的物理地址。
5)地址转换:在程序执行期间,CPU生成的虚拟地址由段标识符、段内偏移量和页内偏移量组成。首先,通过段标识符在段表中找到对应段的起始地址,然后在段内页表中找到对应页的物理页框号,最后加上页内偏移量,得到最终的物理地址。
段页式存储管理兼具分段和分页的优点,可以更好地支持不同大小的程序段,同时有效地避免外部碎片和内部碎片的问题。它在现代计算机系统中被广泛应用,为复杂的内存管理需求提供了灵活和高效的解决方案。
介绍一下请求分页存储管理
分页管理方式是虚拟内存系统的一种实现方式,用于将进程的虚拟地址空间映射到物理内存中。主要的组成部分包括请求页表、缺页中断机构和地址变换机构:
请求页表(Demand Paging):
请求页表是一种将进程虚拟地址空间映射到物理内存的机制。它并不将整个进程的虚拟地址空间一次性映射到物理内存中,而是在需要时才加载相应的页面。当进程访问一个虚拟地址,而该地址对应的页面尚未在物理内存中时,发生缺页中断,操作系统会根据缺页中断处理程序将对应的页面加载到物理内存中,然后重新执行导致缺页中断的指令。
缺页中断机构(Page Fault Handler):
缺页中断是在请求页表中发生的事件。当进程访问一个虚拟地址,而对应的页面不在物理内存中时,会发生缺页中断。操作系统的缺页中断机构负责处理这些中断。缺页中断处理程序会将对应的页面从磁盘加载到物理内存,并更新页表项,然后将控制权交还给进程,使其能够继续执行。
地址变换机构(Address Translation Mechanism):
地址变换机构负责将进程的虚拟地址转换成物理地址。在请求页表中,虚拟地址由两部分组成:虚拟页号和页内偏移量。地址变换机构使用页表来查找虚拟页号对应的物理页框号,然后将页框号与页内偏移量组合成物理地址。该地址变换过程使得进程可以访问到其在物理内存中对应的页面。
总体来说,请求页表和缺页中断机构是实现虚拟内存的关键部分。通过请求页表机制,操作系统可以节省物理内存空间,仅在需要时加载进程所需的页面。当进程访问一个尚未加载的页面时,缺页中断机构会触发页面的加载。地址变换机构则负责将虚拟地址转换为物理地址,使得进程能够正常访问其对应的物理页面。这些机制相互配合,实现了高效的虚拟内存管理。
传统非连续分配(分页,分段,段页式)和虚拟内存分配(分页,分段,段页式)的区别

请求分页和基本分页的区别
在计算机系统中,有两种常见的分页技术:请求分页(Demand Paging)和基本分页(Basic Paging)。它们是关于内存管理的不同方法,下面是它们的区别:
1)定义:
请求分页:请求分页是一种虚拟内存管理技术,它允许操作系统将进程的一部分加载到内存中,而不是将整个进程都加载到内存。当进程需要访问不在内存中的页时,会触发一个缺页中断(Page Fault),操作系统根据需要将相应的页加载到内存中。
基本分页:基本分页是一种简单的内存管理技术,将进程划分为固定大小的页,并将这些页映射到内存中的物理页框,类似于单纯的分页技术。
2)内存加载:
请求分页:只有在进程需要访问特定的页时,才会将该页加载到内存中。这样可以节省内存空间,只将当前活动部分加载到内存中,提高内存利用率。
基本分页:在进程启动时,将整个进程的所有页都加载到内存中,无论是否实际使用。这可能导致内存浪费,因为未使用的页也会占用内存空间。
3)内存访问开销:
请求分页:由于只加载当前需要的页,可以降低内存访问的开销,提高程序执行效率。但在某些情况下,由于缺页中断的发生,可能会引入额外的开销。
基本分页:由于在进程启动时加载所有页,无需发生缺页中断,内存访问的开销相对较低,但可能会造成内存浪费。
4)页表管理:
请求分页:由于只加载当前需要的页,页表通常较小,并且在程序执行过程中可能动态地变化。
基本分页:由于加载所有页,页表较大且固定。页表管理相对简单,但可能需要更多的内存来存储页表。
适用场景:
请求分页:适用于多任务操作系统,可以在有限的内存资源下支持更多的进程,并且适合执行大型程序,因为只加载必要的部分。
基本分页:适用于较简单的系统,不需要动态内存管理的复杂性,或者在内存较为充裕的情况下。
介绍常见的页面置换算法
页面置换算法是在虚拟内存管理中用于决定哪些页应该被置换出物理内存,从而为新的虚拟页腾出空间。以下是常见的页面置换算法:
1)最佳置换算法(Optimal Replacement Algorithm):
最佳置换算法是一种理想化的算法,它根据未来的访问模式选择最长时间不会使用的页进行置换。该算法在理论上提供了最佳的性能,因为它能够最小化页面错误率。但实际上,由于需要事先知道未来的访问模式,因此最佳置换算法无法在实际系统中应用。
2)先进先出置换算法(First-In-First-Out, FIFO):
先进先出置换算法是最简单的页面置换算法。它按照页面进入物理内存的顺序来选择要置换的页面。最早进入内存的页面会被置换出去。该算法容易实现,但它存在"Belady异常",即当内存分配不足时,增加页面数并不一定能降低缺页率。
3)最近最久未使用置换算法(Least Recently Used, LRU):
最近最久未使用算法根据页的历史访问模式来选择要置换的页面。它将最近最久未使用的页面置换出物理内存。LRU算法需要维护一个访问历史列表,不断更新页面的使用情况。但由于需要实时更新访问历史,因此实现较为复杂。
4)普通时钟置换算法(Clock Algorithm):
普通时钟算法是一种简化版的时钟置换算法。它使用一个环形队列来存储页面,并使用一个"使用位"(通常是页表中的一个位)来记录页面是否被访问过。当发生页面置换时,算法会顺时针地查找下一个未被使用的页面,将其替换出去。
5)改造型时钟置换算法(Enhanced Clock Algorithm):
改造型时钟置换算法是对普通时钟算法的改进。它引入了一个额外的引用位(Reference Bit)和修改位(Dirty Bit)。当页面被访问时,引用位会被设置为1,而当页面被修改时,修改位会被设置为1。在置换页面时,改造型时钟算法优先选择引用位和修改位均为0的页面,如果没有这样的页面,则选择引用位为0且修改位为1的页面。
介绍一下 页面分配策略
页面分配策略是虚拟内存管理中的一种技术,用于确定哪些页面应该被加载到物理内存中,以及如何在物理内存中进行页面置换。以下是相关概念的介绍:
驻留集(Resident Set):
驻留集是指当前活跃进程在物理内存中驻留的页的集合。操作系统根据进程的需求和内存的可用情况,将一部分页面加载到物理内存中,这个加载的页面集合就是驻留集。驻留集的大小通常受到系统配置和进程的内存需求影响。
页面分配(Page Allocation):
页面分配是指操作系统将虚拟内存中的某些页面映射到物理内存的过程。当进程需要访问某个虚拟页时,操作系统会检查该页是否在物理内存中,如果没有则发生缺页中断,并将对应的页面加载到物理内存中。
置换策略(Page Replacement Policy):
置换策略用于决定在物理内存空间不足时,选择哪些页面被置换出去腾出空间来加载新的页面。常见的置换策略包括先进先出(FIFO)、最近最久未使用(LRU)、最佳置换算法等。不同的置换策略对系统的性能和页面错误率有不同的影响。
抖动现象(Thrashing):
抖动现象指的是系统频繁发生页面置换,导致大部分时间都在进行页面置换操作,而几乎没有时间用于执行实际的进程任务。这通常是由于系统的驻留集设置过小,无法满足进程的工作集需求,导致频繁发生缺页中断和页面置换。抖动现象会显著降低系统的性能。
工作集(Working Set):
工作集是指进程在一段时间内实际使用的页面集合。它反映了进程在运行过程中所需的物理内存空间大小。通过监测进程的工作集大小,可以为进程分配适当的物理内存,避免抖动现象。
页面分配策略的设计和选择是操作系统内存管理的重要组成部分。合理的页面分配和置换策略可以有效地提高系统性能,减少页面错误率,并防止抖动现象的发生。根据实际系统负载和特点,需要仔细选择适合的页面分配和置换策略,以优化内存管理。
IO设备分类

介绍一下IO设备控制器的主要功能
I/O设备控制器是计算机系统中的一种重要硬件组件,用于管理和控制与计算机系统交互的外部I/O设备(输入/输出设备)。它在数据传输、命令执行、错误处理等方面发挥着关键作用。以下是I/O设备控制器的主要功能:
接口与数据传输: I/O设备控制器负责将计算机系统与外部I/O设备之间的电气和信号接口进行适配,以确保它们可以正确地进行数据传输。控制器负责控制数据的流动、同步和时序,从而实现数据的有效传输。
命令解释和执行: 控制器接收来自CPU或内存的命令,这些命令指示了特定的I/O操作,例如读取数据、写入数据、复位设备等。控制器需要解释这些命令并相应地执行对应的操作,以与外部设备进行通信。
设备状态监控: I/O设备控制器需要不断地监控与外部设备的通信状态和设备状态。这可以包括设备是否准备好进行数据传输、是否发生错误或故障等情况。根据设备状态,控制器可以采取适当的措施,例如等待、中断CPU或执行错误处理。
数据缓冲和处理: 在数据传输过程中,I/O设备控制器可能需要在控制器内部设置缓冲区,用于存储待传输的数据。这有助于平衡CPU和I/O设备之间的速度差异,从而提高整体性能。有些控制器还可以执行简单的数据处理操作,如数据格式转换、校验和计算等。
中断处理: 当I/O设备准备好进行数据传输、发生错误或完成操作时,控制器可以向CPU发送中断信号,通知CPU需要处理特定的事件。中断处理使得CPU可以及时响应设备状态的变化,而不必不断地轮询设备。
错误处理: I/O设备控制器需要监测外部设备操作过程中可能出现的错误情况,例如数据传输错误、设备故障等。在发现错误时,控制器可能会触发适当的错误处理机制,如通知CPU、重新尝试操作或报告错误信息。
地址译码和寻址: 控制器可能需要执行地址译码,将来自CPU或内存的逻辑地址转换为实际的物理地址,以确定与哪个具体的外部设备进行通信。
总之,I/O设备控制器是计算机系统中连接和管理外部I/O设备的关键组件,通过执行上述功能,它实现了计算机与外部设备之间的数据交换和通信,确保了系统的正常运行和数据的高效传输。不同类型的外部设备可能需要不同类型的控制器来满足其特定的通信需求。
I/O设备有几种控制方式 介绍一下
I/O设备控制方式是指计算机与外部I/O设备(如硬盘、键盘、打印机等)之间进行数据交换和通信的方式。以下是几种常见的I/O设备控制方式:
程序直接控制方式(Programmed I/O): 在程序直接控制方式下,CPU通过执行特定的I/O指令来控制I/O设备的数据传输。这种方式是最基本的I/O控制方式,但它需要消耗大量的CPU时间来等待I/O操作完成,因为CPU必须不断地轮询设备状态以确定何时可以进行数据传输。(轮询)
中断驱动方式(Interrupt-Driven I/O): 中断驱动方式使得CPU能够继续执行其他任务,而不必持续轮询设备状态。当I/O设备准备好进行数据传输时,它会发送一个中断信号给CPU,CPU则会暂停当前任务,处理中断并执行I/O操作。这种方式可以有效减少CPU的浪费时间。
直接内存访问(Direct Memory Access,DMA): DMA是一种由专用硬件控制的I/O设备访问方式。在DMA方式下,CPU将I/O设备和主存之间的数据传输控制交给DMA控制器。DMA控制器可以在不经过CPU的情况下直接进行数据传输,从而减轻了CPU的负担,提高了数据传输速率。这对于大量数据的传输非常有用,例如磁盘到内存的数据传输。
通道控制(Channel I/O): 通道控制是一种更高级别的I/O设备控制方式,通常用于大型计算机系统。通道是一个独立的硬件单元,类似于一个独立的处理器,负责管理多个I/O设备的数据传输。通道可以独立于CPU工作,同时与多个I/O设备并行地进行数据传输,从而提高整体I/O性能。
这些不同的I/O设备控制方式在不同场景下具有各自的优势和应用。程序直接控制方式简单但效率较低,中断驱动方式可以提高CPU利用率,DMA可以加速大数据块的传输,而通道控制适用于高性能的大型计算机系统。在设计和实现I/O控制时,根据应用需求和系统特点选择合适的控制方式是非常重要的。
I/O软件层次结构

中间三层就是核心子系统部分
操作系统的核心子系统主要功能

操作系统的核心子系统包括假脱机技术、I/O调度、设备保护、设备分配与回收以及缓冲区管理。每个子系统都有其独特的功能,共同协调和管理计算机系统中的I/O设备和资源。以下是对这些子系统主要功能的介绍:
假脱机技术(Spooling): 假脱机技术是一种用于提高I/O设备利用率的技术。它允许多个作业或任务在内存中等待执行,而不必依赖于实际的I/O设备的速度。这通过将作业的输入输出数据存储在磁盘等存储设备中,然后由操作系统负责管理和调度数据的传输。这样,多个作业可以并行地等待I/O操作完成,从而提高系统的吞吐量。
I/O调度(I/O Scheduling): I/O调度是操作系统中的一个重要功能,用于决定哪个I/O请求应该在何时获得服务。因为不同的I/O请求具有不同的优先级和资源需求,I/O调度算法可以优化系统性能,减少等待时间,以及平衡不同设备之间的负载。
设备保护(Device Protection): 设备保护确保系统中的不同作业或用户不能未经授权地访问或干扰其他作业所使用的设备。操作系统通过实施访问控制策略和权限管理,确保每个作业或用户只能访问其被授权使用的设备,从而维护系统的安全性和稳定性。
设备分配与回收(Device Allocation and Deallocation): 设备分配是指操作系统管理和分配可用I/O设备给不同作业或用户的过程。操作系统需要记录哪些设备正在被使用,哪些设备是可用的。一旦作业完成或用户不再需要设备,设备回收就会发生,使得设备可以被重新分配给其他作业或用户。
以下是几种常见的I/O设备分配与回收策略的简要介绍:
Dedicated Device Allocation (DCT): 在DCT策略中,每个设备被专门分配给一个特定的作业,而且只有这个作业能够使用该设备。这种策略可以确保作业之间不会互相干扰,但可能导致设备资源利用率低下,特别是当某些设备被某些作业闲置时。
Contiguous Device Allocation (COCT): COCT策略中,相邻的设备被分配给一个作业,这可以减少设备之间的寻址开销。然而,这可能会导致某些设备被浪费,因为一个作业可能只使用了分配给它的部分设备。
Clustered Device Allocation (CHCT): CHCT策略将一组相关的设备分配给一个作业。这适用于那些需要同时使用多个相关设备的作业,例如磁盘读写操作。这样可以提高设备之间的协作效率。
Systematic Device Allocation (SDT): SDT策略是一种灵活的策略,根据需要将设备分配给作业。作业可以根据它们的要求和设备的可用性来申请和释放设备。这种策略可以更有效地利用设备资源,但也需要操作系统具备较强的资源管理能力。
缓冲区管理(Buffer Management): 缓冲区管理是一种优化I/O操作的技术,它涉及到在内存中设置缓冲区,用于临时存储从设备读取的数据或等待发送到设备的数据。这些缓冲区可以减少CPU和设备之间的等待时间,从而提高数据传输的效率。缓冲区管理还包括数据在缓冲区中的读取、写入、清除等操作。
以下是几种常见的缓冲区管理技术的简单介绍:
单缓冲(Single Buffering): 单缓冲是最简单的缓冲区管理技术。它涉及在内存中创建一个缓冲区,用于存储从设备读取的数据或等待发送到设备的数据。这种技术的主要优点是简单,但它可能导致CPU和I/O设备之间的等待时间增加,因为数据在单个缓冲区中传输时,CPU可能需要等待数据的读取或写入完成。
双缓冲(Double Buffering): 双缓冲是一种改进的缓冲区管理技术,它使用两个缓冲区交替地进行数据传输。一个缓冲区用于数据的读取或写入,另一个缓冲区用于数据的传输到另一端。这样,在一个缓冲区进行传输时,CPU可以继续处理另一个缓冲区的数据,从而减少等待时间,提高效率。
循环缓冲(Circular Buffer): 循环缓冲是一种特殊的缓冲区管理技术,它在内存中创建一个固定大小的循环数组作为缓冲区。数据可以循环地写入和读取数组,当写入指针达到数组末尾时,它会回到数组的开头继续写入。这种技术可以有效地利用内存空间,但需要适当的同步机制来避免数据的读写冲突。
缓冲池(Buffer Pool): 缓冲池是一种更复杂的缓冲区管理技术,它涉及在内存中维护一个由多个缓冲区组成的池子。这些缓冲区可以由不同的任务或作业共享使用。缓冲池技术可以更好地管理和分配缓冲区资源,避免了单一缓冲区可能导致的瓶颈问题。
文件管理和磁盘管理实在不想写了
这里找了一片博主总结的,很不错,可以直接参考
https://blog.csdn.net/weixin_43914604/article/details/104415990
相关文章:
【操作系统】操作系统知识点总结(秋招篇)
文章目录 前言操作系统主要做了哪些工作?进程 线程 协程之间的区别进程的组成部分介绍一下进程的PCB讲一下进程的五态 以及它们的状态转移用户态和内核态是什么?进程在用户态和内核态之间是如何切换的讲一下进程之间的通信方式讲一下进程调度的三个层次介…...
篇十九:迭代器模式:遍历集合
篇十九:"迭代器模式:遍历集合" 开始本篇文章之前先推荐一个好用的学习工具,AIRIght,借助于AI助手工具,学习事半功倍。欢迎访问:http://airight.fun/。 另外有2本不错的关于设计模式的资料&…...
浅谈JVM中的即时编译器(Just-In-Time compiler, JIT)
Java虚拟机(JVM)中的即时编译器(Just-In-Time compiler, JIT)是一个非常重要的组件,它负责将字节码转换为本地机器代码。在不使用JIT的情况下,JVM通过解释字节码来执行程序,这意味着它会为每个字…...
Android 13 Launcher——长按图标弹窗内容修改以及小组件等隐藏起来
目录 一.背景 二.实现思路 三.布局文件修改 四.隐藏代码中原先的view 一.背景 由于定制化开发需要将原先的长按图标原生弹窗界面隐藏,然后显示自定义的弹窗界面,如下就是我们来实现自定义的弹窗界面...
又一个不可错过的编程大模型来了让你惊呼“码农人生”不虚此行
继Stable Diffusion爆火之后,StabilityAI近期又放大招,推出了号称是革命性的编程大模型StableCode。StableCode是其首款用于编码的LLM生成式AI产品,该产品旨在帮助程序员完成日常工作。目前已发布的版本为StableCode-Completion-Alpha-3B&…...
【Express.js】集成SocketIO
集成SocketIO 本节我们介绍在如何在 express 中集成 Socket.IO Socket.IO 算是 WebSocket 的一个超集,进行了一些封装和拓展。 准备工作 创建一个 express.js 项目(本文基于evp-express-cli)安装socket.io.js: npm i socket.io创建代理 …...
为树莓派Pico配置交叉编译环境和工具链arm-none-eabi-gcc时可能会遇到的错误以及解决方案
本文是一个类似手册的文章,用来记录可能遇到的错误。你可以通过侧栏选择遇到的错误来查看详细信息。 No install step for ‘ELF2UF2Build’ 遇到这种错误有两种原因: 安装了版本不对或者不完整的arm-none-eabi-gcc;没有使用正确的 C/C 的…...
Yum 部署K8S集群
目录 1、准备环境 (温馨提示:尽量一次完成集群) 2.安装master节点 3、安装k8s-master上的node 4、安装配置k8s-node1节点 5、安装k8s-node2节点 6、为所有node节点配置flannel网络 7、配置docker开启加载防火墙规则允许转发数据 一. 环…...
初阶C语言-操作符详解(下)
🌞 “等春风得意,等时间嘉许!” 接下来,我们把操作符没学完的继续学完! 操作符详解 6.2sizeof和数组 7.关系操作符8.逻辑操作符9.条件操作符10.逗号表达式11.下标引用、函数调用和结构成员12.表达式求值12.1隐式类型转…...
reposync命令——下载yum仓库中全部的包到本地
reposync命令可以将远端yum仓库里面的包全部都下载到本地。这样构建自己的yum仓库,就不会遇到网络经常更新包而头疼的事情了。 reposync命令在软件包 yum-utils 里面,需要保证yum-utils已安装。 yum install yum-utils -y 常用参数 -r :指定…...
LC-杨辉三角
LC-杨辉三角 链接:https://leetcode.cn/problems/pascals-triangle/submissions/ 上图就是一个杨辉三角,每个数等于他左上角的数与右上角的数之和。 第一行就是一个1;第二行是两个1;第三行的2就是它肩膀上两个1之和,其余的类似。…...
Golang空结构体struct{}的作用是什么?
文章目录 占位符:通道标识:键集合:内存占用优化:总结: 在Go语言中,空结构体 struct{}是一种特殊的数据类型,它不占用任何内存空间。空结构体没有任何字段,也没有任何方法。尽管它看起…...
自然语言处理从入门到应用——LangChain:提示(Prompts)-[示例选择器(Example Selectors)]
分类目录:《自然语言处理从入门到应用》总目录 如果我们拥有大量的示例,我们可能需要选择在提示中包含哪些示例。ExampleSelector是负责执行此操作的类。 其基本接口定义如下所示: class BaseExampleSelector(ABC):"""Interf…...
【实战项目】c++实现基于reactor的高并发服务器
基于Reactor的高并发服务器,分为反应堆模型,多线程,I/O模型,服务器,Http请求和响应五部分 全局 反应堆模型 Channel 描述了文件描述符以及读写事件,以及对应的读写销毁回调函数,对应存储ar…...
Docker部署ElasticSearch7
前言 帮助小伙伴快速部署研发或测试环境进行学习测试。springboot版本需要与ElasticSearch版本想对应,不同版本api不一致,会产生异常调用的情况。 一、拉取镜像 这里选择固定版本7.15.2 docker pull docker.elastic.co/elasticsearch/elasticsearch:…...
【算法|数组】滑动窗口
算法|数组——滑动窗口 引入 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl1, ..., numsr-1, numsr] ,并返回其长度**。**如果不存在符合条件的子数组,返回 0 。 示例…...
笙默考试管理系统-MyExamTest----codemirror(2)
笙默考试管理系统-MyExamTest----codemirror(2) 目录 一、 笙默考试管理系统-MyExamTest----codemirror 二、 笙默考试管理系统-MyExamTest----codemirror 三、 笙默考试管理系统-MyExamTest----codemirror 四、 笙默考试管理系统-MyExamTest---…...
一次面试下来Android Framework 层的源码就问了4轮
说起字节跳动的这次面试经历,真的是现在都让我感觉背脊发凉,简直被面试官折磨的太难受了。虽然已经工作了七年,但是也只是纯粹的在写业务,对底层并没有一个很深的认识,这次面试经历直接的让我感受到我和那些一线大厂开…...
知网期刊《中阿科技论坛》简介及投稿须知
知网期刊《中阿科技论坛》简介及投稿须知 主管单位:宁夏回族自治区科学技术厅 主办单位:宁夏回族自治区对外科技交流中心(中国一阿拉伯国家技术转移中心) 刊 期:月刊 国际刊号:ISSN 2096-7268 国内刊号:CN 64-…...
kafka是有序的吗?如何保证有序?
首先,Kafka无法保证消息的全局有序性,这是因为Kafka的设计中允许多个生产者并行地向同一个主题写入消息。而且,一个主题可能会被划分为多个分区,每个分区都可以在独立的生产者和消费者之间进行并行处理。因此,生产者将…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...
【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...
深入浅出Diffusion模型:从原理到实践的全方位教程
I. 引言:生成式AI的黎明 – Diffusion模型是什么? 近年来,生成式人工智能(Generative AI)领域取得了爆炸性的进展,模型能够根据简单的文本提示创作出逼真的图像、连贯的文本,乃至更多令人惊叹的…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...
