你会说二进制吗?你能理解机器代码吗?如果我给你一张写满1和0的纸,你能告诉我这是什么意思吗?如果你要去一个你从未去过的国家,说一种你从未听过的语言,或者你可能听说过它,但实际上并不会说它,那么在那里你需要什么来帮助你与当地人沟通?
你需要一个翻译。您的操作系统在您的PC中充当翻译器。它将那些1和0、是/否、开/关的值转换成您可以理解的可读语言。它在一个流线型的图形用户界面(GUI)中完成所有这些工作,您可以通过鼠标点击移动东西,移动它们,看到它们在您眼前发生。
对于任何认真的软件开发人员来说,了解操作系统是如何工作的都是基本的,也是至关重要的。不应该试图绕过它,任何告诉你没有必要的人都应该被忽视。虽然知识的广度和深度可能会受到质疑,但了解比基础知识更多的知识对于您的程序运行得有多好,甚至对它的结构和流程可能是至关重要的。
为什么?当你写了一个程序,它运行得太慢了,但是你看不到你的代码有什么问题,你还会去哪里寻找解决方案呢?如果你不知道操作系统是如何工作的,你怎么能调试这个问题呢?您是否访问了太多文件?内存不足且交换空间使用率高吗?但你甚至不知道什么是掉期!还是I/O阻塞?
并且您想要与另一台机器通信。你是如何在本地或通过互联网做到这一点的?那有什么不同呢?为什么有些程序员喜欢一种操作系统而不是另一种?
为了成为一名认真的开发人员,我最近选修了佐治亚理工学院的课程“操作系统入门”。它讲授基本的操作系统抽象、机制及其实现。本课程的核心内容包括并发编程(线程和同步)、进程间通信和分布式操作系统简介。我想用这篇文章来分享我从这门课程中学到的东西,这是如果你想要擅长开发软件,你需要学习的10个关键的操作系统概念。
但首先,让我们定义一下操作系统是什么。操作系统(OS)是管理计算机硬件并为程序提供服务的软件集合。具体地说,它隐藏了硬件复杂性,管理计算资源,并提供隔离和保护。最重要的是,它直接拥有对底层硬件的特权访问。操作系统的主要组件是文件系统、调度程序和设备驱动程序。您以前可能同时使用过桌面(Windows、Mac、Linux)和嵌入式(Android、iOS)操作系统。
操作系统有3个关键元素:(1)抽象(进程、线程、文件、套接字、内存),(2)机制(创建、调度、打开、写入、分配),以及(3)策略(LRU、EDF)
操作系统设计原则有两个:(1)通过实现灵活的机制来支持策略,实现机制和策略的分离;(2)针对常见情况进行优化:操作系统将在哪里使用?用户希望在该计算机上执行什么操作?工作负载要求是什么?
现在通常使用的3种操作系统是:(1)单片OS,其中整个OS在内核空间中工作,并且在管理模式下是独立的;(2)模块化OS,其中系统核心的某些部分将位于称为模块的独立文件中,可以在运行时添加到系统中;以及(3)Micro OS,其中内核被分解成单独的进程,称为服务器。一些服务器运行在内核空间,另一些服务器运行在用户空间。
进程基本上就是执行中的程序。流程的执行必须按顺序进行。简单地说,我们把我们的计算机程序写在一个文本文件中,当我们执行这个程序时,它就变成了一个执行程序中提到的所有任务的过程。
当一个程序加载到内存中并成为一个进程时,它可以分为四个部分:─堆栈、堆、文本和数据。下图显示了主内存中进程的简化布局。
堆栈:进程堆栈包含方法/函数参数、返回地址、局部变量等临时数据。
文本:这包括由程序计数器的值和处理器寄存器的内容表示的当前活动。
当进程执行时,它会经过不同的状态。这些阶段在不同的操作系统中可能不同,并且这些状态的名称也没有标准化。通常,进程一次可以具有以下五种状态之一:
就绪:进程正在等待分配给处理器。就绪进程正在等待操作系统为其分配处理器,以便它们可以运行。进程可能在开始状态之后进入此状态,或者在由调度器运行但被调度器中断以将CPU分配给某个其他进程时进入此状态。
正在运行:一旦操作系统调度程序将进程分配给处理器,进程状态就被设置为正在运行,并且处理器执行其指令。
正在等待:如果需要等待资源(例如等待用户输入或等待文件变为可用),流程将进入等待状态。
终止或退出:一旦进程完成执行,或者被操作系统终止,它就会进入终止状态,等待从主内存中删除。
进程控制块是由操作系统为每个进程维护的数据结构。PCB由整数进程ID(PID)标识。PCB保存跟踪工艺所需的所有信息,如下所示:
流程状态:流程的当前状态,即它是否就绪、正在运行、正在等待等等。
程序计数器:程序计数器是指向此进程要执行的下一条指令地址的指针。
CPU寄存器:各种CPU寄存器,运行状态下需要存储进程执行的CPU寄存器。
CPU调度信息:调度进程所需的进程优先级等调度信息。
内存管理信息:包括页表、内存限制、段表等信息,具体取决于操作系统使用的内存。
记账信息:包括进程执行占用的CPU数量、时间限制、执行ID等。
线程是通过进程代码执行的流,具有自己的程序计数器(跟踪下一条要执行的指令)、系统寄存器(保存其当前工作变量)和堆栈(包含执行历史记录)。
线程与其对等线程共享的信息很少,如代码段、数据段和打开的文件。当一个线程更改代码段内存项时,所有其他线程都会看到。
线程也称为轻量级进程。线程提供了一种通过并行性提高应用程序性能的方法。线程代表了一种通过减少开销来提高操作系统性能的软件方法,线程相当于经典进程。
每个线程只属于一个进程,任何线程都不能存在于进程之外。每个线程代表一个单独的控制流。线程已成功应用于网络服务器和Web服务器的实现。它们还为在共享内存多处理器上并行执行应用程序提供了合适的基础。
在这种情况下,线程管理内核不知道线程的存在。线程库包含用于创建和销毁线程、用于在线程之间传递消息和数据、用于调度线程执行以及用于保存和恢复线程上下文的代码。应用程序以单个线程启动。
在这种情况下,线程管理由内核完成。应用程序区域中没有线程管理代码。操作系统直接支持内核线程。任何应用程序都可以编程为多线程。在单个进程中支持应用程序内的所有线程。
内核维护整个进程的上下文信息以及进程内各个线程的上下文信息。内核的调度是以线程为基础进行的。内核在内核空间中执行线程创建、调度和管理。内核线程的创建和管理通常比用户线程慢。
如果进程中的一个线程被阻塞,内核可以调度同一进程的另一个线程。
在同一进程内将控制权从一个线程转移到另一个线程需要将模式切换到内核。
进程调度是进程管理器的活动,其处理从CPU移除正在运行的进程以及基于特定策略选择另一个进程。
进程调度是多道程序设计操作系统的重要组成部分。这样的操作系统允许一次将一个以上的进程加载到可执行存储器中,并且加载的进程使用时间复用共享CPU。
操作系统维护进程调度队列中的所有进程控制块(PCB)。OS为每个进程状态维护单独的队列,并且处于相同执行状态的所有进程的PCB被放置在相同的队列中。当进程的状态改变时,其PCB将从其当前队列解除链接,并移至其新状态队列。
Ready Queue−此队列保持一组驻留在主内存中的所有进程,准备好并等待执行。新进程总是放入此队列中。
设备队列−由于I/O设备不可用而阻塞的进程构成此队列。
操作系统可以使用不同的策略来管理每个队列(FIFO、循环调度、优先级等)。操作系统调度程序确定如何在就绪队列和运行队列之间移动进程,这两个队列在系统上的每个处理器核心只能有一个条目;在上图中,它已与CPU合并。
运行中:当创建新的进程时,它以运行状态进入系统。
未运行:未运行的进程保留在队列中,等待轮到它们执行。队列中的每个条目都是指向特定进程的指针。队列使用链表实现。Dispatcher的用法如下。当进程中断时,该进程在等待队列中传输。如果该进程已完成或中止,则该进程将被丢弃。在这两种情况下,调度程序然后从队列中选择要执行的进程。
上下文切换是在进程控制块中存储和恢复CPU的状态或上下文的机制,以便稍后可以从相同的点恢复进程执行。使用此技术,上下文切换器允许多个进程共享单个CPU。上下文切换是多任务操作系统功能的重要组成部分。
当调度器将CPU从执行一个进程切换到执行另一个进程时,来自当前运行进程的状态被存储到进程控制块中。此后,将从其自己的PCB加载下一步运行的进程的状态,并用于设置PC、寄存器等。此时,第二个进程可以开始执行。
上下文切换是计算密集型的,因为必须保存和恢复寄存器和存储器状态。为了避免上下文切换时间量,一些硬件系统使用两组或更多组处理器寄存器。当进程切换时,将存储以下信息以供以后使用:程序计数器、调度信息、基址和限制寄存器值、当前使用的寄存器、更改的状态、I/O状态信息和记帐信息。
内存管理是操作系统的功能,它处理或管理主内存,并在执行期间在主内存和磁盘之间来回移动进程。内存管理跟踪每个内存位置,无论它是分配给某个进程还是空闲。它检查要分配给进程的内存量。它决定哪个进程将在什么时间获得内存。它跟踪某些内存何时被释放或未分配,并相应地更新状态。
进程地址空间是进程在其代码中引用的一组逻辑地址。例如,当使用32位寻址时,地址的范围从0到0x7fffffff;即2³?可能的数字,理论总大小为2 GB。
操作系统负责在向程序分配内存时将逻辑地址映射到物理地址。在分配内存之前和之后,程序中使用的地址有三种类型:
符号地址:源代码中使用的地址。变量名、常量和指令标签是符号地址空间的基本元素。
相对地址:在编译时,编译器将符号地址转换为相对地址。
物理地址:加载程序在将程序加载到主内存时生成这些地址。
在编译时和加载时地址绑定方案中,虚拟地址和物理地址是相同的。虚拟地址和物理地址在执行时地址绑定方案中不同。
由程序生成的所有逻辑地址的集合被称为逻辑地址空间。对应于这些逻辑地址的所有物理地址的集合被称为物理地址空间。
一个流程可以有两种类型:独立流程和协同流程。独立进程不受其他进程执行的影响,而合作进程可能受其他执行进程的影响。虽然人们可以认为那些独立运行的进程将非常高效地执行,但实际上,在许多情况下,可以利用协作的性质来提高计算速度、便利性和模块性。进程间通信(IPC)是一种允许进程相互通信并同步其操作的机制。这些进程之间的通信可以被视为它们之间合作的一种方法。进程可以使用这两种方式相互通信:共享内存和消息解析。
有两个过程:生产者和消费者。生产者生产一些商品,消费者消费该商品。这两个进程共享一个称为缓冲区的公共空间或内存位置,生产者生产的物品在这里存储,如果需要,消费者可以从这里消费物品。这个问题有两种版本:第一种称为无限缓冲问题,生产者可以继续生产物品,并且缓冲的大小没有限制;第二种称为有界缓冲问题,生产者可以生产一定数量的物品,然后开始等待消费者消费。
在有界缓冲问题中:首先,生产者和消费者将共享一些公共内存,然后生产者将开始生产项目。如果总的生产量等于缓冲区的大小,生产者将等待消费者消费它。类似地,消费者首先检查商品的可用性,如果没有商品可用,消费者将等待生产商生产它。如果有商品可用,消费者就会消费。
在此方法中,进程之间无需使用任何类型的共享内存即可进行通信。如果两个进程p1和p2想要彼此通信,则它们按如下方式进行:
建立通信链路(如果链路已存在,则无需再次建立。)。
开始使用基本原语交换消息。我们至少需要两个原语:Send(Message,Destination)或Send(Message)和Receive(Message,host)或Receive(Message)。
消息大小可以是固定大小,也可以是可变大小。如果是固定大小,对操作系统设计人员来说很容易,但对程序员来说很复杂;如果它是可变大小的,那么对于程序员来说很容易,但对操作系统设计人员来说很复杂。标准消息可以有两个部分:头和正文。
报头部分用于存储报文类型、目的ID、源ID、报文长度和控制信息。控制信息包含缓冲器空间用完时要做什么、序列号、优先级等信息。一般情况下,报文采用FIFO方式发送。
操作系统的重要工作之一是管理各种I/O设备,包括鼠标、键盘、触摸板、磁盘驱动器、显示适配器、USB设备、位图屏幕、LED、模数转换器、开/关开关、网络连接、音频I/O、打印机等。
I/O系统需要接收应用程序I/O请求并将其发送到物理设备,然后获取设备返回的任何响应并将其发送到应用程序。I/O设备可以分为两类:
块设备-块设备是驱动程序通过发送整个数据块与之通信的设备。例如硬盘、USB摄像机、盘上钥匙等。
字符设备-字符设备是驱动程序通过发送和接收单个字符(字节、二进制八位数)与之通信的设备。例如串口、并口、声卡等。
CPU必须有一种向I/O设备传递信息和从I/O设备传递信息的方法。有三种方法可用于与CPU和设备通信。
这使用专门用于控制I/O设备的CPU指令。这些指令通常允许将数据发送到I/O设备或从I/O设备读取数据。
使用内存映射I/O时,内存和I/O设备共享相同的地址空间。该设备直接连接到某些主存储器位置,以便I/O设备无需通过CPU即可将数据块传输到存储器或从存储器传输数据块。
在使用内存映射IO时,操作系统分配内存中的缓冲区,并通知I/O设备使用该缓冲区将数据发送到CPU。I/O设备与CPU异步运行,完成后中断CPU。
这种方法的优点在于,可以访问存储器的每条指令都可以用来操作I/O设备。内存映射IO用于大多数高速I/O设备,如磁盘、通信接口。
键盘等速度较慢的设备在传输完每个字节后都会对主CPU产生中断。如果快速设备(如磁盘)为每个字节生成一个中断,则操作系统将花费大部分时间来处理这些中断。因此,典型的计算机使用直接内存访问(DMA)硬件来减少此开销。
直接内存访问(DMA)是指CPU授予I/O模块读取或写入内存的权限,而无需参与。DMA模块本身控制主存储器和I/O设备之间的数据交换。CPU仅在传输的开始和结束时参与,并且仅在整个数据块传输完成后中断。
直接存储器访问需要一种称为DMA控制器(DMAC)的特殊硬件来管理数据传输并仲裁对系统总线的访问。控制器采用源和目标指针(读/写数据的位置)、跟踪传输字节数的计数器和设置(包括CPU周期的I/O和存储器类型、中断和状态)进行编程。
虚拟化是允许您从单个物理硬件系统创建多个模拟环境或专用资源的技术。称为虚拟机管理程序的软件直接连接到该硬件,并允许您将一个系统拆分成称为虚拟机(VM)的独立、不同且安全的环境。这些虚拟机依赖于虚拟机监控程序分隔机器资源的功能。
.