进程与线程基础

异步

  • 进程,线程,协程的区别?

 1、进程

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。

  2、线程

线程是进程的一个实体,是CPU调度和分配的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。

  3、协程

协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

协程就是在unity主线程运行时开启另外一段逻辑来协助运行,来模拟多线程。使用场景多用于异步、动画、等待等操作,最常见的就是进度条异步加载。原理是利用了像list、Dictionary内部使用的迭代器,但是使用方法不同,不是迭代元素,而是分帧执行代码。每一帧调用迭代器的方法(MoveNext),其中unity利用关键字yield return快速实现了迭代器,两个yield return之间就是MoveNext方法执行的内容。
线程是操作系统调度的基本单位,一个程序至少会有一个线程。unity本身没有多线程,但是支持多线程的使用(不这样说,不懂的就查到网上很多文章说unity没有多线程的概念来质疑你)。unity中使用多线程是无法直接调用unity的api,需要访问unity的GameObject、Transform这些对象的时候可以搞容器去在主线程(比如在update)中处理。一般来说,多线程用在处理网络、数据计算、资源加载此类的场景。

协程是异步,进程和线程是同步

特点 进程 (Process) 线程 (Thread) 协程 (Coroutine)
资源开销 最大,创建和销毁开销大 较小,轻量级 最小,开销极小
调度方式 由操作系统内核调度 由操作系统内核调度 由程序自身调度
独立性 独立地址空间 共享进程资源 共享单线程资源
通信方式 通过 IPC 机制 直接共享内存 直接共享内存
适用场景 高度隔离的任务 并发执行任务 I/O 密集型任务状态管理
状态管理 创建、就绪、运行、等待、终止 创建、就绪、运行、等待、终止 创建、就绪、运行、挂起、终止

进程线程区别

  • 调度:进程是资源管理的基本单位,线程是程序执行的基本单位。

  • 切换:线程上下文切换比进程上下文切换要快得多。

  • 拥有资源: 进程是拥有资源的一个独立单位,线程不拥有系统资源,但是可以访问隶属于进程的资源。

  • 系统开销: 创建或撤销进程时,系统都要为之分配或回收系统资源,如内存空间,I/O设备等,OS所付出的开销显著大于在创建或撤销线程时的开销,进程切换的开销也远大于线程切换的开销。

协程线程区别

  • 线程和进程都是同步机制,而协程是异步机制。

  • 线程是抢占式,而协程是非抢占式的。需要用户释放使用权切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。

  • 一个线程可以有多个协程,一个进程也可以有多个协程。

  • 协程不被操作系统内核管理,而完全是由程序控制。线程是被分割的CPU资源,协程是组织好的代码流程,线程是协程的资源。但协程不会直接使用线程,协程直接利用的是执行器关联任意线程或线程池。协程能保留上一次调用时的状态。线程和进程都是同步机制,而协程是异步机制。

  • 线程是抢占式,而协程是非抢占式的。需要用户释放使用权切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。

  • 一个线程可以有多个协程,一个进程也可以有多个协程。

  • 协程不被操作系统内核管理,而完全是由程序控制。线程是被分割的CPU资源,协程是组织好的代码流程,线程是协程的资源。但协程不会直接使用线程,协程直接利用的是执行器关联任意线程或线程池。

  • 协程能保留上一次调用时的状态。

协程与unity实现

进程、线程、协程

线程与进程

知识点— 了解线程前先了解进程

  • 进程(Process) 是计算机程序中关于某数据集合上的一次运行活动

  • 是系统进行资源分配和调度的基本单位,是操作系统结构的基础

  • 说人话:打开应用程序就是在操作系统上开启了一个进程

  • 进程之间可以相互独立运行,互不干扰

  • 进程之间也可以相互访问、操作

什么是线程

  • 操作系统能够进行运算调度的最小单位。
    它被包含在进程之中,是进程中的实际运作单位
  • 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程
  • 我们目前写的程序 都在主线程中

简单理解线程:

  • 就是代码从上到下运行的一条“管道”

什么是多线程

  • 我们可以通过代码开启新的线程
  • 可以同时运行代码的多条“管道”就叫多线程

在C#中,你可以通过多种方式创建和管理线程。以下是一些基本的线程创建和操作的示例代码:

区别

协程:即协作式程序,其思想是,一系列互相依赖的协程间依次使用CPU,每次只有一个协程工作,而其他协程处于休眠状态。协程实际上是在一个线程中,只不过每个协程对CPU进行分时,协程可以访问和使用unity的所有方法和component。同一时间只能执行某个协程。开辟多个协程开销不大。协程适合对某任务进行分时处理。
线程:多线程是阻塞式的,每个IO都必须开启一个新的线程,但是对于多CPU的系统应该使用thread,尤其是有大量数据运算的时刻,但是IO密集型就不适合;而且thread中不能操作unity的很多方法和component。同一时间可以同时执行多个线程。开辟多条线程开销很大。线程适合多任务同时处理。
线程和协同程序的主要不同在于:在多处理器情况下,从概念上来讲多线程程序同时运行多个线程;而协同程序是通过协作来完成,在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只在必要时才会被挂起。

进程、线程、协程

  • 进程

    • 保存在硬盘上的程序运行以后,会在内存空间里形成一个独立的内存体,这个内存体有自己独立的地址空间,有自己的堆,不同进程间可以进行进程间通信,上级挂靠单位是操作系统。一个应用程序相当于一个进程,操作系统会以进程为单位,分配系统资源(CPU 时间片、内存等资源),进程是资源分配的最小单位。
  • 线程

    • 线程从属于进程,也被称为轻量级进程,是程序的实际执行者。线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条- 线程并行执行不同的任务。一个线程只有一个进程。

    • 每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

    • 线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是的)。

  • 协程

  • 协程是伴随着主线程一起运行的一段程序。

  • 协程与协程之间是并行执行,与主线程也是并行执行,同一时间只能执行一个协程提起协程,自然是要想到线程,因为协程的定义就是伴随主线程来运行的。

  • 一个线程可以拥有多个协程,协程不是被操作系统内核所管理,而完全是由程序所控制。

  • 协程和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。

  • 协成是单线程下由应用程序级别实现的并发。

unity 协程

在Unity中,协程是一种轻量级的线程,用于在单一线程上执行多个任务,实现异步等待和延迟执行等效果。协程不是取代线程,而是抽象于线程之上,同一时间只有一个协程拥有运行权,相当于单线程的能力。协程的执行基于迭代器,通过yield语句来实现暂停和恢复执行,从而更加灵活地控制协程的执行过程。

协程可以在不创建新线程的情况下实现异步等待和延迟执行,避免了线程切换和同步等问题,从而提高了程序的性能和效率。在Unity中,协程通常用于处理一些异步任务,比如等待一段时间、播放动画、加载资源等。协程的执行过程可以通过yield语句来控制,比如yield return new WaitForSeconds(5)可以让协程等待5秒钟后再继续执行。此外,协程还可以通过yield return null等方式来控制协程的执行过程。

需要注意的是,协程并不是多线程,协程的执行是在主线程上进行的,因此协程中的操作不应该阻塞主线程的执行。如果协程需要执行一些耗时的操作,可以将其放到后台线程中执行,然后通过yield return null等方式来控制协程的执行过程。

在Unity中只有主线程才能访问Unity3D的对象、方法、组件。当主线程在执行一个对资源消耗很大的操作时,在这一帧我们的程序就会出现帧率下降,画面卡顿的现象!

那这个时候我们就可以利用协程来做这件事,因为协程是伴随着主线程运行的,主线程依旧可以丝滑轻松的工作,把脏活累活交给协程处理就好了!简单来说:协程是辅助主线程的操作,避免游戏卡顿。

迭代器

迭代器是什么

详解C# 迭代器 - Cat Qi - 博客园 (cnblogs.com)

  • 迭代器(iterator)有时又称光标(cursor)
  • 是程序设计的软件设计模式
  • 迭代器模式提供一个方法顺序访问一个聚合对象中的各个元素
  • 而又不暴露其内部的标识
  • 在表现效果上看
  • 是可以在容器对象(例如链表或数组)上遍历访问的接口
  • 设计人员无需关心容器对象的内存分配的实现细节
  • 可以用foreach遍历的类,都是实现了迭代器的

使用迭代器

IEnumerator,IEnumerable

命名空间: using System.Collections;

可以通过同时继承IEnumerable和IEnumerator实现其中的方法

  • 底层

  • 协程是通过迭代器来实现功能的,通过关键字IEnumerator来定义一个迭代方法。

  • StartCoroutine 接受到的是一个 IEnumerator ,这是个接口,并且是枚举器或迭代器的意思。

  • yield 是 C#的一个关键字,也是一个语法糖,背后的原理会生成一个类,并且也是一个枚举器,而且不同于 return,yield 可以出现多次。

  • yield 实际上就是返回一次结果,因为我们要一次一次枚举一个值出来,所以多个 yield 其实是个状态模式,第一个 yield 是状态 1,第二个 yield 是状态 2,每次访问时会基于状态知道当前应该执行哪一个 yield,取得哪一个值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System.Collections;

class CustomList: IEnumerable
{
private int [] list;
public CustomList(){
list=new int[]{1,2,3,4};
}

pubilc IEnumerator GetEnumerator(){
//调用方法
}
}



  • foreach 本质
    • 先获取in 后面的对象的IEnumerator
    • 会调用其中的GetEnumerator 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class IterationSampleEnumerator : IEnumerator
{
IterationSample parent;//迭代的对象 #1
Int32 position;//当前游标的位置 #2
internal IterationSampleEnumerator(IterationSample parent)
{
this.parent = parent;
position = -1;// 数组元素下标从0开始,初始时默认当前游标设置为 -1,即在第一个元素之前, #3
}

public bool MoveNext()
{
if (position != parent.values.Length) //判断当前位置是否为最后一个,如果不是游标自增 #4
{
position++;
}
return position < parent.values.Length;
}

public object Current
{
get
{
if (position == -1 || position == parent.values.Length)//第一个之前和最后一个自后的访问非法 #5
{
throw new InvalidOperationException();
}
Int32 index = position + parent.startingPoint;//考虑自定义开始位置的情况 #6
index = index % parent.values.Length;
return parent.values[index];
}
}

public void Reset()
{
position = -1;//将游标重置为-1 #7
}
}

通过yield语句简化迭代

1
2
3
4
5
6
7
8
public IEnumerator GetEnumerator()
{
for (int index = 0; index < this.values.Length; index++)
{
yield return values[(index + startingPoint) % values.Length];
}
}

总结

从程序的角度讲,协程的核心就是迭代器。

想要定义一个协程方法有两个因素,第一:方法的返回值为 IEnumerator 。第二,方法中有 yield关键字。

当代码满足以上两个条件时,此方法的执行就具有了迭代器的特质,其核心就是 MoveNext方法。

方法内的内容将会被分成两部分:yield 之前的代码和 yield 之后的代码。yield之前的代码会在第一次执行MoveNext时执行, yield之后的代码会在第二次执行MoveNext方法时执行。

而在Unity中,MoveNext的执行时机是以帧为单位的,无论你是设置了延迟时间,还是通过按钮调用MoveNext,亦或是根本没有设置执行条件,Unity都会在每一帧的生命周期中判断当前帧是否满足当前协程所定义的条件,一旦满足,当前帧就会抽出CPU时间执行你所定义的协程迭代器的MoveNext。

注意,只要方法中有yield语句,那么方法的返回值就必须是 IEnumerator ,不然无法通过编译

进程间通信

机制 通信范围 速度 数据格式 典型应用场景
管道 单机有亲缘进程 字节流 进程间简单数据流
信号 单机 异步事件 进程控制、异常处理
信号量 单机进程/线程 同步信号 资源竞争控制
消息队列 单机 结构化消息 异步任务队列、削峰填谷
共享内存 单机 极快 原始数据 高频数据交换(如视频处理)
Socket 跨网络 较低 字节流/报文 网络服务、分布式系统
  1. 管道:管道这种通讯方式有两种限制,一是半双工的通信,数据只能单向流动,二是只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。管道可以分为两类:匿名管道和命名管道。匿名管道是单向的,只能在有亲缘关系的进程间通信;命名管道以磁盘文件的方式存在,可以实现本机任意两个进程通信。
  2. 信号:信号是一种比较复杂的通信方式,信号可以在任何时候发给某一进程,而无需知道该进程的状态。Linux系统中常用信号: (1) SIGHUP:用户从终端注销,所有已启动进程都将收到该进程。系统缺省状态下对该信号的处理是终止进程。(2) SIGINT:程序终止信号。程序运行过程中,按Ctrl+C键将产生该信号。 (3) SIGQUIT:程序退出信号。程序运行过程中,按Ctrl+\键将产生该信号。(4) SIGBUS和SIGSEGV:进程访问非法地址。(5) SIGFPE:运算中出现致命错误,如除零操作、数据溢出等。(6) SIGKILL:用户终止进程执行信号。shell下执行kill-9发送该信号。(7)SIGTERM:结束进程信号。shell下执行kill进程pid发送该信号。(8) SIGALRM:定时器信号。(9)SIGCLD:子进程退出信号。如果其父进程没有忽略该信号也没有处理该信号,则子进程退出后将形成僵尸进程。
  3. 信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
  4. 消息队列:消息队列是消息的链接表,包括Posix消息队列和System V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
  5. 共享内存:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
  6. Socket:与其他通信机制不同的是,它可用于不同机器间的进程通信。

优缺点:

管道:速度慢,容量有限;

Socket:任何进程间都能通讯,但速度慢;

消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题;

信号量:不能传递复杂消息,只能用来同步;

共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存。

死锁

多线程冲突

互斥与同步

互斥与同步使用

信号量

生产者- 消费者模型

同步问题

哲学家就餐问题

如何防止死锁

死锁概念与特点

先上来一个提问:

  • 什么是死锁,死锁的条件以及如何防止?什么是银行家算法?

(1)死锁就是多个进程并发执行,在各自占有一定资源的情况下,希望获得其他进程占有的资源以推进执行,但是其他资源同样也期待获得另外进程的资源,大家都不愿意释放自己的资源,从而导致了相互阻塞、循环等待,进程无法推进的情况。

(2)死锁条件:1)互斥条件(一个资源每次只能被一个进程使用);2)请求并保持条件(因请求资源而阻塞时,对已获得的资源保持不放);3)不剥夺条件(在未使用完之前,不能剥夺,只能自己释放);4)循环等待(若干进程之间形成一种头尾相接的循环等待资源关系)。

(3)死锁防止:1)死锁预防,打破四个死锁条件;2)死锁避免,使用算法来进行资源分配,防止系统进入不安全状态,如银行家算法;3)死锁检测和解除,抢占资源或者终止进程;

(4)银行家算法是一种最有代表性的避免死锁的算法。在避免死锁方法中允许进程动态地申请资源,但系统在进行资源分配之前,应先计算此次分配资源的安全性,若分配不会导致系统进入不安全状态,则分配,否则等待。为实现银行家算法,系统必须设置若干数据结构。安全的状态指的是一个进程序列{P1,P2,…Pn},对于每一个进程Pi,它以后尚需要的资源不大于当前资源剩余量和其余进程所占有的资源量之和。

  • 那么死锁的概念是什么呢?

代码模拟死锁

避免死锁

悲观锁与乐观锁

编译

  • C++从源文件到可执行文件需要经历哪些步骤

首先是**预处理阶段(preprocessing)-》编译阶段(compilation)-》汇编阶段(assembly)-》链接阶段(linking)**。

c++编译过程简介 - 云东 - 博客园

答:包括四个阶段:预处理阶段、编译阶段、汇编阶段、连接阶段。

(1)预处理阶段处理头文件包含关系,对预编译命令进行替换,生成预编译文件;

(2)编译阶段将预编译文件编译,生成汇编文件(编译的过程就是把预处理完的文件进行一系列的词法分析,语法分析,语义分析及优化后生成相应的汇编代码);

(3)汇编阶段将汇编文件转换成机器码,生成可重定位目标文件(.obj文件)(汇编器是将汇编代码转变成机器可以执行的命令,每一个汇编语句几乎都对应一条机器指令。汇编相对于编译过程比较简单,根据汇编指令和机器指令的对照表一一翻译即可);

(4)链接阶段,将多个目标文件和所需要的库连接成可执行文件(.exe文件)。

Cache

计算机组成原理

  • 什么是缓存(Cache)?为什么需要缓存?如何提高缓存的命中率?缓存是不是最快的?(⭐⭐)

(1)Cache即CPU的高速缓冲存储器,是一种是用于减少处理器访问内存所需平均时间的部件;

(2)由于CPU的计算速度远远大于从CPU向内存取数据的速度,如果每次都让CPU去内存取数据,会导致CPU计算能力的浪费,所以人们设计了缓存,CPU通过读写缓存来获取操作数,结果也通过缓存写入内存;

(3)注意程序的局部性原理,在遍历数组时按照内存顺序访问;充分利用CPU分支预测功能,将预测的指令放到缓存中执行;此外缓存的容量和块长是影响缓存效率的重要因素。如何提升CPU的缓存命中率? - 知乎 (zhihu.com)

(4)缓存不是最快的,寄存器更快。

  • 什么是缓存一致性,如何保证缓存一致性

(1)在多核CPU中,每个核有自己的缓存,在两个核进行独自修改缓存中的数据的时候,就可能会造成数据不一致的问题,就是缓存的一致性问题;

(2)一个是在总线中加锁,一个是采用缓存一致性协议。

  • 一个缓存块的大小是多少,读取内存中的字段是读多少数据取多少内存吗?

如果缓存没有命中(即读取一个数据没有在缓存中),不仅需要把该字(数据)从主存中取出,还需要从主存中将它所在的整个字块一次调入缓存中。缓存线(块)的长度是64B。

内存

  • 操作系统如何管理内存,什么是虚拟内存?

通过一种分页管理机制来进行内存管理。分页管理机制将程序的逻辑地址划分为固定大小的页,而物理内存划分为同样大小的帧,程序加载时,可以将任意一页放入内存中任意一个帧,这些帧不必连续,从而实现了离散分离。虚拟内存是基于分页存储管理机制的,它允许程序不必将所有的页都放入内存中,而只是将一部分页映射到内存中,另一部分页放在外存上(如磁盘、软盘、USB),当引用到不在内存中的页时,系统产生缺页中断,并从外存中调入该部分页进来,从而产生一种逻辑上内存得到扩充的感觉,实际上内存并没有增大。

  • 什么是内存碎片,内存碎片是在虚拟内存还是物理内存?

【参考资料】:内存碎片_百度百科 (baidu.com)

采用分区式存储管理的系统,在储存分配过程中产生的、不能供用户作业使用的主存里的小分区称成“内存碎片”。内存碎片分为内部碎片和外部碎片。内存碎片只存在于虚拟内存上。

其他概要

概要:

  • 操作系统的四大功能:进程控制,内存管理,设备管理,文件管理
  • 进程的定义:并发程序的执行,进程的同步与互斥
  • 进程的状态:创建,终止,挂起,激活,阻塞,唤醒,以及相互转换关系
  • 进程同步问题:生产者消费者模型,信号量机制
  • 进程的调度与死锁,死锁条件
  • 进程死锁的原因,如何避免
  • 存储器管理:只读存储器,随机存储器,相对地址和绝对地址
  • 速度问题:CPU>寄存器>缓存>主存>磁盘缓存>硬盘
  • 程序的装入和链接(动态装入,静态装入,重定位)
  • 内存空间划分算法:首次适应;最佳适应;最坏适应
  • 段式存储和页式存储,以及段页式存储的优缺点,原理
  • 页面置换算法(LRU,LFU,FIFO这些)
  • 进程与线程的区别和联系
  • 多线程编程安全性
  • 线程池,线程的创建,同步,安全问题
  • 信号量机制

概要解析:

  1. 四大操作系统功能:
    • 进程控制: 管理程序执行,包括进程的创建、调度和终止。
    • 内存管理: 分配和管理内存空间,保证内存的有效使用。
    • 设备管理: 管理计算机硬件设备的访问,包括输入输出设备。
    • 文件管理: 管理文件的存储、访问、保护和共享。
  2. 进程的定义:
    • 进程是计算机中运行的一个程序,它是程序执行的实例。
    • 并发执行: 同时运行多个进程。
    • 进程的同步与互斥: 同步是指不同进程间的协调,互斥是指多个进程共享资源时的访问控制。
  3. 进程的状态:
    • 创建: 进程被创建。
    • 终止: 进程完成并退出。
    • 挂起: 进程因某些原因被暂停执行,等待条件满足。
    • 激活: 挂起的进程恢复执行。
    • 阻塞: 进程等待某个事件的发生(如I/O操作)。
    • 唤醒: 阻塞的进程条件满足,恢复执行。
    • 状态转换的关系:进程在不同状态间转换,通常由调度器和事件控制。
  4. 进程同步问题:
    • 生产者-消费者模型: 解决多个进程共享资源时的同步问题。
    • 信号量机制: 通过计数器控制进程对共享资源的访问。
  5. 进程调度与死锁:
    • 进程调度: 操作系统决定哪个进程获得CPU时间。
    • 死锁条件: 互斥、请求与保持、不剥夺、环路等待。
  6. 进程死锁的原因与避免:
    • 死锁的原因是进程相互等待资源而无法释放,导致永远无法继续执行。
    • 避免死锁: 可以通过资源分配策略(如资源有序分配、死锁检测等)避免死锁。
  7. 存储器管理:
    • 只读存储器(ROM): 只能读取的存储器,通常用于存储固件。
    • 随机存储器(RAM): 计算机的主要内存,数据可以读写。
    • 相对地址与绝对地址: 相对地址是偏移量,绝对地址是内存中的实际位置。
  8. 速度问题:
    • CPU > 寄存器 > 缓存 > 主存 > 磁盘缓存 > 硬盘:速度从快到慢。
  9. 程序的装入和链接:
    • 动态装入: 程序在运行时加载。
    • 静态装入: 程序在执行前就加载到内存。
    • 重定位: 调整程序的地址使其适应不同的内存位置。
  10. 内存空间划分算法:
    • 首次适应: 第一个足够大的空闲块。
    • 最佳适应: 最小的足够大的空闲块。
    • 最坏适应: 最大的空闲块。
  11. 存储管理方式:
    • 段式存储: 将程序分为多个段,如代码段、数据段。
    • 页式存储: 将程序划分为固定大小的页。
    • 段页式存储: 结合段式和页式存储,平衡两者的优缺点。
  12. 页面置换算法:
    • LRU (Least Recently Used): 最近最少使用页面替换。
    • LFU (Least Frequently Used): 最少使用次数的页面替换。
    • FIFO (First In, First Out): 先进先出页面替换。
  13. 进程与线程的区别:
    • 进程: 操作系统中的独立执行单元,拥有自己的地址空间。
    • 线程: 进程内部的执行单元,多个线程共享同一进程的资源。
    • 区别与联系: 线程是轻量级的进程,多个线程可以并发执行,并且共享进程的内存。
  14. 多线程编程安全性:
    • 多线程编程需要关注同步和互斥,防止资源竞争和数据不一致。
  15. 线程池:
    • 线程池: 用于管理多个线程,减少线程创建和销毁的开销。
    • 线程的创建与同步: 线程池通过线程的复用来避免频繁创建销毁线程,并保证线程间同步。
    • 安全问题: 线程之间的资源共享需要通过锁等机制保证数据一致性和安全性。
  16. 信号量机制:
    • 用于进程间同步,控制多个进程对共享资源的访问。

参加数十次校招笔试面试后,我做了全面的总结(游戏开发岗) - 知乎