操作系统基础

zzyNorthPole

操作系统介绍

功能

用户角度上,操作系统是一个控制软件,它管理应用程序、为应用程序提供服务、杀死应用程序。

操作系统有资源管理的功能,它能够管理外设、分配资源。

操作系统将CPU抽象成进程磁盘抽象成文件内存抽象成地址空间来供应用程序进行使用。

定位

操作系统位于应用软件之下,为应用软件提供服务支撑。

shell是面向应用程序的接口,kernel是面向内部资源的接口。

特征

  1. 并发:计算机系统中同时存在多个运行的程序,需要OS管理和调度

并发和并行的区别:并发是在一段时间上有多个程序运行,并行是一个时间点上有多个程序运行。

  1. 共享:“同时”访问、互斥共享。

  2. 虚拟:利用多道程序设计技术,让每个用户都觉得有一个计算机专门为他服务。

  3. 异步:程序的执行不是一贯到底,而是走走停停,向前推进的速度不可预知,但只要运行环境相同,OS需要保证程序的运行结果也相同。

发展

  1. 监视器、装载器

  2. 批处理阶段:操作程序流水线化

  3. 多道程序设计阶段:将多个程序同时放到一个内存中,通过上下文切换和产生中断来提高效率

  4. 分时调度:时间片轮转。时钟会定期的产生中断,来使得操作系统完成切换

结构

单体的操作系统:MS-DOS

微内核:在操作系统内核中只放置最基本的功能如中断处理、消息传递

操作系统的启动

初始情况:OS放置在DISK上,bootloader的基础功能是加载OS

BIOS加电自检,寻找显卡和执行BIOS,之后将硬盘中的bootloader加载到内存,再通过bootloader将硬盘中的OS加载到内存。操作系统通过中断来处理外设,通过系统调用和异常来与应用程序交互。

系统调用、异常、中断

定义

系统调用:来源于应用程序
应用程序主动向操作系统发出服务请求

异常:来源于不良的应用程序
非法指令或者其它坏的处理状态

中断:来源于外设
来自不同的硬件设备的计时器和网络的中断

比较

源头:
中断:外设
异常:应用程序意想不到的行为
系统调用:应用程序请求操作提供服务

处理时间:
中断:异步
异常:同步
系统调用:同步或异步(发出请求是同步的,返回的时间点可能是异步的也可能是同步的)

响应:
中断:持续,对用户应用程序是透明的
异常:杀死或重新执行意想不到的应用程序指令
系统调用:等待和持续

处理机制

中断:
硬件:设置中断标记[CPU初始化],将内部、外部事件设置中断标记,中断事件的ID
软件:保存当前处理状态,中断服务程序处理,清除中断标记,恢复之前保存的处理状态

异常:异常编号
保存现场
异常处理:杀死产生异常的程序或者重新执行异常指令
恢复现场

系统调用:程序访问主要是通过高层次的API接口而不是直接进行系统调用
通常情况下,每个系统调用有相关的序号,系统调用接口根据这些序号来维护表的索引,系统调用接口调用内核态中预期的系统调用,并返回系统调用的状态和其他任何返回值

跨越操作系统边界的开销:
在执行时间上的开销超过程序调用
开销:
建立中断、异常、系统调用号与对应服务例程映射关系的初始化开销
建立内核堆栈
验证参数
内核态映射到用户态的地址空间,更新页面映射权限
内核态独立地址空间

内存

内存分层体系结构

抽象:逻辑地址空间
保护:独立地址空间
共享:访问相同内存
虚拟化:更多的地址空间

地址空间与地址生成

  1. CPU发出对某条指令地址的请求
  2. CPU中的MMU会查找逻辑地址的映射表,如果有则返回物理地址,如果没有则通过处理过程在内存中寻找

连续内存分配

内存碎片问题:空间内存不能被利用,分为外部碎片和内部碎片

分配算法

首次适配算法 最优适配算法 最差适配算法
定义 分配字节,使用第一个可用空闲块使得其尺寸大于 分配字节,使用最小的可用空闲块使得其尺寸大于 分配字节,使用最大的可用空闲块使得其尺寸大于
需求 按照地址排序的空闲块列表,回收时看是否能合并 按尺寸排列的空闲块列表,回收时看能否合并 按尺寸排列的空闲块列表,回收时看能否合并
优势 简单;易于产生更大空闲块 当大部分分配是小尺寸时非常有效;简单 如果分配是中等尺寸时效果最好
劣势 容易产生外部碎片 易产生很多微小碎片 易于破碎大的空闲块使得大分区无法被分配

碎片整理

  1. 压缩式碎片整理:重置程序以合并

  2. 交换式碎片整理:抢占等到的程序,回收它们的内存

非连续内存管理

优点:

更好的内存利用和管理
允许共享代码与数据
支持动态加载和动态链接

分段

分成堆栈段、数据段、代码段等

段访问机制:
(段号+偏移)

硬件实现方案:
逻辑地址分为段号和段内偏移两部分,段表中保存逻辑和物理段号的映射,物理段号的起始地址和物理段号的长度;
段表由操作系统建立

分页

段的大小可变,页的大小不可变
划分物理内存至固定大小的帧,划分逻辑地址空间至相同大小的页

帧:
(帧号+帧内偏移)
记帧号为,一共有位;
帧内偏移位,一共有位;
物理地址 =

页:
页内偏移的大小 = 帧内偏移的大小
记页号为,一共有位;
页内偏移为,一共有位。
虚拟地址 =

页寻址机制:
页表[页表基址+页号] = 帧号

不是所有的页都有对应的帧

页表项的内容:
标志位:dirty bit;resident bit;clock/reference bit。
TLB:[p+f]

虚拟内存

覆盖技术

必要部分常驻内存;
可选部分平时存放在外存中,必要时才装入内存;
不存在调用关系的模块不必同时装入内存,从而相互覆盖。

交换技术

将暂时不能运行的程序送到外存,从而获得空闲内存空间
操作系统把一个进程的整个地址空间的内容保存在外存中,将外存中的某个进程的地址空间读入内存中,换入换出内容的大小为整个程序的地址空间

交换时机:当内存空间不够或者有不够的危险时换出
交换区的大小:足够大以存放所有用户进程的所有内存映像的拷贝
程序换入时的重定位:动态地址映射

覆盖发生在程序内,交换发生在程序与程序之间

虚存技术

基本概念:
在装入程序时,不必将其全部装入到内存中,只需要将需要执行的部分页面或段装入到内存中,就可以让程序开始执行
在程序执行过程中,如果需要执行的指令或访问的数据尚未在内存中(缺页或缺段),则由处理器通知操作系统来将相应的页面或段调入内存,继续执行程序
另一方面,操作系统将内存中暂时不使用的页面或段调出保存在外存上,从而将更多空闲空间存放将要装入的程序以及即将调入的页面或段

基本特征:
大的用户空间:虚拟内存空间大于实际物理内存
部分交换:虚拟内存的调入和调出是对部分虚拟地址空间进行的
不连续性:物理内存分配的不连续,虚拟地址空间使用的不连续

基本思路:
当一个用户程序要调入内存运行时,不是将该程序的所有页面都装入内存,而是只装入部分页面,就可以启动程序运行。
在运行的过程中,如果要运行的程序或要访问的数据不在内存中,则向系统发出缺页中断请求,系统在处理中断时,将外存中相应的页面调入内存,使得该程序能够继续运行。

页表表项:
逻辑页号+访问位+修改位+保护位+驻留位+物理页帧号
访问位:如果该页面被访问过,设置此位,用于页面置换
修改位:表明此页在内存中是否被修改过,当系统回收该物理页面时,根据此位来决定是否把它的内容写回外存
保护位:表示允许对该页做何种类型的访问,如只读、可读写、可执行
驻留位:表示该页在内存还是外存中

缺页中断:

  1. 如果在内存中有空闲的物理页面,则分配物理页帧,转4,否则转2;
  2. 采用某种页面置换算法,选择一个将被置换的物理页帧,它所对应的逻辑页面位q。如果该页在内存期间被修改过,则将它写回外存;
  3. 对q所对应的页表项进行修改,驻留位置为0;
  4. 将需要访问的页p装入到物理页面f中;
  5. 修改p所对应的页表项,将驻留位置为1,将物理页帧号置为f;
  6. 重新运行被中断的指令。

后备存储:
数据:数据文件
代码:执行文件
动态链接库:库文件
运行时产生的数据:交换分区
虚拟内存性能:
EAT = 访问时间 * 页表命中几率 + 硬盘访问时间 * (1 - 页表命中几率) * (1 + 脏页几率)

页面置换算法

功能目标:尽可能减少页面的换进换出次数
页面锁定:用于描述必须常驻内存的操作系统的关键部分或者时间关键的应用进程,实现方法是在页表中添加锁定标志位

最优页面置换算法

基本思路:当一个缺页中断发生时,对于保存在内存当中的每一个逻辑页面,计算在它下一次访问之前,还需要等待多长时间,从中选择等待时间最长的那个,作为被置换的页面。

先进先出算法FIFO

基本思路:选择在内存中驻留时间最长的页面并淘汰之。

通过维护链表来实现。

最近最久未使用算法LRU

基本思路:当一个缺页中断发生时,选择最久未使用的那个页面并淘汰它。

利用局部性原理。

两种可能的实现方法是:
系统维护一个页面链表,最近刚刚使用过的页面作为首节点,最久未使用的页面作为尾节点。每一次访问内存时,找到相应的页面,把它从链表中摘下,再移动到链表首,每次缺页中断发生时,淘汰链表末尾的页面。
设置一个活动页面栈,当访问某页时,将此页号压入栈顶,考察栈内是否有与此页面相同的页号,若有责抽出。淘汰栈底页面。

时钟页面置换算法

需要用到页表项当中的访问位,当一个页面被装入内存时,将该位初始化为0。如果该页面被访问,则将该位置为1。
将各个页面组成环形链表,将指针指向最老的页面。
当发生一个缺页中断时,考察指针指向的最老页面,如果访问位为0,立即淘汰;如果访问位为1,将该位置为0,将指针往下移动一格,直到找到被淘汰的页面,然后将指针移动到它的下一格。

二次机会法

通过访问位和脏位共同控制

最不常用法LFU

基本思路:当一个缺页中断发生时,选择访问次数最少的那个页面,并淘汰它。

实现方法:对每个页面设置一个访问计数器,当一个页面被访问时,访问计数器+1,发生缺页中断时,淘汰计数值最小的那个页面。

Belady现象:采用FIFO算法时,有时会发生分配的物理页面数增加,缺页率反而提高的异常现象。

工作集替换算法

工作集模型:一个进程当前正在使用的逻辑页面集合,可以用一个二元函数来表示。
是当前的执行时刻;称为工作集窗口,即一个定长的页面访问的时间窗口;
在当前时刻之前的时间窗口中所有页面组成的集合;
指工作集的大小,即页面数目。
常驻集:当前时刻,进程实际驻留在内存中的页面集合,与物理页面数目和页面置换算法有关。

工作集页面置换算法:
追踪之前个引用,将不属于工作集的窗口的页换出

缺页率页面置换算法

可变分配策略:常驻集大小可变
具体实现:可以使用缺页率算法动态调整常驻集的大小

缺页率 = 缺页次数/内存访问次数
影响因素:页面置换算法;分配给进程的物理页面数目;页面本身的大小;程序的编写方法。
算法:
当缺失发生时,
如果,从内存中移除所有在时间内没有被引用的页;
如果增加缺失页到工作集中。

抖动问题:如果分配给一个进程的物理页面太少,不能包含整个工作集,即常驻集工作集,进程将会造成很多的缺页中断,需要频繁地在内存和外存之间替换页面。

平均页缺失时间/页缺失服务之间

进程

定义:一个具有一定独立功能的程序在一个数据集合上的一次动态执行过程

组成:
程序的代码
程序处理的数据
程序计数器中的值,指示下一条将要运行的指令
一族通用的寄存器的当前值,堆、栈
一组系统资源

进程与程序的联系:
程序是产生进程的基础,进程是程序功能的体现
通过多次执行,一个程序可以对应多个进程;通过调用关系,一个进程可以包括多个程序
进程是动态的,程序是静态的;程序是有序代码的集合,进程是程序的执行,有核心态和用户态
进程是暂时的,程序是永久的
进程的组成包括程序、数据和进程控制块

特点:
动态性:可以动态地创建、结束进程
并发性
独立性
制约性:因访问共享数据/资源或进程间同步而产生制约

进程控制块PCB

进程控制块:操作系统管理控制进程运行所用的信息集合

  1. 进程标识信息:本进程的标识、父进程的标识、用户标识
  2. 处理机状态信息保存区:
    用户可见寄存器:用户程序可以使用的数据、地址等寄存器
    控制和状态寄存器:程序计数器、程序状态字
    栈指针:过程调用、系统调用、中断处理和返回需要使用
  3. 进程控制信息:
    调度和状态信息
    进程间通信信息
    存储管理信息
    进程所用资源
    有关数据结构连接信息

PCB的组织方式:
链表:同一状态的进程其PCB成一链表
索引表:同一状态的进程归入一个index表,多个状态对应多个index表

进程状态

进程生命期管理:创建、运行、等待、唤醒、结束

进程创建:

  1. 系统初始化
  2. 用户请求创建一个新进程
  3. 正在运行的进程执行了创建进程的系统调用

进程运行:

进程等待:

  1. 请求并等待系统服务且无法马上完成
  2. 启动某种操作且无法马上完成
  3. 需要的数据没有到达
    进程只能自己阻塞自己

进程唤醒:

  1. 被阻塞进程需要的资源可以满足
  2. 被阻塞进程等待的时间到达
  3. 将该进程的PCB插入到就绪队列
    进程只能被别的进程或操作系统唤醒

进程结束:
正常退出(自愿)
错误退出(自愿)
致命错误(强制)
被其它进程杀死(强制)

三种基本状态:
运行状态:一个进程正在处理机上运行
就绪状态:一个进程获得除处理机之外的一切资源,一旦得到处理机即可运行
等待状态:一个进程正在等待某一事件而暂停运行

进程的其它基本状态:
创建状态:一个进程正在被创建,还没被转到就绪状态之前
结束状态:一个进程正在从系统中消失时的状态

进程挂起:/TODO/
阻塞挂起状态:进程在外存并等待某事件的出现
就绪挂起状态:进程在外存,但只要进入内存,即可运行
阻塞到阻塞挂起:没有进程处于就绪或就绪进程要求更多内存资源时,以提交新进程或运行就绪进程
就绪到就绪挂起:当有高优先级阻塞进程和低优先级就绪进程时,系统会选择挂起低优先级就绪进程
运行到就绪挂起:对抢占式分时系统,当有高优先级阻塞挂起进程因事件出现而进入就绪挂起时,系统就可能会将运行进程转到就绪挂起状态
阻塞挂起到就绪挂起:当有阻塞挂起进程因相关事件出现时,系统就会将阻塞挂起进程转换为就绪挂起进程
就绪挂起到就绪:没有就绪进程或挂起就绪进程优先级高于就绪进程时
阻塞挂起到阻塞:当一个进程释放足够内存时,系统会将一个高优先级阻塞挂起进程转换为阻塞进程

状态队列:
由操作系统维护一组队列,用来表示系统中所有进程的当前状态
不同的状态分别用不同的队列表示
每个进程的PCB都根据它的状态加入相应的队列中,当一个进程的状态发生变化时,它的PCB从一个状态队列中脱离出来,加入到另外一个队列

线程

特性:
实体间并发执行,且共享相同的地址空间

定义:进程中的一条执行流程
从两个方面来重新理解进程:
从资源组合的角度:进程将一组相关的资源组合起来,构成一个资源平台,包括地址空间、打开的文件
从运行的角度:代码在这个资源平台上的一条执行流程

线程的缺点:一个线程崩溃,导致其所属进程的所有线程崩溃

共享:代码、数据、文件
独立:寄存器、堆栈

进程是资源分配单位、线程是CPU调度单位
进程拥有一个完整的资源平台,而线程只独享必不可少的资源
线程能够减少并发执行的时间和空间开销:
线程的创建时间比进程短
线程的终止时间比进程短
同一进程内的线程切换时间比进程短
由于同一进程的各线程间共享内存和文件资源,可直接进行不通过内核的通信

线程的实现:
用户线程:在用户空间实现的线程机制,不依赖于操作系统的内核,由一组用户级的线程库函数来完成线程的管理,包括线程的创建、终止、同步和调度
缺点:如果一个线程发起系统调用而阻塞,整个进程都在等待;当一个线程开始运行后,除非它主动地交出CPU的使用权,否则所在的进程中的其它线程将无法运行

内核线程:在操作系统的内核中实现的一种线程机制,由操作系统的内核来完成线程的创建、终止和管理

轻量级进程:一个进程可有多个轻量级线程

上下文切换:
必须在切换之前存储许多部分的进程上下文;必须在之后能恢复;必须快速
管理:
就绪队列
等待I/O队列
僵尸队列

进程加载和执行:
pid = fork()
pid > 0 父进程
pid = 0 子进程
pid < 0 错误

exec调用允许一个进程“加载”一个不同的程序并在main开始执行,允许制定参数的数量(argc)和字符串参数数组(argv)

wait配合子进程的exit回收PCB资源

僵尸状态:进程无法正常工作,等待被回收

上下文切换:
切换CPU当前任务,从一个进程/线程到另一个;
保存当前进程/线程在PCB/TCB中的上下文;
读取下一个进程/线程的上下文

内核运行调度程序的条件:
一个进程从运行状态切换到等待状态;
一个进程被终结了

不可抢占:
调度程序必须等待事件结束

可以抢占:
调度程序在中断被相应后执行
当前的进程从运行切换到就绪,或者一个进程从等待切换到就绪
当前运行的进程可以被换出

CPU利用率:CPU处于忙状态所占时间的百分比

吞吐量:在单位时间内完成的进程数量

周转时间:一个进程从初始化到结束,包括所有等待时间所花费的时间

等待时间:进程在就绪队列中的总时间

响应时间:从一个请求被提交到产生第一次相应所花费的时间

减少响应时间
减少平均响应时间的波动
增加吞吐量:减小开销;系统资源的高效利用
减少等待时间:减少每个进程的等待时间

吞吐量是操作系统的计算带宽;相应时间是操作系统的计算延迟

Multilevel Feedback Queues:多级反馈队列

Fair Share Scheduling:公平共享调度
公平:公平通常会增加平均响应时间

FCFS:先来先服务
优点:简单
缺点:平均等待时间波动较大;
花费时间少的任务可能排在花费时间长的任务后面
可能导致I/O和CPU之间的重叠处理

SPN(SJF) SRT:短进程(作业)优先;短剩余时间优先
可能导致饥饿:连续的短任务会导致长任务饥饿;短任务可用时的任何长任务的CPU时间都会增加平均等待时间
需要预知未来

HRRN:最高响应比优先
关注进程等待了多长时间
不可抢占
R = (w + s) / s
w(waiting time) s(service time)

Round Robin:轮循
轮询调度算法:

在叫作量子的离散单元中分配处理器

时间片结束时,切换到下一个准备好的进程

RR花销:额外的上下文切换

时间量子太大:等待时间过长;极限情况退化成FCFS

时间量子太小:吞吐量由于大量的上下文切换开销受到影响

就绪队列被划分为独立的队列:前台(交互)、后台(批处理)

每个队列拥有自己的调度策略:前台(RR)、后台(FCFS)

一个进程可以在不同的队列中移动:

多级反馈队列

FFS:公平

多人多任务共享

实时调度:

正确性依赖于其时间和功能两方面的一种os

性能指标:时间约束的及时性;速度和平均性能相对不重要

主要特性:时间约束的可预测性

强实时系统:需要在保证的时间内完成重要的任务,必须完成

弱实时系统:要求重要的进程的优先级更高,尽量完成,并非必须

任务:以此计算,一次文件读取,一次信息传递

属性:取得进展所需要的资源;定时参数

任务:一系列相似的任务:

周期任务:任务有规律地重复

周期

执行时间

使用率

硬时限:如果错过了最后期限,可能会发生灾难性或非常严重的后果

软实现:理想情况下,时限应该被最大满足

RM 速率单调调度:

最佳静态优先级调度

通过周期安排优先级

周期越短优先级越高

执行周期最短的任务

EDF最早期限调度:

最佳的动态优先级调度

Deadline越早优先级越高

执行Deadline最早的任务

优先级反转的持续时间取决于其他不相关任务的不可预测的行为

解决方法:

低优先级任务继承高优先级任务的优先级依赖于他们共享的资源

优先级天花板:“资源”优先级和“所有可以锁定该资源的任务中优先级最高的那个任务”的优先级相同

除非优先级高于系统中所有被锁定的资源的优先级上限,否则任务尝试执行临界区的时候会被阻塞

持有最高优先级上限信号量锁的任务,会继承被该锁所阻塞的任务的优先级

同步

合作优点:

共享资源

加速

模块化

原子操作:

原子操作是指一次不存在任何中断或者失败的执行:

该执行成功结束,或者根本没有执行,并且不应该发现任何部分执行的状态

临界区:

临界区是指进程中的一段需要访问共享资源并且当另一个进程处于相应代码区域时候便不会被执行的代码区域

互斥:当一个进程处于临界区并访问共享资源时,没有其它进程会处于临界区并且访问任何相同的共享资源

死锁:两个或以上的进程,在相互等待完成特定任务,而最终没法讲自身任务进行下去

饥饿:一个可执行的进程,被调度器持续忽略,以至于虽然处于可执行状态但不被执行

临界区:

  1. 互斥
  2. 前进
  3. 有限等待
  4. 无忙等待(可选)

方法:

  1. 禁用硬件中断:

进入临界区禁用中断、离开临界区开启中断

缺点:

受制于临界区的执行之间

对效率有较大影响

多CPU情况下无法解决互斥问题

  1. 基于软件的解决方案:
1
2
3
4
5
6
do {
enter section;
critical section;
exit section;
reminder section;
} while (1);
1
2
3
4
5
6
7
8
do {
flag[i] = true;
turn = j;
while (flag[j] && turn == j);
critical section;
flag[i] = false;
remaider section;
} while (1);

Bakery算法:

N个进程的临界区:

进入临界区之前,进程接收一个数字

得到的数字最小的进入临界区

如果进程收到相同的数字,如果 , 先进入临界区,反之先进入临界区。

编号方案总是按照枚举的增加顺序生成数字。

  1. 原子:

硬件提供一些原语,操作系统提供更高级的编程抽象来简化:
锁;信号量

锁:
Lock::Acquire() 锁被释放前一直等待,直到得到锁;
Lock::Release() 释放锁,唤醒任何等待的进程。

1
2
3
lock_next_pid->Acquire();
new_pid = next_pid++;
lock_next_pid->Release();

Test-And-Set测试和置位:
从内存中读取值;测试该值是否为1;内存值设置为1。

交换:交换内存中的两个值。

1
2
3
4
5
6
7
8
9
class Lock {
int value = 0;
};
Lock::Acquire() {
while (test-and-set(value));
}
Lock::Release() {
value = 0;
}

无忙等待

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Lock {
int value = 0;
WaitQueue q;
}

Lock::Acquire() {
while (test-and-set(value)) {
add this TCB to wait queue q;
schedule();
}
}

Lock::Release() {
value = 0;
remove one t from q;
wakeup(t);
}
1
2
3
4
5
6
7
8
int key;
do {
key = 1;
while (key == 1) exchange(lock, key);
critical section;
lock = 0;
remainder section;
}

缺点:
忙等待消耗处理器时间;
当进程离开临界区并且多个进程在等待的时候可能导致饥饿;
死锁。

信号量:
一个整形(sem),两个原子操作:
P():sem - 1, 如果sem < 0 , 等待,否则继续。
V():sem + 1, 如果sem <= 0, 唤醒一个等待的P。

信号量是整数;
信号量是被保护的变量:只能通过PV来改变。
我们假定信号量是公平的,FIFO经常被使用。

信号量实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Semaphore {
int sem;
WaitQueue q;
};
Semaphore::P() {
sem--;
if (sem < 0) {
Add this thread t to q;
block(p);
}
}
Semaphore::V() {
sem++;
if (sem <= 0) {
Remove a thread t from q;
wakeup(t);
}
}

管程:
一个锁
0或多个条件变量
Lock:
Lock::Acquire()等待直到锁可用,抢占锁
Lock::Release()释放锁,唤醒等待者
Condition Variable:
允许等待状态进入临界区
Wait()释放锁,睡眠,重新获得锁后返回
Signal()唤醒等待者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class condition {
int numWaiting = 0;
WaitQueue q;
};
condition::wait(lock) {
numWaiting++;
add this thread t to q;
release(lock);
schedule();
require(lock);
}
condition::signal() {
if (numWaiting > 0) {
remove a thread t from q;
wakeup(t);
numWaiting--;
}
}

死锁:

文件系统

基本概念

文件系统:一种用于持久性存储的系统抽象
文件:文件系统中一个单元的相关数据在操作系统中的抽象

功能:

  1. 分配文件磁盘空间
  2. 管理文件集合
  3. 提供保护和便利

文件属性:名称、类型、位置、大小、保护、创建者、创建时间、最近修改时间
文件头:在存储元数据中保存每个文件的信息、属性

文件描述符

操作系统为每个进程维护一个打开文件表,一个打开文件描述符是这个表中的索引

文件指针:指向最近的一次读写位置,每个打开了者各文件的进程都使用这个指针
文件打开计数:记录文件打开的次数;最后一个进程关闭文件时,允许将其从打开文件表中移除
文件磁盘位置:缓存数据访问信息
访问权限:每个程序访问模式信息

系统访问接口:字节的集合

操作系统内部视角:块的集合(块是逻辑转换单元,而扇区是物理转换单位)

访问文件方式:
顺序访问:按字节一次读取
随机访问:从中间读写
基于内容访问:通过特征,索引

目录

一个文件系统需要先挂载才能被访问

  • Title: 操作系统基础
  • Author: zzyNorthPole
  • Created at : 2023-02-08 22:01:45
  • Updated at : 2023-05-04 19:57:30
  • Link: https://zzynorthpole.github.io/2023/02/08/操作系统基础/
  • License: This work is licensed under CC BY-NC-SA 4.0.