多处理机操作系统:进程同步

2024-06-21 12:52

本文主要是介绍多处理机操作系统:进程同步,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一.集中式与分布式同步方式

1.集中式同步

2.分布式同步

二.自旋锁

1.自旋锁的特点

2.自旋锁的缺点

3.自旋锁示例(伪代码)

三.读-复制-更新锁

1.RCU锁的特点

2.RCU锁的缺点

3. RCU锁示例(伪代码)

三.二进制数补偿算法和待锁 CPU 等待队列机构

二进制数补偿算法 

1.概述:

2.优点:

3.缺点:

待锁 CPU 等待队列机制 (Lock-Queue-Wait)

1.概述:

2.优点:

3.缺点:

四.定序机构

1. 为什么要使用定序机构?

2. 定序机构的类型

3. 定序机构的应用

五.面包房算法

1.概述

2.算法原理

3.算法实现

分析

适用场景

六.令牌环算法

概述

算法原理

优点

缺点

应用场景

示例

分析


        在多处理机系统中,多个处理器可以同时执行多个进程,这使得进程同步成为了一个关键问题。进程同步是指协调多个进程执行顺序和访问共享资源的方式,以确保数据一致性和避免冲突。本文将探讨多处理机操作系统中的各种进程同步机制和算法。

一.集中式与分布式同步方式

        多处理机操作系统中的进程同步方式可以分为集中式同步和分布式同步两种。每种方式在实现和应用上都有其优点和缺点,适用于不同的场景和需求。

1.集中式同步

概述

  • 集中式同步方式采用一个中央同步机构来协调所有进程的执行。所有进程对共享资源的访问请求都发送到中央同步机构,由它来控制访问顺序。

优点

  1. 实现简单:由于所有同步操作都集中在一个地方,管理和控制相对简单。
  2. 一致性保障:中央同步机构可以确保所有进程对共享资源的访问顺序,避免数据冲突和不一致问题。

缺点

  1. 单点瓶颈:中央同步机构可能成为系统的瓶颈,影响整体性能。如果中央机构处理请求的速度跟不上进程的请求速度,系统性能会显著下降。
  2. 可靠性问题:如果中央同步机构出现故障,会导致整个系统的同步机制失效,影响系统的稳定性和可靠性。

应用场景

  • 小规模多处理机系统:如多核处理器中的共享内存同步。
  • 需要严格一致性的系统:如事务处理系统中的锁管理。

示例

假设有一个共享资源需要被多个进程访问,使用集中式同步机制的伪代码如下:

class CentralSync:def __init__(self):self.lock = threading.Lock()def request_access(self, process_id):self.lock.acquire()print(f"Process {process_id} is accessing the resource")def release_access(self):print("Resource is released")self.lock.release()central_sync = CentralSync()def process_task(process_id):central_sync.request_access(process_id)# Critical sectiontime.sleep(random.uniform(0.1, 0.5))central_sync.release_access()threads = []
for i in range(5):t = threading.Thread(target=process_task, args=(i,))threads.append(t)t.start()for t in threads:t.join()
2.分布式同步

概述

  • 分布式同步方式中,每个处理器都有自己的同步机构,进程之间的同步由它们自行协调。分布式同步减少了对中央同步机构的依赖,提高了系统的可扩展性。

优点

  1. 高可扩展性:由于没有单一的中央同步机构,系统可以更容易地扩展,处理更多的进程和共享资源。
  2. 减少单点瓶颈:每个处理器独立处理同步请求,避免了单点瓶颈问题,提高了系统性能。
  3. 提高可靠性:分布式同步避免了中央机构故障导致的全系统失效问题,提高了系统的可靠性。

缺点

  1. 实现复杂:进程之间的同步需要更多的协调和通信机制,增加了实现的复杂性。
  2. 一致性问题:需要额外的机制确保多个处理器之间的一致性,避免数据冲突和不一致问题。

应用场景

  • 大规模分布式系统:如分布式数据库、分布式文件系统。
  • 高并发应用:如大规模互联网服务、云计算平台。

示例

假设在分布式系统中,每个节点都有自己的同步机构,使用分布式同步机制的伪代码如下:

 

import threading
import time
import randomclass DistributedSync:def __init__(self):self.locks = {}def request_access(self, process_id, resource_id):if resource_id not in self.locks:self.locks[resource_id] = threading.Lock()self.locks[resource_id].acquire()print(f"Process {process_id} is accessing resource {resource_id}")def release_access(self, resource_id):if resource_id in self.locks:print(f"Resource {resource_id} is released")self.locks[resource_id].release()distributed_sync = DistributedSync()def process_task(process_id, resource_id):distributed_sync.request_access(process_id, resource_id)# Critical sectiontime.sleep(random.uniform(0.1, 0.5))distributed_sync.release_access(resource_id)threads = []
for i in range(5):t = threading.Thread(target=process_task, args=(i, i % 2)) # Accessing two different resourcesthreads.append(t)t.start()for t in threads:t.join()

二.自旋锁

        自旋锁是一种常用的同步机制,用于在多处理器环境下控制对共享资源的访问。当一个进程尝试获取锁时,如果锁已被其他进程持有,则该进程不会被挂起,而是进入自旋状态,不断循环等待直到锁被释放。自旋锁适合锁被持有时间较短的情况,可以避免挂起进程并调度其他进程的额外开销。

1.自旋锁的特点
  1. 简单实现:自旋锁的实现通常较为简单,主要依赖于硬件提供的原子操作(如Test-and-Set、Compare-and-Swap等)。

  2. 短锁持有时间:自旋锁适用于锁被持有时间较短的场景。在这种情况下,自旋等待不会产生明显的性能影响。

  3. 高频率锁访问:自旋锁适合高频率锁访问的场景,因为它避免了过程中频繁的上下文切换。

  4. 避免进程挂起:自旋锁不会将等待的进程挂起,而是保持自旋等待状态。这减少了调度开销和进程切换时间。

2.自旋锁的缺点
  1. 忙等待:自旋锁使用忙等待的方式获取锁,消耗CPU资源。如果锁被长时间持有,自旋锁可能导致性能下降。

  2. 不适合长时间锁持有:对于锁持有时间较长的情况,自旋锁不适用,因为自旋等待会浪费大量的CPU时间。

3.自旋锁示例(伪代码)
lock = 0; // 0表示锁未被持有,1表示锁被持有void acquire_lock() {while (atomic_test_and_set(&lock, 1)) {// 自旋等待,直到锁被释放}
}void release_lock() {lock = 0; // 释放锁
}

三.读-复制-更新锁

        RCU锁是一种为频繁读少写的共享数据设计的同步机制。它允许多个进程同时读取数据,而写数据的进程需要先复制数据,然后再进行修改。这可以确保读进程可以不受干扰地访问数据,同时写进程也不会阻塞读进程。

1.RCU锁的特点
  1. 高效读操作:RCU锁允许多个读进程同时读取数据,而不需要互斥锁。这提高了读操作的性能和并发性。

  2. 复制更新:写进程在更新数据前,先复制一份数据进行修改。修改完成后,使用指针替换旧数据,实现更新。

  3. 延迟回收:RCU锁使用延迟回收机制,在所有读进程都完成对旧数据的访问后,才回收旧数据的内存。这确保了读进程访问的一致性。

  4. 适用于读多写少:RCU锁非常适用于读多写少的场景,如操作系统内核数据结构、路由表等。

2.RCU锁的缺点
  1. 写操作复杂:写操作需要复制数据并更新指针,过程相对复杂。

  2. 内存占用增加:在读进程较多的情况下,旧数据的延迟回收可能导致内存占用增加。

3. RCU锁示例(伪代码)
data = new_data(); // 新数据副本void rcu_read_lock() {// 读锁,标志读操作开始rcu_read_count++;
}void rcu_read_unlock() {// 读锁,标志读操作结束rcu_read_count--;
}void rcu_synchronize() {// 等待所有读进程完成读操作while (rcu_read_count > 0) {// 自旋等待}
}void rcu_update(data_t *new_data) {rcu_synchronize(); // 确保所有读操作完成old_data = data; // 旧数据data = new_data; // 替换为新数据free(old_data); // 释放旧数据
}

三.二进制数补偿算法和待锁 CPU 等待队列机构

二进制数补偿算法 

1.概述


        二进制数补偿算法是一种用于避免死锁和饥饿的同步机制。每个进程都有一个二进制数,当一个进程请求一个锁时,会检查所有其他进程的二进制数。如果其他进程的二进制数较小,则允许该进程获取锁;否则,该进程进入等待队列。这样可以确保所有进程都有机会获取锁,避免饥饿情况的发生。

2.优点

  1. 避免饥饿:通过检查和比较二进制数,确保所有进程都有机会获取锁,避免某些进程长期无法获取锁的情况。
  2. 解决死锁:通过严格的次序控制,避免死锁的发生。

3.缺点

  1. 实现复杂:需要额外的机制来管理和比较每个进程的二进制数。
  2. 性能开销:二进制数的检查和比较会增加一些性能开销。

应用场景
适用于需要严格控制锁获取次序,避免饥饿和死锁的高并发环境。

示例

以下是一个简单的二进制数补偿算法的伪代码示例:

import threading
import timeclass BinaryCompensationLock:def __init__(self):self.lock = threading.Lock()self.queue = []self.current_binary_number = 0def acquire(self, process_id):with self.lock:binary_number = self.current_binary_numberself.queue.append((process_id, binary_number))self.queue.sort(key=lambda x: x[1])self.current_binary_number += 1while self.queue[0][0] != process_id:self.lock.release()time.sleep(0.01)self.lock.acquire()def release(self):with self.lock:self.queue.pop(0)binary_compensation_lock = BinaryCompensationLock()def process_task(process_id):binary_compensation_lock.acquire(process_id)# Critical sectionprint(f"Process {process_id} is in critical section")time.sleep(1)binary_compensation_lock.release()threads = []
for i in range(5):t = threading.Thread(target=process_task, args=(i,))threads.append(t)t.start()for t in threads:t.join()

待锁 CPU 等待队列机制 (Lock-Queue-Wait)

1.概述


待锁 CPU 等待队列机制是一种同步机制,当一个处理器请求一个锁时,如果锁已被其他处理器持有,则该处理器进入等待队列。当锁被释放时,等待队列中的下一个处理器被唤醒并获取锁。这样的机制可以避免处理器忙等待,提高系统效率。

2.优点

  1. 避免忙等待:处理器在等待锁时不会一直占用CPU,而是进入等待队列,释放CPU资源。
  2. 提高系统效率:通过等待队列机制,可以更高效地管理锁的分配和处理器资源。

3.缺点

  1. 实现复杂:需要额外的数据结构和机制来管理等待队列。
  2. 上下文切换开销:等待处理器的唤醒和上下文切换会增加一些开销。

应用场景
适用于多处理器系统中的高并发环境,需要避免忙等待,提高资源利用效率。

示例

以下是一个简单的待锁 CPU 等待队列机制的伪代码示例:

import threading
import queue
import timeclass LockQueueWait:def __init__(self):self.lock = threading.Lock()self.wait_queue = queue.Queue()def acquire(self):self.lock.acquire()if self.lock.locked():self.wait_queue.put(threading.current_thread())self.lock.release()while threading.current_thread() != self.wait_queue.queue[0]:time.sleep(0.01)self.lock.acquire()else:self.lock.release()def release(self):if not self.wait_queue.empty():self.wait_queue.get()self.lock.release()lock_queue_wait = LockQueueWait()def process_task(process_id):lock_queue_wait.acquire()# Critical sectionprint(f"Process {process_id} is in critical section")time.sleep(1)lock_queue_wait.release()threads = []
for i in range(5):t = threading.Thread(target=process_task, args=(i,))threads.append(t)t.start()for t in threads:t.join()

 

四.定序机构

        定序机构(Ordering Mechanism)是计算机系统中一种重要的机制,用于确保多个处理器以正确的顺序执行指令。它通常通过在处理器之间发送消息来实现,以协调它们的执行顺序。这对于保证程序的正确性至关重要,特别是在有内存共享的系统中。

 

1. 为什么要使用定序机构?

        在多处理器系统中,多个处理器可以并行执行指令。如果没有定序机构,处理器可能会在未完成依赖操作的情况下执行指令,导致程序行为不可预测甚至出现错误。例如,如果处理器 A 正在写入一个共享变量,处理器 B 在读取该变量之前就读取了它,那么处理器 B 读取到的值将是错误的。

        使用定序机构可以避免这些问题,因为它可以确保处理器按照正确的顺序执行指令,并完成所有依赖操作。

 

2. 定序机构的类型

定序机构有多种类型,它们在实现方式和性能上有所不同。常见类型的定序机构包括:

  • 程序顺序 (Program Order): 程序顺序是最简单的定序机构,它确保处理器按照程序文本的顺序执行指令。但是,程序顺序无法利用指令之间的并行性,因此效率较低。
  • 弱一致性 (Weak Consistency): 弱一致性允许处理器在不违反程序顺序的情况下重新排序指令。这可以提高性能,但会使程序的调试更加困难。
  • 强一致性 (Strong Consistency): 强一致性确保处理器看到的内存状态与程序顺序一致。这使得程序的调试更加容易,但会降低性能。
  • 锁 (Lock): 锁是一种显式的定序机制,它允许程序员控制多个处理器对共享资源的访问。锁可以提高性能,但如果使用不当,会导致死锁等问题。
  • 栅栏 (Barrier): 栅栏是一种用于强制处理器同步的定序机制。所有处理器必须到达一个栅栏才能继续执行。栅栏可以用于确保多个处理器在完成特定操作之前不再执行。

 

3. 定序机构的应用

定序机构在计算机系统的各个层级都有应用,包括:

  • 处理器架构: 处理器架构通常包含定序机构,用于控制处理器内部的不同执行单元之间的指令执行顺序。
  • 操作系统: 操作系统通常包含定序机构,用于协调多个处理器对内存和其他资源的访问。
  • 应用程序: 应用程序可以使用定序机构来控制多线程之间的指令执行顺序。

 

五.面包房算法

1.概述

        面包房算法(Bakery Algorithm)是由Leslie Lamport提出的一种用于避免死锁的分布式同步算法。该算法以面包店排队购买面包为喻,每个进程都有一个唯一的编号,当多个进程尝试获取同一个锁时,编号较小的进程优先获取锁。这种编号机制可以保证公平性,避免死锁和饥饿问题。

2.算法原理

面包房算法的基本思路是,每个进程在进入临界区之前,先“拿一个数字”,然后按照数字的顺序进入临界区。具体步骤如下:

  1. 进程获取号票

    • 每个进程请求进入临界区时,首先获取一个号票。这个号票是一个唯一的编号,表示该进程的优先级。
  2. 等待顺序到达

    • 进程在获取号票后,检查其他所有进程的号票,确保自己是当前最小的号票,才可以进入临界区。
  3. 进入临界区

    • 当进程确定自己的号票是最小的并且没有其他进程在进入临界区时,它可以进入临界区。
  4. 释放临界区

    • 进程完成临界区的任务后,释放号票,让其他进程可以继续进入。

3.算法实现

面包房算法的伪代码如下:

import threading
import timeclass BakeryAlgorithm:def __init__(self, n):self.n = nself.choosing = [False] * nself.number = [0] * ndef max_number(self):return max(self.number)def lock(self, process_id):self.choosing[process_id] = Trueself.number[process_id] = self.max_number() + 1self.choosing[process_id] = Falsefor i in range(self.n):while self.choosing[i]:passwhile self.number[i] != 0 and (self.number[i] < self.number[process_id] or (self.number[i] == self.number[process_id] and i < process_id)):passdef unlock(self, process_id):self.number[process_id] = 0def critical_section(process_id):print(f"Process {process_id} is in critical section")time.sleep(1)  # 模拟临界区操作print(f"Process {process_id} is leaving critical section")def process_task(bakery, process_id):while True:bakery.lock(process_id)critical_section(process_id)bakery.unlock(process_id)time.sleep(1)  # 模拟非临界区操作if __name__ == "__main__":n = 5bakery = BakeryAlgorithm(n)threads = []for i in range(n):t = threading.Thread(target=process_task, args=(bakery, i))threads.append(t)t.start()for t in threads:t.join()
分析

优点

  1. 避免死锁:面包房算法通过唯一编号机制,确保所有进程都能公平地获取锁,避免死锁和饥饿问题。
  2. 简单实现:相对其他复杂的分布式同步算法,面包房算法的实现较为简单。

缺点

  1. 性能开销:在进程数较多时,获取号票和比较号票的操作可能增加系统开销。
  2. 不适用于高实时性要求的场景:由于需要等待其他进程的号票,可能导致高延迟,不适合高实时性的应用场景。

适用场景

面包房算法适用于需要确保进程公平性和避免死锁的分布式系统,特别是在多进程并发访问共享资源的环境中,如:

  • 多线程编程中的临界区保护。
  • 分布式系统中的资源分配。

 

六.令牌环算法

概述

        令牌环算法是一种用于分布式同步的算法,常用于保证多个处理器或节点之间的同步和公平性。该算法使用一个令牌(Token)在处理器之间传递,只有持有令牌的处理器才能执行临界区代码。令牌按照一定的顺序在处理器之间传递,确保每个处理器都有机会进入临界区,从而避免死锁和饥饿问题。

算法原理
  1. 令牌初始化

    • 系统初始化时,创建一个唯一的令牌,并将其赋予某个处理器。
  2. 令牌传递

    • 持有令牌的处理器可以执行临界区代码。
    • 执行完临界区代码后,该处理器将令牌传递给下一个处理器。
    • 如果一个处理器没有临界区代码要执行,它直接将令牌传递给下一个处理器。
  3. 循环传递

    • 令牌在处理器之间按照固定顺序循环传递,确保所有处理器都有机会获取令牌。

优点
  1. 避免死锁和饥饿:令牌环算法通过令牌顺序传递,确保每个处理器都有机会获取令牌,避免死锁和饥饿情况。
  2. 公平性:每个处理器按顺序轮流获取令牌,保证了访问临界区的公平性。
  3. 简单实现:令牌环算法的概念和实现相对简单,适合分布式系统。

缺点
  1. 令牌丢失问题:如果令牌在传递过程中丢失,系统需要额外机制检测和恢复令牌。
  2. 延迟问题:令牌传递需要时间,处理器在等待令牌时会产生一定的延迟,尤其在处理器数量较多时。

应用场景

令牌环算法适用于需要公平访问共享资源的分布式系统,例如:

  • 局域网协议(如令牌环网络协议)。
  • 分布式数据库系统。
  • 分布式文件系统。

示例

以下是一个简单的令牌环算法伪代码示例:

import threading
import time# 初始化处理器数量和令牌
num_processors = 5
token = threading.Lock()
token_holder = 0def processor_task(process_id):global token_holderwhile True:# 等待令牌while token_holder != process_id:time.sleep(0.01)# 模拟临界区操作with token:print(f"Processor {process_id} is in critical section")time.sleep(1)  # 临界区操作print(f"Processor {process_id} is leaving critical section")# 传递令牌给下一个处理器token_holder = (token_holder + 1) % num_processorstime.sleep(1)  # 模拟非临界区操作# 创建并启动线程
threads = []
for i in range(num_processors):t = threading.Thread(target=processor_task, args=(i,))threads.append(t)t.start()# 等待所有线程完成
for t in threads:t.join()
分析

工作流程

  1. 每个处理器在循环中等待令牌。
  2. 一旦获取到令牌,处理器进入临界区执行任务。
  3. 完成临界区任务后,处理器将令牌传递给下一个处理器。
  4. 重复上述过程,确保所有处理器都有机会进入临界区。

注意事项

  • 令牌的初始化:需要在系统初始化时生成唯一令牌,并赋予某个处理器。
  • 令牌恢复机制:需要设计令牌丢失检测和恢复机制,以应对令牌在传递过程中丢失的情况。

 

这篇关于多处理机操作系统:进程同步的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1081276

相关文章

【Linux进阶】UNIX体系结构分解——操作系统,内核,shell

1.什么是操作系统? 从严格意义上说,可将操作系统定义为一种软件,它控制计算机硬件资源,提供程序运行环境。我们通常将这种软件称为内核(kerel),因为它相对较小,而且位于环境的核心。  从广义上说,操作系统包括了内核和一些其他软件,这些软件使得计算机能够发挥作用,并使计算机具有自己的特生。这里所说的其他软件包括系统实用程序(system utility)、应用程序、shell以及公用函数库等

【操作系统】信号Signal超详解|捕捉函数

🔥博客主页: 我要成为C++领域大神🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞👍收藏⭐评论✍️ 本博客致力于知识分享,与更多的人进行学习交流 ​ 如何触发信号 信号是Linux下的经典技术,一般操作系统利用信号杀死违规进程,典型进程干预手段,信号除了杀死进程外也可以挂起进程 kill -l 查看系统支持的信号

操作系统实训复习笔记(1)

目录 Linux vi/vim编辑器(简单) (1)vi/vim基本用法。 (2)vi/vim基础操作。 进程基础操作(简单) (1)fork()函数。 写文件系统函数(中等) ​编辑 (1)C语言读取文件。 (2)C语言写入文件。 1、write()函数。  读文件系统函数(简单) (1)read()函数。 作者本人的操作系统实训复习笔记 Linux

HarmonyOS NEXT:华为开启全新操作系统时代

在全球科技浪潮的汹涌澎湃中,华为再次以创新者的姿态,引领了一场关于操作系统的革命。HarmonyOS NEXT,这一由华为倾力打造的分布式操作系统,不仅是对现有技术的一次大胆突破,更是对未来智能生活的一次深邃展望。 HarmonyOS NEXT并非简单的迭代升级,而是在华为多年技术积淀的基础上,对操作系统的一次彻底重构。它采用微内核架构,摒弃了传统的宏内核模式,实现了模块化和组件化的设计理念

Linux操作系统段式存储管理、 段页式存储管理

1、段式存储管理 1.1分段 进程的地址空间:按照程序自身的逻辑关系划分为若干个段,每个段都有一个段名(在低级语言中,程序员使用段名来编程),每段从0开始编址。内存分配规则:以段为单位进行分配,每个段在内存中占连续空间,但各段之间可以不相邻。 分段系统的逻辑地址结构由段号(段名)和段内地址(段内偏移量)所组成。 1.2段表 每一个程序设置一个段表,放在内存,属于进程的现场信息

【Linux详解】冯诺依曼架构 | 操作系统设计 | 斯坦福经典项目Pintos

目录 一. 冯诺依曼体系结构 (Von Neumann Architecture) 注意事项 存储器的意义:缓冲 数据流动示例 二. 操作系统 (Operating System) 操作系统的概念 操作系统的定位与目的 操作系统的管理 系统调用和库函数 操作系统的管理: sum 三. 系统调用实现示例:Pintos 项目 Step 1:进入 examples 目录 St

【操作系统】第五章 文件系统

文件系统 5.1 概述5.2 文件5.2.1 文件及文件系统的概念5.2.2 文件的属性5.2.3 文件的操作5.2.4 文件的类型文件分类 5.3 文件的访问文件的逻辑结构顺序文件顺序文件的优缺点 索引文件索引顺序文件直接文件和哈希文件 5.4 文件保护5.5 文件目录文件控制块(FCB)文件控制块的内容 文件目录索引结点磁盘索引结点内存索引结点 单级目录结构二级目录结构多级目录结构

Linux|操作系统运维|磁盘性能检测之fio和iostat的初步使用

前言: 有的时候,我们接手一个新的服务器的时候,需要了解该服务器的磁盘性能是否可靠,比如,磁盘是否有坏道,磁盘的读写性能是否能够符合我们将要部署的服务,例如数据库服务,如果该数据库是一个读写比较频繁也就是IO比较高的数据库,那么,该磁盘是否能够支持高IO呢? 针对以上需求,建议使用工具fio和iostat这两个工具 一、 iostat在centos7下的安装 配置update源即可 i

软件设计师笔记-操作系统知识

操作系统的作用 操作系统(Operating System, OS)在计算机系统中扮演着至关重要的角色。通过资源管理提高计算机系统的效率;改善人机界面,向用户提供友好的工作环境。 通过资源管理提高计算机系统的效率: 处理器管理:操作系统负责管理和调度计算机的中央处理器(CPU)资源。它使用各种调度算法(如先来先服务、最短作业优先、优先级调度等)来确定哪个进程或线程应该在何时获得CPU的使用权

操作系统真象还原:用户进程

第11章-用户进程 这是一个网站有所有小节的代码实现,同时也包含了Bochs等文件 11.1 为什么要有任务状态TSS Linux 任务切换未采用 Intel 的做法,而是用了一套自己的方法,只是用了 TSS 的一小部分功能。 操作系统最直接控制的就是 CPU,要想让 CPU 这颗奔腾的心永远地跳下去,首先必须把内存分成段,把内存按“内存块”访问,其次必须让代码段寄存器 cs 和指令寄存器