操作系统原理

本篇通俗解释操作系统部分重要内容,个人理解较多,虚心接受指教

目录

操作系统结构

linux内存原理

进程与线程

文件系统和设备管理


 

 

操作系统结构

本章介绍操作系统结构以及用户态,内核态

拿linux操作系统举例,先上图:

9f6e584b56f147be9aac2ab6d712d721.png

这是一张linux操作系统结构图,图中双箭头部分就是操作系统核心

先介绍其他部分:

应用程序:也称用户级程序,就是手机,电脑上我们所能直接使用的程序

SHELL:提供用户与内核直接交互的接口,接收用户的指令送入内核执行

文件系统:在Linux中一切皆文件这句话耳熟能详,Linux 操作系统将独立的文件系统组合成了一个层次化的树形结构,并且由一个单独的实体代表这一文件系统。Linux  将新的文件系统通过一个称为“挂装”或“挂上”的操作将其挂装到某个目录上,从而让不同的文件系统结合成为一个整体。Linux  操作系统的一个重要特点是它支持许多不同类型的文件系统。

操作系统结构

操作系统结构主要分为四部分:内核,系统调用,用户空间,系统空间

d48c8e68b98144a5809bb4f6b605f58d.png

内核:操作系统的核心部分,它有两个任务,管理系统硬件资源提供基本服务。它控制文件系统,内存管理,进程管理,提供网络服务等。其在特权模式下可以直接访问底层硬件资源,并且提供了一个安全环境以便程序通过系统调用访问资源

系统调用:系统调用接口是用户程序和内核之间的接口,允许用户程序请求操作系统提供的服务。这些服务包括文件操作、进程管理、内存管理等。通过系统调用,用户程序可以利用操作系统的功能而不需要了解底层硬件的细节,从而实现了对硬件的抽象和封装

用户空间:操作系统提供给用户程序运行的空间,通过系统调用与内核通信

系统空间:操作系统内核的运行空间,可以直接访问硬件资源

什么是用户态与内核态?为什么要有用户态和内核态呢?怎么切换用户态和内核态(变态)呢?

什么是用户态与内核态?

用户态和内核态是操作系统的两种运行级别

cc7ba5f45e774c4391b16888552d791b.png

用户态:程序在没有特权访问硬件资源的情况下运行的状态

内核态:有对硬件资源直接访问权的,内核运行的特权模式

在内存资源上操作系统为这两种状态划分了用户空间与系统空间(见上文)

为什么要分用户态,内核态?

两个字:安全。拿车来打比方,我们在没有完全了解车的所有底层结构的情况下,依然能驾驶汽车,是因为通过踏板来控制汽车加油,减速,如果让你不用踏板直接控制脚下的那三个东西,把油门当刹车的概率有多大?所以,我们能看到的,直接使用的汽车控件是安全层次最高的,再回到操作系统,如果直接让用户控制硬件,一旦错用,将会造成毁灭级打击,系统崩溃都是最轻的。所以得划分,而且必须划分

怎么切换用户态和内核态?

 

系统调用,中断,异常处理

系统调用(System Call):

用户程序通过系统调用接口请求操作系统提供的服务。
操作系统将用户程序的请求转化为特定的内核函数,然后通过一条特殊的指令(通常是int指令或syscall指令)切换到内核态。
在内核态中,操作系统执行相应的内核函数,完成用户程序请求的操作。
执行完毕后,操作系统通过返回值或状态将结果返回给用户程序,并再次通过特殊指令切换回用户态。


中断(Interrupt):

硬件设备或其他外部事件可能会引发中断,使处理器从用户态切换到内核态。
当发生中断时,处理器会保存当前执行状态的上下文,并跳转到事先定义好的中断服务程序(Interrupt Service Routine,ISR)。
在内核态中,ISR会处理中断事件,可能涉及设备驱动程序的调用、数据处理等。
处理完中断后,处理器会恢复之前保存的用户态上下文,继续用户程序的执行。


异常处理(Exception):

异常是由于程序错误或非法操作引起的,如除零错误、访问越界等。
当发生异常时,处理器会暂停当前执行的程序,并跳转到异常处理程序。
异常处理程序运行在内核态,可以对异常进行处理,例如终止程序或修复错误。
处理完异常后,处理器将控制权返回到用户态,继续执行用户程序。

切换过程:

保留用户态现场(上下文、寄存器、用户栈等)
复制用户态参数,用户栈切到内核栈,进入内核态
额外的检查(因为内核代码对用户不信任)
执行内核态代码
复制内核态代码执行结果,回到用户态
恢复用户态现场(上下文、寄存器、用户栈等)

可见切换开销之大

 

linux内存原理

内存的作用

首先,我们的程序与数据是存放在外存(辅存)的。由于CPU能直接访问内存(主存)而非外存,所以程序若要运行则必须载入内存中,包括操作系统也是(操作系统是在计算机上电时就载入内存,是计算机启动的第一个程序)。因此,内存其实就相当于一个流水线的工作区域,任务流到此处,工人(CPU)加工,送走。都是过客。。。但一般情况下,内存小而外存大,如何使用内存来分配给多个进程(swap),如何管理内存,这是操作系统一个重要任务

虚拟地址和物理地址

程序载入内存后如何运行呢?四个字:取指执行。

然而,既然要执行,那必须涉及地址问题了,倘若使用真实的也就是物理地址,在多个程序想要载入时地址段占用冲突,阁下该如何应对,假如程序有一段使用0地址处,那是一些鼠辈能碰的吗?(操作系统:大胆!!!)

由此,为避免冲突和以下犯上,逻辑地址出场:我们需要找一段空闲地址将程序载入,同时修改程序中的地址,这种操作叫重定位,修改后的地址叫逻辑地址(虚拟地址)

所以程序使用的地址是虚拟地址,而硬件中的真实空间叫物理地址

运行时重定位

上文讲到重定位,一个程序重定位后能够正常执行,但若在多进程中,多个进程换入换出,你能保证它们重定位后的地址灵活变换吗?如果都使用其本身之前使用过的地址,怎么保证这段地址不会被其他进程占用呢?

其实重定位分为载入时重定位,编译时重定位,和运行时重定位。

只有在运行时重定位(字面意思也能理解),程序的地址才能根据空闲空间灵活变换。那么它是怎么做出这种骚操作的呢?四个字:地址翻译。

73c81e5b425646afa22810df8d62e8b6.png

程序使用内存,重定位,执行过程如下:

  1. 首先创建进程,PCB(存放进程号,进程状态信息等)
  2. 在内存中找到一段空闲内存存入PCB,这段空闲地址的起始地址也赋给PCB
  3. 将程序放入内存。
  4. 之后每执行一条指令根据地址翻译base+offset找到该指令物理地址。

经过运行时重定位,在某一段程序某一时间段不运行时,其使用过的地址也能空出来供其他进程使用。(高!实在是高!)

分段机制

我们已经知道了,程序放入内存中执行,那么每个程序都是整个独立的放进内存中的吗?非也!

一般来说,程序中的代码由若干段组成,分堆,栈,数据段等等,有的只读,有的可写,各有特点,如果我们将其混在一起,不小心将只读段改写,寄。倘若堆栈使用时要挪动大段程序,寄。所以为了处理不同段,采用分治方法,引入分段机制:就是将程序根据特性分段存放于内存。

aea55e68bc3f424094ea387d03de5266.png

由于要分段,那么在重定位时PCB就不是存放一个程序的base了,要存放每段的base

419edade53734bf8a31b9a43f54930cb.png

分页机制

接下来我们考虑内存的分配:

920e4881f8844480b3e23aa6ac23278f.png

如果我们不进行处理,按照粗鄙的方式放入程序段,就会产生如上图所示的处于尴尬境地的内存碎片,其不能放入较大甚至正常大小的程序段,若置之不理,在庞大的计算机系统中如产生了大量内存碎片,内存利用率大跌,这对聪明的操作系统人是绝对不允许的

所以我们引入分页机制

一个字:书。书是一页一页的,每个章节分有若干页,每一页都要内容,无浪费。分页机制也是如此:在操作系统启动时就将内存分页,一页规定固定的较小的内存,就算一页分配一个字,浪费也是很小的。既然分段有迹可循,分页更是,分页机制建立页表,根据页表重定位算出地址

年轻人,你以为就到此为止了吗?不不不!!!

四个字:页小表大。毋庸置疑,如果我们将页分小,那么查找的时候工作量大到无可奈何(一个数字你就怕了:100000000),有迹可循的问题解决了,我们来解决措手不及的问题

回到那个字:书。书是有目录,章节的兄弟们,也就是说为了解决查找时间长的问题,操作系统类比这种方法做出多级页表,也可以将它看成树结构,这样查找速度就蹭蹭往上涨咯。至此已成艺术,艺术源于生活

5c731ef7d3164b50b77f864fc612fe42.png

另外,我们看书若没看完,会折印或做标记,这样我们下次翻书就能很快找到,同样,快表也被设计出来,快表能存放最近经常访问的地址,那么我们就不用每次都查表了,效率也就提高了

linux内存管理

上文我们了解到,分段与分页都是刚需。那么我们如何将其结合来管理内存呢?还记得那两张图吗?这次我来当个缝合怪:

c8f54378596a43d88598f0b3c448c43f.png

相信各位已经看懂了,这就是段页结合的实际内存管理,面对程序员,分段的功能有,面对物理内存,页的功能有,那么原来分段的内存就让虚拟内存来代替了

那么重定位又要重新讨论了:其他部分不变,在进行地址翻译时:先通过段号+偏移量找到逻辑地址,再通过分页机制找页号+偏移量找到虚拟地址,最后通过物理页号+偏移量找到物理地址

进程与线程

进程的概念

在多道程序环境下,程序的执行属于并发执行,因此它们会失去封闭性,并具有间断性和运行结果不可再现性。通常,程序不能参与并发执行,否则程序的执行就失去了意义。而如今我们都知道,程序并发执行是基本要求,怎么做呢?进程就出来了

进程是程序的一次执行,是一个程序及其数据在处理机上顺序执行时所发生的活动(我喜欢把它比作做一餐饭的过程)

eb28171e7e8a41ef83a05cb3be8124a8.png

我想也是时候介绍PCB了:PCB是专门为进程设计的数据结构,系统利用PCB描述进程的基本情况和活动过程,进而控制管理进程。创建进程实质上是创建进程的PCB

一个进程实体是由程序段,相关数据段和PCB这三部分构成

线程的概念

进程作为一个独立运行的基本单位,在多进程频繁上下文切换的情况将付出大量时空开销,为解决或缓解这种问题,线程被提出

线程是进程中的一个执行任务,一个进程包括 >=1 个线程(相当于做一道道菜)

3d1f211bd364483b8017fee7c465ac20.png

进程有PCB,线程当然也有TCB,存放了用于控制和管理线程的信息

进程与线程的比较

  1. 调度的基本单位:进程上下文切换开销较大;线程切换仅需保存设置少量寄存器内容,开销小
  2. 并发性:进程之间可以并发执行,进程中的多个线程甚至所有线程都可以并发执行
  3. 拥有资源:进程可以拥有资源(基本单位);线程几乎不拥有资源
  4. 独立性:不同进程间的独立性较强(防止进程间彼此干扰破坏,进程的地址空间和资源独有);同一进程的不同线程间独立性较弱(为提高并发性以及满足进程间的合作需求,可以共享进程的内存地址空间和资源)
  5. 系统开销:进程开销大(系统要为进程分配或回收PCB和资源);线程开销小
  6. 根本区别:进程是操作系统资源分配的基本单位;线程是处理器任务调度和执行的基本单位

总的来说,在线程提出之前的进程就相当于一个单线程任务的进程(重型进程),做的那一餐饭是蛋炒饭,而多线程概念来了之后,现在改三菜一汤了。这是因为之前做菜要频繁换厨房,而如今做啥就可以在一个厨房里面完成。

进程间的通信方式

每个进程都有自己独立的用户空间,而若要进行进程与进程间的数据交换,通信,则必须通过内核,在内核中开辟一块缓冲区,两个进程通过这个缓冲区进行数据交换,而内核提供的这种机制叫进程间通信

88e7393a65cd403187348639f46a00b5.png

在linux中进程通信有一下6种通信方法:

管道

管道是最古老的一种方法,分为无名管道(pipe,用于父进程与子进程间通信)和命名管道(fifo,同一台机器任意两个进程间通信)

85f72725857d47b18b96c60511f5d7d3.png

在管道中数据只能单向流动,有固定的读写端

命名管道与无名管道区别甚小,命名管道有磁盘索引节点,就是有名字,这样任何两个进程都可以通过它建立通信。而无名管道因为名声不大,只在父子进程间才使用

信号

信号是软件中断,是进程间相互传递消息的一种方法,用于通知进程发生了事件,但不能给进程传递任何数据。可以在任何时候发送给某一进程,而无须知道该进程的状态。

消息队列

消息队列用在运行同一台机器上的进程通信中,在系统内核中以消息链表的形式出现,进程若有足够权限则可以在消息队列中添加消息,读取消息。消息队列克服了管道只能承载无格式字节流以及缓冲区大小受限,信号承载信息量少等缺点。

2b2f9e0308e9412884f039c831e5a722.png

共享内存

共享内存可以使同一台机器上进程间的通信最快,因为数据无须在不同的进程间进行复制,通常由一个进程在内存中创建一个共享存储区,其余进程使用。

共享内存实现方式有两种:一是内存映射,此法直接操作物理内存,而这种敏感的东西一般不会使用。二是共享内存机制,在实际开发中一般使用shmXXX函数族,其原理就是一个进程使用函数创建一个共享存储区,其他进程再通过连接的方式将这个存储区连接到自身的地址空间

8acb6b7f077b49219d4b8567e851a8e4.png

信号量

如果某进程在访问共享内存时其他进程也来访问,那么先来的进程写的内容就会被后来的覆盖,并且资源是有限的,为解决这些问题,我们可以采用计数器控制多个进程对共享资源的访问,这就是信号量,也类似一种锁机制,用于进程同步。

信号量有两种操作:

  • P(wait):将信号量的值-1,如果信号量的值为0,将阻塞等待,直到信号量的值大于0
  • V(post):将信号量的值+1,任何时候都不会阻塞

应用场景:约定0或1可实现互斥锁;生产者/消费者模型

套接字

套接字(Socket)用于异地计算机进程间通信(也可用于本地),主要是跨网络与不同主机上的进程进行通信。

Socket是对TCP/IP封装后的一个API,对用户来说,只要通过一组简单的 API 就可以实现网络的连接。

123549cc0b8241b59931a9f9eef687b5.png

套接字连接原理:

服务端:

  1. 应用程序创建一个socket,进程给socket命名
  2. 进程等待客户连接此socket
  3. 创建一个队列存放客户的进入连接,服务器用accept接受连接
  4. 创建一个新socket与此客户通信,原socket继续等待客户连接

客户端:

  1. 创建一个未命名socket
  2. 将服务器的命名socket作为地址建立连接

线程同步

线程同步的概念:线程同步就是在一个线程对某个内存地址操作时,其他线程都不能对这个地址操作,直到该线程完成操作

互斥量

在linux中提供一把互斥锁mutex(互斥量),在线程对资源操作前都尝试上锁,成功后就可操作,操作完就解锁,这样就实现了线程竞争,通过互斥量将资源访问变为互斥操作,避免时间上的错误

06564383b21640eeadc182ccafb7c270.png

信号量

同进程间通信的信号量原理相同

死锁

许多应用中进程需要以独占的方式访问资源,当操作系统允许多个进程(线程)并发执行时可能会出现进程永远被阻塞现象,如两个进程(线程)分别等待对方所占的资源,于是两者都不能执行而处于永远等待状态,此现象称为死锁。

死锁产生的原因:

  • 资源竞争:比如CPU可由优先级高的剥夺优先级低的处理机,再比如A等待B使用的资源,B等待A使用的资源(转盘上的恋人)。
  • 进程(线程)推进顺序不当:运行过程中,请求和释放资源的顺序不当,而导致死锁

死锁产生的条件:

  • 互斥条件:临界资源是独占资源,进程应互斥且排他的使用这些资源。
  • 占有等待条件:进程在请求资源得不到满足而等待时,不释放已占有资源。
  • 不剥夺条件:又称不可抢占,已获资源只能由进程自愿释放,不允许被其他进程剥夺。
  • 循环等待条件:又称环路条件,存在循环等待链,其中,每个进程都在等待链中等待下一个进程所持有的资源,造成这组进程处于永远等待状态。

死锁解决的方法:

  • 死锁防止:破坏四个死锁产生的条件中的至少一项
  • 死锁避免:会降低系统并发性,导致低效的资源利用率,使用银行家算法等
  • 死锁检测:进程-资源分配图中有环路图且每种资源类中仅有一个资源,则系统一定发生了死锁。每种资源类中有多个资源,则环路的存在只是产生死锁的必要不充分条件,系统未必会发生死锁
  • 死锁恢复:1)剥夺陷于死锁的进程所占用的资源,但并不撤销此进程,直至死锁解除。2)撤销陷入死锁的所有进程,解除死锁,继续运行。或逐个撤销陷入死锁的进程,回收其资源并重新分配,直至死锁解除。3)根据系统保存的检查点让所有的进程回退,直到足以解除死锁,这种措施要求系统建立保存检查点、回退及重启(重启解决99%的问题)。

文件系统和设备管理

文件系统

文件系统是操作系统一个重要的子系统,负责管理持久化数据,“一切皆文件”,这是Linux中广为流传的一句话,文件,目录,甚至块设备等都由文件系统管理。

在Linux中,文件系统通常由以下几个部分组成:

  • 超级区块:存储文件系统的整体信息,包括inode与数据区块的总量,使用量,剩余量,以及文件系统的格式与相关信息
  • inode:每个文件或目录都有一个唯一的inode,用于存储文件或目录的元数据信息,如文件的权限、所有者、文件大小、数据块的指针等。一个文件占用一个inode,同时记录此文件数据所在区块号码
  • 数据区块:用于存储文件的实际数据内容,包括文件的文本、图片、视频等。若文件太大会占用多个区块
  • 目录项:存储在目录中的条目,每个目录项包含文件或子目录的文件名以及对应的Inode号
  • 目录文件:保存了目录项的文件,以树形结构组织文件系统的层次结构
  • 链接:包括硬链接和符号链接,用于创建文件之间的关联关系
  • 位图:记录数据块的使用情况,包括哪些数据块已被分配、哪些数据块是空闲的

以上都存放在硬盘中,但我们都知道在内存中操作比较高效,所以在实际情况下会将目录项等缓存到内存,而实现高效查找,并且可以减少硬盘访问次数

由超级区块,inode,数据区块,我们来大概了解一下文件系统的运行方式:就是通过inode找实际数据。因为实际数据存放在数据区块,而一个文件占用一个inode,自然就能通过找对应的文件inode而找到防止数据的区块号码,也就能找到实际数据了

cd2c630143b545d294de90a608b77b7b.png

文件的存储

linux文件存储是索引存放方式,在典型的文件系统中,多级索引更为常见,因为它更适用于管理大文件的存储,并且能够提供更快速的数据访问。多级索引结构包括直接块、间接块、双重间接块和三重间接块等级别,通过多级索引结构,文件系统可以高效地管理大文件的存储。这种结构允许文件系统通过一系列的索引指针来访问和管理数据块,从而实现高效的数据存储和访问。

ca3ef807d3f44355b6ad9762a03ef1b0.png

文件目录

常见的 Linux 文件系统(如 ext2、ext3、ext4 等)通常使用 B+ 树来存储目录结构。B+ 树是一种自平衡的树状数据结构,具有良好的平衡性和高效的查找性能。在 B+ 树中,每个节点包含多个子节点和对应的键值,用于快速定位和管理数据。B+ 树中的节点通常存储在磁盘上,节点的大小通常被设计为磁盘块的大小,以便于读取和写入操作。B+ 树的叶子节点存储了实际的目录项信息,包括文件名、Inode 号等,而非叶子节点仅包含键值和指向子节点的指针

0a0b5a391b5148b7bfedc8300db938bd.png

文件IO

IO就是input output,文件IO是指文件读写。在Linux中文件IO有系统IO和标准IO两种方式

标准IO:使用标准库函数对文件IO操作,如C语言的fopen(),fwrite(),fread(),fclose()等,标准IO提供高级易用的接口,其读写基于缓冲区,就是在读写过程中数据会先存放在内存缓冲区,然后通过系统调用操作缓冲区和磁盘,适用于常规文件操作

系统IO:使用系统调用函数对文件IO操作,如open(),read(),write(),close()等,这些系统调用函数更接近底层,能直接与内核交互,效率高,适用于需要更底层控制和更高性能的情况下

两者区别:

  • 性能: 标准IO的性能一般较低,因为它需要缓冲区的复制操作;而系统IO直接与内核交互,性能较高。
  • 缓冲: 标准IO使用缓冲区管理,可以提高文件读写的效率;而系统IO没有缓冲,需要频繁的系统调用来读取和写入数据。
  • 接口: 标准IO提供了更高级、更易用的接口,处理了更多细节,支持格式化输入和输出;而系统IO是更底层、直接的接口,更灵活,但也需要更多的代码来实现一些功能。
  • 文件描述符: 标准IO使用FILE结构表示打开的文件,并返回文件指针;而系统IO使用文件描述符表示打开的文件,使用整数标识。
  • 跨平台性: 标准IO函数是C库标准的一部分,具有跨平台性,可在不同操作系统上使用;而系统IO函数依赖于操作系统提供的具体实现,可能在不同操作系统上有差异。

在选择使用标准IO还是系统IO时,需要根据具体的需求和性能要求来决定。大多数情况下,标准IO是更常用和更方便的选择,而对于特定的性能优化或底层控制的需求,系统IO可以提供更灵活和高性能的解决方案。

IO控制方式

IO操作指将数据从外部设备输入到计算机或将计算机中的数据输出到外部数据。而IO控制方式就是管理操作输入输出设备的方式

IO控制方式有如下四种:

程序控制方式:就是由软件使用操作系统提供的文件读写函数或设备驱动程序控制输入输出操作,更加灵活但需要更多CPU时间

中断控制方式:输入输出设备向CPU发送中断信号,然后处理器停止当前任务,响应信号,快速处理输入输出操作,但如果单次数据需求较大,那么数据在IO过程会被划分为很多小块,CPU中断处理次数增加,频繁上下文切换(这样遭不住的牢底)

DMA控制方式:设备直接访问内存,不需要处理器介入,减少了处理器负担,但CPU还是需要提前规划虚拟内存,并且需要连续内存,比较死板

通道控制方式:使用专用的硬件通道(通道控制器)来管理和控制多个输入/输出设备,就是开一个简单的协助型处理机,专门计算计算IO事物,而CPU只需要向这个处理机发送IO命令就可以,简单来说就是CPU计算繁琐的IO事物大材小用了,就专门安排一个处理机处理这种繁琐的事物

设备控制器

设备控制器是一种硬件组件,用于管理和控制计算机系统中的各种输入和输出设备。它位于设备和计算机系统之间,负责协调和控制设备的操作。设备控制器通常与特定的设备相对应,并负责处理与该设备相关的输入和输出任务。

主要功能包括以下几个方面:

  • 设备接口:设备控制器提供与设备之间的物理连接和交互接口。它可以包含适当的接口电路、插槽、端口或电缆,以便设备能够与计算机系统进行通信
  • 设备通信:设备控制器负责与设备进行通信,包括发送请求、接收反馈和处理设备状态等。它通过控制信号和数据传输线将计算机系统中的命令和数据传递给设备,并接收设备返回的响应和数据
  • 设备操作管理:设备控制器管理设备的操作,包括初始化设备、配置设备参数、发送控制命令、数据传输等。它确保设备按照系统的要求进行操作,并在必要时进行错误检测和纠正
  • 中断处理:设备控制器可以通过产生中断信号,向处理器表明设备的状态或操作完成。这使得处理器可以及时响应设备事件,处理器可以快速切换到适当的设备控制器以处理输入/输出任务,而不需要不断地轮询设备状态
  • 缓冲管理:有些设备拥有较大的数据传输速率,而处理器可能无法立即处理接收或发送的数据。设备控制器可以通过使用缓冲区来暂存数据,允许设备和处理器之间的异步传输,从而更好地处理数据流。

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值