操作系统2

分成两个部分
第一部分:基本概念、处理器管理、存储管理
第二部分:设备管理、文件管理、并发程序设计

设备管理

基本概念

I/O设备

现代计算机系统通常配备大量的I/O设备,用于计算机系统与外部世界(如用户、其它计算机或电子设备等)进行信息交换或存储

按设备管理划分
  • 字符设备:以字符为单位进行信息交换,发送或接收一个字符流
    • 人机交互设备大多是字符设备,例如鼠标、显示器等
  • 块设备:以固定大小的数据块(块是存储介质上连续信息组成的一个区域)进行信息交换
    • 存储设备通常为块设备,例如磁盘驱动器等
  • 网络设备:用于与远程设备通信的设备
    • 机机通信设备为网络设备,例如网卡等
    • 网络设备可以抽象为传送字符流的特殊字符设备,也可以抽象为传送连续小块数据的块设备
设备管理的目标
  • 克服设备和CPU速度的不匹配所引起的问题,使主机和设备并行工作,提高设备使用效率

  • 对设备进行抽象,屏蔽设备的物理细节和操作过程,配置驱动程序,提供统一界面,供用户或高层软件使用

    • 抽象为文件系统中的节点,统一管理

      对设备的输入输出抽象为对文件的读和写

    • 裸设备:不被操作系统直接管理,由应用程序读写,I/O效率更高

设备管理的功能
  • 设备中断处理
  • 缓冲区管理
  • 设备的分配和去配
  • 设备驱动调度
  • 实现虚拟设备
设备管理的层次
  • I/O硬件
    • I/O设备及其接口线路
    • 控制部件
    • 通道
  • I/O软件
    • 系统I/O软件
    • 用户空间I/O软件

I/O控制方式

设备管理器

设备控制器是计算机中的一个实体,其主要职责是控制一个或多个I/O设备,以实现I/O设备和计算机之间的数据交换。它是CPU与I/O设备之间的接口,它接收从CPU发来的命令,并去控制I/O设备工作,以使处理机从繁杂的设备控制事务中解脱出来。设备控制器是一个可编址的设备,当它仅控制一个设备时,它只有一个唯一的设备地址;若控制可连接多个设备时,则应含有多个设备地址,并使每一个设备地址对应一个设备。

数据交换是指CPU与控制器之间控制器与设备之间的数据交换。对于前者,是通过数据总线,由CPU并行地把数据写入控制器,或从控制器中并行地读出数据;对于后者,是设备将数据输入到控制器,或从控制器传送给设备。

image-20200430122917064

轮询方式

  1. 处理器向控制器发送一个I/O命令
  2. 如果设备未就绪,则重复测试过程,直至设备就绪
  3. 执行数据交换
  4. 等待I/O操作完成后,才可以继续其它操作
image-20200430123050568

中断方式

  1. 处理器向控制器发出一个I/O命令,然后继续执行后续指令
    • 如果该进程不需要等待I/O完成,后续指令可以仍是该进程中的指令
    • 否则,该进程在这个中断上挂起,处理器执行其他工作
  2. 控制器检查设备状态,就绪后发起中断
  3. CPU响应中断,转向中断处理程序
  4. 中断处理程序执行数据读写操作
  5. 恢复执行原先的程序
image-20200430123347799

直接存储器访问(DMA)方式

DMA模块 :模仿处理器来控制主存和设备控制器之间的数据交换

流程:

  1. 处理器向DMA模块发出I/O命令
  2. 处理器继续执行其它工作, DMA模块负责传送全部数据
  3. 数据传送结束后, DMA中断处理器
image-20200430124050358

特点:

  • CPU不会终止原程序的执行
  • CPU只在数据传送的开始和结束时参与
    • 开始时, CPU需要对DMA模块进行初始化
    • 结束时, CPU响应中断,但不必保存现场
DMA方式中的周期窃取

当DMA和CPU同时经总线访问内存时, CPU总是将总线的占有权让给DMA一个或几个主存周期

  • 周期窃取对延迟CPU与主存的数据交换影响不大
  • 数据传送过程是不连续的和不规则的
  • CPU大部分情况下与Cache进行数据交换,直接访问内存较少
image-20200430124305377

三种方式的对比

采用轮询方式的设备控制器

CPU需要等待设备就绪,且参与数据传送

采用中断方式的设备控制器

CPU无需等待设备就绪,但响应中断后参与数据传送

通过DMA直接控制存储器

CPU在数据传送开始和结束时参与,与主存进行数据交换时不参与

CPU 作用 等待 设备 数据 传送
轮询 方式 需要 参与
中断 方式 不需要 参与
DMA 方式 不需要 不参与

I/O通道

设备控制器包含自身专用的处理器和通道程序

  • I/O指令不再由处理器执行,而是存在主存中,由
  • I/O通道所包含的处理器执行
    采用四级连接:处理器,通道,控制器,设备
流程
  1. CPU在遇到I/O请求,启动指定通道一旦启动成功,通道开始控制I/O设备进行操作,

  2. CPU执行其他任务

  3. I/O操作完成后, I/O通道发出中断, CPU停止当前工作,转向处理I/O操作结束事件

CPU与通道并行工作

I/O通道与DMA

与DMA方式不同的是,在DMA方式中,数据的传送方向、存放数据的内存始址以及传送的数据块长度等都由CPU控制,而在通道方式中,这些都由通道来进行控制。另外,DMA方式每台设备至少需要一个DMA控制器,一个通道控制器可以控制多台设备。

  1. DMA只能实现固定的数据传送控制,而通道有自己的指令和程序,具有更强的独立处理数据输入和输出的能力。
  2. DMA只能控制一台或者少数几台同类设备,而一个通道可以控制多台同类或者不同的设备。

详见https://www.jianshu.com/p/d1542085afde

总线与I/O

单总线

将CPU、主存和I/O模块连接到同一组总线上

优点:结构简单,易于扩充

缺点:主存需要和I/O模块共用总线;设备增多会造成总线变长,进而增加传输时延;无法适用于大量高速设备

image-20200430142917272

传统的三级总线

主存和Cache通过主存总线传送数据,主存总线和扩展总线上的I/O设备之间传送数据通过扩展总线接口缓冲

优点:主存与I/O之间的数据传送与处理器的活动分离;可以支持更多的I/O设备

缺点:不适用于I/O设备数据速率相差太大的情形

image-20200430143006160

采用南北桥的多级总线

通过存储总线、 PCI总线、 E(ISA)总线分别连接主存、高速I/O设备和低速I/O设备

优点:可以支持不同数据速率的I/O设备

image-20200430143049595

采用I/O通道的多级总线

支持CPU、主存和多个I/O通道之间的数据传送

支持I/O通道和I/O控制器,以及I/O控制器和设备之间的数据传送

image-20200430143131359

I/O软件

实现层次

设计目标
  • 高效率:改善设备效率,尤其是磁盘I/O操作的效率
  • 通用性:用统一的标准来管理所有设备
设计思路

把软件组织成层次结构,低层软件用来屏蔽硬件细节,高层软件向用户提供简洁、友善的界面

主要考虑的问题
  • 设备无关性:编写访问文件的程序与具体设备无关
  • 出错处理:低层软件能处理的错误不让高层软件感知
  • 同步/异步传输: 支持阻塞和中断驱动两种工作方式
  • 缓冲技术:建立数据缓冲区,提高吞吐率
image-20200430143747489

I/O软件的实现

I/O中断处理程序

位于操作系统底层,与硬件设备密切相关,与系统其余部分尽可能少地发生联系

进程请求I/O操作时,通常被挂起,直到数据传输结束后,并产生I/O中断时,操作系统接管CPU后转向中断处理程序

当设备向CPU提出中断请求时, CPU响应请求并转入中断处理程序

功能
  1. 检查设备状态寄存器内容
  2. 判断产生中断的原因,
  3. 根据I/O操作的完成情况进行相应的处理

如果数据传输有错,向上层软件报告设备的出错信息,实施重新执行

如果正常结束,唤醒等待传输的进程,使其转换为就绪态

如果有等待传输的I/O命令,通知相关软件启动下一个I/O请求

设备驱动程序

包括与设备密切相关的所有代码,从独立于设备的软件中接收并执行I/O请求

  • 把用户提交的逻辑I/O请求转化为物理I/O操作的启动和执行

  • 监督设备是否正确执行,管理数据缓冲区,进行必要的纠错处理

功能
  • 设备初始化
    • 在系统初次启动或设备传输数据时,预置设备和控制器以及通道状态
  • 执行设备驱动例程
    • 负责启动设备,进行数据传输
    • 对于具有通道方式,还负责生成通道指令和通道程序,启动通道工作
  • 调用和执行中断处理程序
    • 负责处理设备和控制器及通道所发出的各种中断
层次

每个设备驱动程序只处理一种设备,或者一类紧密相关的设备

设备驱动程序分为整体驱动程序和分层驱动程序

  • 整体驱动程序直接向操作系统提供接口和控制硬件
    • 适用于功能简单的驱动程序,效率较高,但较难迁移
  • 分层驱动程序将驱动程序分成多层,放在栈中,系统接到I/O请求时先调用栈顶的驱动程序,栈顶的驱动程序可以直接处理请求或向下调用更低层的驱动程序,直至请求被处理
    • 用于功能复杂、重用性要求较高的驱动程序,结构清晰且便于移植,但会增加一部分系统开销
独立于设备的I/O软件

执行适用于所有设备的常用I/O功能,并向用户层软件提供一致性接口

功能
  • 设备命名:通过路径名寻址设备
  • 设备保护:检查用户是否有权访问所申请设备
  • 提供与设备无关的数据单位:字符数量,块尺寸
  • 缓冲技术:传输速率,时间约束,不能直接送达目的地
  • 设备分配和状态跟踪:分配不同类型的设备
  • 错误处理和报告:驱动程序无法处理的错误
用户空间的I/O软件

库函数:一部分I/O软件可以使用库函数实现放在操作系统内核之外,运行时与应用程序连接

虚拟设备软件:用一类设备模拟另一类设备的仿真I/O软件,如虚拟光驱

I/O缓冲区

目的

解决CPU与设备之间速度不匹配的矛盾,协调逻辑记录大小和物理记录大小不一致的问题,高CPU和设备的并行性,减少I/O操作对CPU的中断次数,放宽对CPU中断响应时间的要求

缓冲区

在内存中开辟的存储区,专门用于临时存放I/O操作的数据

操作

写操作:将数据送至缓冲区,直到装满,进程继续计算,同时系统将缓冲区的内容写到设备上

读操作:系统将设备上的物理记录读至缓冲区,根据要求将当前所需要的数据从缓冲区中读出并传送给进程

单缓冲

操作系统在主存的系统区中开设一个缓冲区

输入:将数据读至缓冲区,系统将缓冲区数据送至用户区,应用程序对数据进行处理,同时系统读入接下来的数据

输出:把数据从用户区复制到缓冲区,系统将数据输出后,应用程序继续请求输出

image-20200430150438524

双缓冲

使用两个缓冲区

输入:设备先将数据输入缓冲区1,系统从缓冲区1把数据传到用户区,供应用程序处理,同时设备将数据传送到缓冲区2

输出:应用程序将数据从用户传送到缓冲区1,系统将数据传送到设备,同时应用程序将数据传送到缓冲区2

数据从设备输入到缓存区和缓存区输入到用户区就可以同时进行

image-20200430150623270

循环缓冲

操作系统分配一组缓冲区,每个缓冲区度有指向下一个缓冲区的链接指针,构成循环缓冲

解决设备和进程速度不匹配的问题。

这些缓存区为系统公共资源,供进程共享并由系统统一分配和管理

image-20200430151104741

设备独立性

用户通常不指定物理设备,而是指定逻辑设备,使得用户作业和物理设备分离开来,再通过其它途径建立逻辑设备和物理设备之间的映射

比如用户编写程序的时候需要实现打印功能,在实现打印功能的时候不去指定具体的某台打印机,而是只是声明需要打印机,由系统去调配一个打印机

设备管理中需要将逻辑设备名转换为物理设备名,为此系统需要提供逻辑设备名和物理设备名的对应表以供转换使用

优点

  • 应用程序与具体物理设备无关,系统增减或变更设备时不需要修改源程序
  • 易于应对I/O设备故障,提高系统可靠性
  • 增加设备分配的灵活性,更有效地利用设备资源实现多道程序设计

设备的分配方式

独占设备:只能由一个进程独占式使用

分配方式

  • 静态分配
    在程序运行之前先分配好资源,实现简单,能够防止系统发生死锁,但会降低设备利用率
  • 动态分配
    提高设备利用率

设备分配的数据结构

设备类表
  • 每类设备对应于设备类表的中一栏
  • 包括:设备类,总台数,空闲台数,设备表起始地址等
  • 支持设备独立性时才会使用
设备表
  • 每类设备都有各自的设备表,用来登记这类设备中的每台物理设备
  • 包括:物理设备名(号), 逻辑设备名(号),占有设备的进程号,是否分配,好/坏标志等

磁盘

物理结构

image-20200430153230257

磁盘由多个盘片组成

每个盘片被划分为多个同心圆结构的磁道

不同盘片上位于相同位置的磁道构成的圆柱体称为柱面

每个磁道分为固定多个或不等个数的扇区

为了对大量扇区寻址,操作系统将相邻的扇区组合成存储文件

物理块(扇区)地址:

  1. 柱面号,磁头号,扇区号

  2. x面y道z扇区

    “ x面y道z扇区”中的“面”是指磁头(盘面),不是柱面,扇区是从1开始的

读写数据

读写数据时, 磁头必须定位到指定的磁道上的指定扇区的开始处

过程
  1. 寻道:控制移动臂到达指定柱面,选择磁头号
  2. 旋转:等待要读写的扇区旋转到磁头下
  3. 数据传送

磁盘访问时间=寻道时间+旋转时间+传输时间

驱动调度

移臂调度

使移动臂的移动时间最短,从而减少寻道总时间

先进先出
  • 移动臂是随机移动,寻道性能较差
  • 按顺序处理请求,对所有进程公平
最短查找时间优先
  • 先执行查找时间最短的请求,具有较好的寻道性能

  • 存在“饥饿”现象

单向扫描
  • 移动臂总向一个方向扫面,归途中不提供服务
  • 适用于不断有大量柱面均匀分布的请求的情形
双向扫描
  • 移动臂每次向一个方向移动,遇到最近的I/O请求便进行处理,到达最后一个柱面后再向相反方向移动
  • 对最近扫描所跨越区域的请求响应较慢
电梯调度
  • 无请求时移动臂停止不动,有请求时按电梯规律移动
  • 每次选择沿移动臂的移动方向最近的柱面
  • 如果当前移动方向上没有但相反方向有请求时,改变移动方向
旋转调度

使得旋转延迟的总时间最少

循环排序
  • 通过优化I/O请求排序,在最少旋转圈数内完成位于同一柱面的访问请求
  • 旋转位置测定硬件和多磁头同时读写技术有利于提高旋转调度的效率
优化分布

通过信息在存储空间的排列方式来减少旋转延迟

  • 交叉因子
    如果沿着磁道按序对扇区编号,可能由于磁盘转速太快,造成处理当前扇区的数据时, 下一个扇区已经跳过,需要再转一圈才能继续读数据。因此,对扇区编号时会间隔编号,例如交叉因子为2:1表示相邻编号之间会间隔1个扇区, 3:1表示会间隔2个扇区。
  • 按柱面而非盘面进行数据读写
    连续记录数据时,先记录在同一柱面的不同磁道上,然后再更换柱面,可以减少数据读写时的移臂操作。

虚拟设备

使用一类物理设备模拟另一类物理设备的技术

eg:内存卡模拟磁盘,块设备模拟字符设备

一个经典的SPOOLing系统

为存放输入数据和输出数据,系统在磁盘上开辟输入井和输出井

井是用作缓冲的存储区域

组成
  • 预输入程序:将数据从输入设备传送到磁盘输入井
  • 缓输出程序:将数据从磁盘输出井传送到输出设备
  • 井管理程序:控制作业和井之间的数据交换
作用
  • 预输入:操作系统将作业需要的输入数据成批从输入设备上预先输入至磁盘的输入缓冲区中暂存
  • 调度作业执行时,作业使用数据不必再启动输入设备,从磁盘的输入缓冲区读入即可
  • 缓输出:作业不启动输出设备,只是将输出数据暂存到磁盘的输出缓冲区
  • 作业执行完毕后,由操作系统成批输出

不仅设备利用率提高,作业的运行时间也会缩短,每个作业都感觉各自拥有所需的独占设备

image-20200430160846927

文件管理

文件系统

文件系统是操作系统中负责存取和管理信息的模块,它用统一的方式管理用户和系统信息的存储、检索、更新、共享和保护,并为用户提供一整套方便有效的文件使用和操作方法

文件这一术语不但反映了用户概念中的逻辑结构,而且和存放它的辅助存储器(也称文件存储器)的存储结构紧密相关

功能

文件系统面向用户的功能
  • 文件的按名存取
  • 文件的共享和保护
  • 文件的操作和使用

为了实现这些功能, OS必须考虑:

  • 文件目录的建立和维护
  • 存储空间的分配和回收
  • 数据的保密和保护
  • 监督用户存取和修改文件的权限
  • 实现在不同存储介质上信息的表示方式、 编址方法、 存储次序, 以及信息检索等问题
image-20200430163306675

文件的存储

卷和块

  • 卷是存储介质的物理单位,对应于一盘磁带、一块软盘、一个光盘片、一个硬盘分区

  • 块是存储介质上连续信息所组成的一个区域,也叫做物理记录

    块在主存储器和辅助存储器进行信息交换的物理单位,每次总是交换一块或整数块信息

    外围设备由于启停机械动作或识别不同块的要求,两个相邻块之间必须留有间隙,间隙是块之间不记录用户代码信息的区域

文件的结构

文件的逻辑结构

文件的逻辑结构分为两种形式,流式文件和记录式文件

流式文件

指文件内的数据不再组成记录, 只是由一串依次的字节组成的信息流序列

这种文件常常按长度来读取所需信息,也可以用插入的特殊字符作为分界

记录式文件

是一种有结构的文件,它是若干逻辑记录信息所组成的记录流文件

逻辑记录是文件中按信息在逻辑上的独立含义所划分的信息单位

如,每个职工的工资信息是一个逻辑记录;整个单位职工的工资信息便组成了该单位工资信息的记录式文件

image-20200430165011341

文件的物理结构

文件的物理结构和组织是指文件在物理存储空间中的存放方法和组织关系

文件的存储结构涉及块的划分、记录的排列、索引的组织、信息的搜索等许多问题

顺序文件

将一个文件中逻辑上连续的信息存放到存储介质的依次相邻的块中便形成顺序结构,这类文件叫顺序文件,又称连续文件

优点:顺序存取记录时速度较快

缺点:建立文件前需要能预先确定文件长度以便分配存储空间;修改、插入和增加文件记录有困难

类似于数组

连接文件

连接文件,又称串联文件;连接结构的特点是使用连接字来表示文件中各个物理块之间的先后次序

第一块文件信息的物理地址由文件目录给出,而每一块的连接字指出了文件的下一个物理块位置;连接字内容为0时,表示文件至本块结束

类似于链表

优点:

  • 易于对文件记录做增、删、改,易于动态增长记录

  • 不必预先确知文件长度

  • 存储空间利用率高

缺点:

  • 存放指针需额外的存储空间
  • 由于存取须通过缓冲区,待获得连接字后,才能找到下一物理块的地址,因而,仅适用于顺序存取
直接文件

直接文件,又称散列文件,它通过计算记录的关键字建立与其物理存储地址之间的对应关系

这种变换通常采用散列法 (hash法)

类似于哈希表

索引文件

索引文件为每个文件建立了一张索引表,其中,每个表目包含一个记录的键(或逻辑记录号)及其存储地址

索引表的地址可由文件目录指出,查阅索引表先找到相应记录键(或逻辑记录号),然后获得数据存储地址

访问方式

索引文件在文件存储器上分两个区:索引区和数据区

访问索引文件需两步操作:

  1. 查找索引表,
  2. 获得记录物理地址

需要两次访问辅助存储器,若文件索引已预先调入主存储器,那么,就可减少一次内外存信息交换

特点

优点:索引结构可以被认为是连接结构的一种扩展,除了具备连接文件的优点外,还克服了它只能作顺序存取的缺点,具有直接读写任意一个记录的能力,便于文件的增、删、改

去点:增加了索引表的空间开销和查找时间

image-20200430170818354

文件目录

文件目录结构

文件系统的基本功能之一就是负责文件目录的建立、维护和检索,要求编排的目录便于查找、防止冲突

一级目录结构

在操作系统中构造一张线性表,与每个文件的相关属性占用一个目录项, 构成了一级目录结构

二级目录机构

第一级为主文件目录,它用于管理所有用户文件目录,它的目录项登记了系统接受的用户的名字及该用户文件目录的地址

第二级为用户的文件目录,它为该用户的每个文件保存一个登记栏,其内容与一级目录的目录项相同

每一用户只允许查看自己的文件目录

image-20200501114837043
树形目录结构

每一级目录可以登记下一级目录,也可以登记文件,从而,形成了层次文件目录结构

层次目录结构通常采用树形目录结构,它是一棵倒向的有根树,树根是根目录;从根向下,每一个树分叉是一个子目录;而树叶是文件

一个文件的全名包括从根目录开始到文件为止,通路上遇到的所有子目录路径,又称为路径名

image-20200501115109789

目录的管理

查找
文件查找

“按名存取”文件就是系统根据用户提供的文件路径名来搜索各级文件目录,找到该文件

  • 可以从根目录查起(绝对路径名)

  • 也可以从“当前目录”查起(相对路径名)

    用.表示当前目录, ..表示父目录

  • 现代操作系统都设置有改变工作目录命令,即变更当前工作目录

目录项查找

搜索具体目录项时,可以采用顺序查找法,依次扫描文件目录中的目录项,将目录项中的名字与欲查找的文件名相比较,可以采用一些优化办法加快查找目录的速度,如二分法,哈希

文件目录处理

树型目录结构存在的一个问题是:当一个文件经过许多目录节点时,使用很不方便;系统在沿路径查找目录时,往往要多次访问文件存储器,使访问速度大大减慢,若把所有文件的目录都复制到主存,访问速度是加快了,但又增加了主存的开销。

一种有效办法是把常用和正在使用的那些文件目录复制进主存,这样,既不增加太多的主存开销,又可明显减少目录查找时间

活动文件表

系统可以为每个用户进程建立一张活动文件表,当用户使用一个文件之前,先通过“打开”操作,把该文件有关目录信息复制到指定主存区域,有关信息填入活动文件表,以建立用户进程和该文件索引的联系

当不再使用该文件时,使用“关闭”,切断用户进程和这个文件的联系,同时,若该目录已被修改过,则应更新辅存中对应的文件目录

文件安全与保护

文件是计算机系统的重要资源,因此, 要求文件系统具有保障文件安全的手段,提供文件保密的措施,有效地实现文件的共享

文件共享

指不同用户共同使用某些文件

并发控制

在允许文件共享的系统中,操作系统应提供手段实现对共享文件的同步控制

  • 多个进程可能同时存取一个文件,如果它们同时进行读操作,操作系统应对文件进行公用控制
  • 如果有进程进行写操作,例如,有两个进程,进程A要求修改文件,同时进程B要求读出同一文件中的数据,则操作系统必须提供同步控制机制,以保证文件数据的完整性
保密措施

文件保密是指文件及其内容不能被未经文件主授权的其他用户窃取
文件的保密措施有以下几种:

  1. 隐蔽文件目录

  2. 设置口令

  3. 使用密钥

    设置口令指的是打开之前需要输入口令才能打开,但是文件是存储在磁盘上的,所以还可以直接对文件本身使用密钥进行加密,使得没有密钥的时候,文件本身内容是无法理解的

文件保护

指防止文件被破坏

文件系统必须要有防止硬软件故障,保存信息完整性的能力,文件副本是主要实现机制

动态多副本技术

在多个介质上维持同一内容的文件,并且在更新内容时同时进行

转储、备份与恢复

定时把文件复制转储到其它介质上,当某介质上出现故障时,复原转储文件

  • 一是在一定时间间隔或一个单位处理结束时,系统自动复写更新过的文件和数据
  • 二是每天或每周把文件信息全部复写一遍,需要时再通过装入转储文件来恢复系统,诸如BACKUP、 RESTORE等命令

文件保密

指防止文件及其内容被其他用户窃取

文件的存取控制矩阵

系统为每个用户设置访问每个文件对象的存取属性

系统的全部用户对全部文件的存取属性就组成的一个二维矩阵,称为存取控制矩阵

存取控制表

由于操作系统拥有很多用户和众多文件,存取控制矩阵是一个稀疏矩阵,可以将其简化为一张存取控制表

每行包括:用户、文件、存取属性

存取控制表仅登记那些对文件拥有存取属性的部分

基于存取控制矩阵/表的文件保护

存取属性:可以有访问、读、写、执行、创建、删除、授权等

授权就是文件的所有者将文件的权限授权给其他用户

系统通过查阅(矩阵/表)核对用户对文件的存取权限

系统管理用户(超级用户)等同于文件属主权限,并获得对系统文件的授访问权权限

文件属性

存取控制表的一种简化方法是用户分类,再针对每类用户规定文件属性

  • 用户分类:属主、合作者、其他

  • 文件属性:读、写、执行、 …

  • 文件属性可以放在文件目录项中,管理大为简化

  • 用户使用文件时, 通过核对文件属性,实现保护

    执行
    文件主 1 1 0
    伙伴 1 0 0
    它用户 1 0 0

文件的存取

操作系统为用户程序提供的使用文件的技术和手段,某种程度上依赖于文件的物理结构

顺序存取

按记录顺序进行读/写操作

读操作根据读指针读出当前记录,同时推进读指针,指向下一次要读出的记录

写操作则设置写指针,把一个记录写到文件未端, 同时推进写指针

允许对读指针进行前跳或后退n(整数)个记录的操作

直接存取

很多应用场合要求快速地以任意次序直接读写某个记录

例如,航空订票系统,用航班号作标识,把特定航班的所有信息存放在物理块中,用户预订某航班时,直接计算出该航班的存位置

索引存取

基于索引文件的索引存取方法

对于这种文件, 信息块的地址都可以通过查找记录键而换算出

实际的系统中,大都采用多级索引,以加速记录查找过程

文件的使用

用户通过两类接口与文件系统联系

  1. 与文件有关的操作命令

    例如,UNIX中的cat, cd, cp, find, mv,rm, mkdir, rmdir等

  2. 第二类是提供给用户程序使用的文件类系统调用

    基本文件类系统调用有:建立、打开、读/写、定位、关闭、撤销

建立文件

用于创建一个文件

所需参数:文件名、设备类(号)、文件属性及存取控制信息

处理流程:在相应设备上建立一个文件目录项, 为文件分配第一个物理块,在活动文件表申请一个项,登记有关目录信息,并返回一个文件句柄

撤销文件

用于删除一个文件

所需参数:文件名和设备类(号)

处理流程: 若文件没有关闭, 先关闭文件;若为共享文件, 进行联访处理;在目录文件中删去相应目录项;释放文件占用的文件存储空间

如果是共享文件不一定是物理的删除,可能只是对于这个用户来说文件删除了

打开文件

用于建立起文件和用户进程之间的使用联系

所需参数:文件名、 设备类(号)、打开方式

处理流程: 在主存活动文件表申请一个项,返回一个文件句柄;跟据文件名查找目录文件, 把目录信息复制到活动文件表相应栏;按存取控制说明检查访问的合法性;若打开的是共享文件,则应有相应处理

关闭文件

用于结束一个文件的读写

所需参数:文件句柄

处理流程:将活动文件表中该文件的“当前使用用户数”减1;若此值为0,则收回此活动文件表;完成“推迟写”;若活动文件表目内容已被改过,则应先将表目内容写回文件存储器上相应表目中,以使文件目录保持最新状态

读/写文件

所需参数参数:文件句柄、用户数据区地址、读写的记录或字节个数

处理流程:按文件句柄从活动文件表中找到该文件的目录项信息;根据目录项指出的该文件的逻辑和物理组织方式,把相关逻辑记录转换成物理块

定位文件

用于调整所打开文件的读写指针位置

所需参数:文件句柄,定位指针

辅存空间的使用

磁盘等大容量辅存空间被OS及许多用户共享,用户进程运行期间常常要建立和删除文件, OS应能自动管理和控制辅存空间

随着用户文件不断建立和撤销,文件存储空间会出现许多‘碎片’。OS解决‘碎片’ 的办法是整理‘碎片’;在整理过程中,往往对文件重新组织,让其存放在连续存储区中

分配方式

连续分配:存放在辅存空间连续存储区中(连续的物理块号)

  • 优点是顺序访问时速度快,管理较为简单,但为了获得足够大的连续存储区,需定时进行‘碎片’整理

非连续分配:动态分配给若干扇区或簇(几个连续扇区),不要求连续

  • 优点是辅存空间管理效率高,便于文件动态增长和收缩

空闲块的管理

位示图

使用若干字节构成一张表,表中每一字位对应一个物理块,字位的次序与块的相对次序一致。字位为“1”表示相应块已占用,字位为“0”状态表示该块空闲

其主要优点是,可以把位示图全部或大部分保存在主存中,再配合现代计算机都具有的位操作指令,可实现高速物理块分配和去配

空闲块成组连接法

把硬盘分为块来管理,然后块分为两类,一类是被占用了的(存储了数据),称为专用块,一类是没有被占用的,称为空闲块,空闲块组成组,如图所示连接在一起

image-20200501150339987

每个空闲块的组最上面一行显示空闲块的数量

每个空闲块组最多100个空闲块,分别编号0-99,其中0号空闲块作为指针,1-99空闲块作为真正使用的空闲块。

每当需要分配空闲块的时候:

if(this.空闲块数 =1 ){
    if(this.第一个单元 == 0){
        等待分配空闲块;
    }else{
        x = 一个单元所指向的空闲块组;
        分配x的空闲块;
        x.空闲块数--;
    }
}else if(this.空闲块数>1){
    分配this.空闲块;
    this.分配空闲块--;
}

每当归还空闲块的时候:

if(this.空闲块数<100){
    归还空闲块;
    this.空闲块数++;
}else if(this.空闲块数 == 100){
    空闲块组 x = new 空闲块组();
    x.空闲块数 = 1;
    x.第一个单元 = this;
    将空闲块添加到x;
    x.空闲块数++;
}

文件系统的实现层次

image-20200501152325932

并发程序设计

概念

顺序程序设计

每个程序在处理器上执行是严格有序的

程序设计的一般习惯是顺序程序设计:把一个具体问题的求解过程设计成一个程序或者若严格顺序执行的程序序列,这称为程序执行的外部顺序性

特征
  • 程序执行的顺序性:程序指令执行是严格按序的
  • 计算环境的封闭性:程序运行时如同独占受操作系统保护的资源
  • 计算结果的确定性:程序执行结果与执行速度和执行时段无关
  • 计算过程的可再见性:程序对相同数据集的执行轨迹是确定的

进程的并发执行

多道程序设计让多个程序同时进入内存去竞争处理器以获得运行机会,OS允许计算机系统在一个时间段内存在多个正在运行的进程,即允许多个进程并发执行。OS保证按照“顺序程序设计” 方法编制的程序在并发执行时不受影响如同独占计算机。这些按照顺序程序设计思想编制的进程在OS中并发执行属于无关的并发进程。

循环地(从输入机读78秒再计算52秒再向磁带机输出20秒)按照顺序程序设计方法,设计成如下一个程序:while(1) { input, process, output }

image-20200501154609781

处理器利用率: 52/(78+52+20)≈35%

换一种设计思路, 设计3个独立运行的程序,让它们同时进入多道程序系统去并发执行

  1. while(1) { input, send }

  2. while(1) { receive, process, send }

  3. while(1) { receive, output }

image-20200501154709944

处理器利用率: (52n) /(78n+52+20)≈67%

并发程序设计

把一个具体问题求解设计成若干个可同时执行的程序模块的方法

特性
  • 并行性:多个进程在多道程序系统中并发执行或者在多处理器系统中并行执行,提高了计算效率
  • 共享性:多个进程共享软件资源
  • 交往性:多个进程并发执行时存在制约,增加了程序设计的难度

并发进程之间的制约关系

无关的并发进程:一组并发进程分别在不同的变量集合上运行, 一个进程的执行与其他并发进程的进展无关

交往的并发进程:一组并发进程共享某些变量,一个进程的执行可能影响其他并发进程的结果

与时间有关的错误

对于一组交往的并发进程,执行的相对速度无法相互控制。如果程序设计不当,可能出现各种
“与时间有关的”错误。

  • 结果错误

    两个进程几乎同时对一个数据进行读写,导致数据的读写产生错误

    如x中数据为1,A和B都去读并加1写回,可能他们读到的x都是1,当A写回之后,B也写回,最后的结果是B的结果覆盖了A的结果,而如果分开执行就是+2,而最后只+1

  • 永远等待

    如进程A申请资源的时候,发现没有资源,则进程一直等待,而进程B在此之后归还了资源,但A没有重新查看是否存在资源,则处于一直等待

进程互斥与进程同步

交互的并发进程在执行时必须进行制约,才能保证得到合理的结果

进程互斥:并发进程之间因相互争夺独占性资源而产生的竞争制约关系

进程同步: 并发进程之间为完成共同任务基于某个条件来协调执行先后关系而产生的协作制约关系

临界区

临界资源:互斥共享变量所代表的资源,即一次只能被一个进程使用的资源

临界区指并发进程中与互斥共享变量相关的程序段

多个并发进程访问临界资源时, 存在竞争制约关系。如果两个进程同时停留在相关的临界区内,就会出现与时间相关的错误。

临界区的描述

确定临界资源shared <variable>

确定临界区region <variable> do < statement_list>

两个进程的临界区有相同的临界资源,就是相关的临界区,必须互斥进入

临界区管理的三个要求

  1. 一次至多允许一个进程停留在相关的临界区内
  2. 一个进程不能无限止地停留在临界区内
  3. 一个进程不能无限止地等待进入临界区

临界区的嵌套使用

image-20200501163246863

并非临界区的嵌套使用一定会可能产生死锁,只要申请临界资源的顺序是确定的,就不会死锁

临界区管理

框内的(测试锁、建立锁)两条指令,执行过程不能中断,如果中断就可能产生同时进入临界区或者死锁的状况。

测试并建立指令
TS(x) {
    if (x==false) { 
        x=true; return true;
    }else return false;
}
Boolean lock;
lock = false; // 临界区可用
process Pi { // i = 1,2,…,n
    Boolean pi;
    repeat pi = TS(lock) until pi; // 循环请求锁
    临界区;
    lock = false; // 解锁
}
对换指令
swap (a,b) { temp=a; a=b; b=temp; }
Boolean lock;
lock = false; //临界区可用
process Pi { // i = 1,2,…,n
    Boolean pi;
    pi = true;
    repeat swap(lock, pi) until !pi; //循环请求锁
    临界区;
    lock = false; //解锁
}

TS和swap指令均是忙式等待,效率低

中断

简单的解决办法是在进出临界区时开关中断,这样临界区执行就不会中断了,执行就有原子性

关中断; 临界区; 开中断

操作系统原语就采用这种实现思路

但是,临界区的指令长度应该短小精悍,这样才能保证系统效率,不建议用户程序使用, 滥用是可怕的!

PV操作

TS或swap指令管理临界区,采用忙式轮询,效率低

关开中断管理临界区, 不便交给用户程序使用

参考:操作系统访问硬件资源时采用“请求-等待-中断恢复”方式

记录型信号量

一种带数值的软资源

typedef struct semaphore {
    int value; // 信号量值
    struct pcb *list; // 信号量等待进程队列指针
}
  • 每个信号量建立一个等待进程队列list
  • 每个信号量相关一个整数值value
    • 正值表示资源可复用次数
    • 0值表示无资源且无进程等待
    • 负值表示等待队列中进程个数

PV操作原语

P操作原语(申请资源)
procedure P(semaphore:s) {
    s = s – 1; //信号量减去1
    if (s < 0) W(s); //若信号量小于0,则调用进程被置成等待信号量s的状态
}
V操作原语(释放资源)
procedure V(semaphore:s) {
    s = s + 1; //信号量加1
    if (s <= 0) R(s); //若信号量小于等于0,则释放一个等待信号量s的进程
}

PV操作解决进程互斥问题框架

semaphore s;
s = 1;
cobegin
    process Pi {
        ……
        P(s);
        临界区;
        V(s);
        ……
    }
coend;

PV操作解决进程同步

并发进程为完成共同任务基于某个条件来协调执行先后关系而产生的协作制约关系

一个进程的执行等待来自于其他进程的消息

1生产者1消费者1缓冲区问题

生产者和消费者共享缓冲区

缓冲区有空位时,生产者可放入产品,否则等待

缓冲区有产品时,消费者可取出产品,否则等待

image-20200501171725235

同步关系1:消费者一开始在等待产品到来, 考虑设置一个信号量(等待产品);一开始无产品,初值为0

同步关系2:消费者则在等待缓冲区中有空位, 也可设置一个信号量(等待缓冲区);一开始缓冲区有空位,初值为1

Int B; // 共享缓冲区
Semaphore sput; // 可以使用的空缓冲区数
Semaphore sget; // 缓冲区内可以使用的产品数
sput = 1; // 缓冲区内允许放入一件产品
sget = 0; // 缓冲区内没有产品

process producer {
    while(1){
        produce a product;
        P(sput);//
        B = product;
        V(sget);//
    }
}

process consumer {
    while(1){
        P(sget);//
        Product = B;
        V(sput);//
        consume a product;
    }
}
1生产者1消费者n缓冲区问题
Int B[k]; // 共享缓冲区队列
Semaphore sput; // 可以使用的空缓冲区数
Semaphore sget; // 缓冲区内可以使用的产品数
sput = k; // 缓冲区内允许放入 k 件产品
sget = 0; // 缓冲区内没有产品
Int putptr, getptr; // 循环队列指针
putptr = 0; getptr = 0;

process producer{
    while(1){
        produce a product;
        P(sput);//
        B[putptr] = product;
        putptr =(putptr+1) mod k;
        V(sget);//
    }
}

process consumer{
    while(1){
        P(sget);//
        product= B[getptr];
        getptr=(getptr+1) mod k;
        V(sput);//
        consume a product; 
    }
}
n生产者n消费者n缓冲区
Int B [k];
Semaphore sput; /* 可以使用的空缓冲区数 */
Semaphore sget; /* 缓冲区内可以使用的产品数 */
sput = k; /* 缓冲区内允许放入 k 件产品 */
sget = 0; /* 缓冲区内没有产品 */
Int putptr, getptr; putptr = 0; getptr = 0;
Semaphore s1,s2; /* 互斥使用 putptr,getptr */
s1 = 1; s2 = 1;

process producer_i {
    while(1){
        produce a product;
        P(sput);//
        P(s1);//*
        B[putptr] = product;
        putptr =(putptr+1) mod k;
        V(s1);//*
        V(sget); //
    }
}

process consumer_j {
    while(1){
        P(sget);//
        P(s2);//*
        Product= B[getptr];
        getptr=(getptr+1) mod k;
        V(s2);//*
        V(sput);//
        consume a product;
    }
}
苹果橘子问题
image-20200501174030588
Int plate;
Semaphore sp; /* 盘子里可以放几个水果 */
Semaphore sg1; /* 盘子里有桔子*/
Semaphore sg2; /* 盘子里有苹果 */
sp = 1; /* 盘子里允许放入一个水果*/
sg1 = 0; /* 盘子里没有桔子 */
sg2 = 0; /* 盘子里没有苹果*/

process father {
    while(1){
        削一个苹果;
        P(sp);
        把苹果放入plate;
        V(sg2);
    }
}
process mother {
    while(1){
        剥一个桔子;
        P(sp);
        把桔子放入plate;
        V(sg1);   
    }
}

process son {
    while(1){
        P(sg1);
        从plate中取桔子;
        V(sp);
        吃桔子; 
    }
}

process daughter {
    while(1){
        P(sg2);
        从plate中取苹果;
        V(sp);
        吃苹果;  
    }
}

进程通信

进程通信就是进程之间信息的传递

交往进程通过信号量操作实现进程互斥和同步,这是一种低级通信方式

进程有时还需要交换更多的信息(如把数据传送给另一个进程),可以引进高级通信方式——进程通信机制,实现进程间用信件来交换信息

进程直接通信

发送或接收信件的进程指出信件发给谁从谁那里接收信件

  • send(P, 信件):把信件发送给进程P
  • receive(Q, 信件):从进程Q接收信件

进程间接通信

发送或者接收信件通过一个信箱来进行, 该信箱有唯一标识符,多个进程共享一个信箱

  • send(A, 信件) : 把信件传送到信箱A
  • receive(A, 信件) : 从信箱A接收信件
间接通信的信箱

信箱是存放信件的存储区域,每个信箱可以分成信箱特征和信箱体两部分

  • 信箱特征指出信箱容量、信件格式、指针等
  • 信箱体用来存放信件,信箱体分成若干个区,每个区可容纳一封信
发送信件原语的处理流程
若指定的信箱未满

则把信件送入信箱中指针所指示的位置,释放等待该信箱中信件的等待者

若指定的信箱已满

发送信件者被置成等待信箱的状态

接收信件原语的处理流程
若指定信箱中有信件

则取出一封信件,释放等待信箱的等待者

若指定信箱中无信件

接收信件者被置成等待信箱中信件的状态

高级进程通信机制

基于流的进程通信

多个进程使用一个共享的消息缓冲区(可称为管道、多路转接器、套接字)

一些进程往消息缓冲区中写入字符流(send/write)

一些进程从消息缓冲区中读出字符流(receive/read)

信息交换单位基于字符流,长度任意

image-20200502145011916
基于RPC的进程通信

采用客户/服务器计算模式

服务器进程提供一系列过程/服务,供客户进程调用

客户进程通过调用服务器进程提供的过程/服务获得服务

考虑到客户计算机和服务器计算机的硬件异构型,外部数据表示XDR被引入来转换每台计算机的特殊数据格式为标准数据格式

外部数据表示XDR就是一个中间格式,客户进程和服务器进程的格式不同在发送的时候用中间格式进行中转

image-20200502145146422

死锁

允许多个进程并发执行共享系统资源时,系统必须提供同步机制和进程通信机制。然而,对这种机制使用不当的话,可能会出现进程永远被阻塞的现象

产生死锁的因素不仅与系统拥有的资源数量有关,而且与资源分配策略进程对资源的使用要求以及并发进程的推进顺序有关

可从三个方面来解决死锁问题:

  • 死锁防止
  • 死锁避免
  • 死锁检测和恢复

死锁防止

死锁产生的必要条件

  • 互斥条件: 进程应互斥使用资源,任一时刻一个资源仅为一个进程独占
  • 占有和等待条件:一个进程请求资源得不到满足而等待时,不释放已占有的资源
  • 不剥夺条件:任一进程不能从另一进程那里抢夺资源
  • 循环等待条件:存在一个循环等待链,每一个进程分别等待它前一个进程所持有的资源

破坏四个必要条件之一,死锁就可防止

破坏第一个条件,把独占型资源改造成共享性资源,使资源可同时访问而不是互斥使用。这是一个简单的办法,但对许多资源往往是不能做到的

采用剥夺式调度方法可以破坏第三个条件,但剥夺式调度方法目前只适用于对主存资源和处理器资源的分配,而不适用于所有资源

静态分配(预分配)

所谓静态分配是指一个进程必须在执行前就申请它所要的全部资源,并且直到它所要的资源都得到满足之后才开始执行

所有并发执行的进程要求的资源总和不超过系统拥有的资源数

采用静态分配后,进程在执行中不再申请资源,因而不会出现占有了某些资源再等待另一些资源的情况,即破坏了第二个条件

层次分配

这种分配策略将阻止第四个条件的出现在层次分配策略下,资源被分成多个层次

一个进程得到某一层的一个资源后,它只能再申请在较高层的资源

当一个进程要释放某层的一个资源时,必须先释放所占用的较高层的资源

当一个进程获得了某一层的一个资源后,它想再申请该层中的另一个资源,那么,必须先释放该层中的已占资源

死锁的避免

当不能防止死锁的产生时,如果能掌握并发进程中与每个进程有关的资源申请情况,仍然可以避免死锁的发生

只需在为申请者分配资源前先测试系统状态,若把资源分配给申请者会产生死锁的话,则拒绝分配,否则接收申请,为它分配资源

银行家算法

系统首先检查申请者对资源的最大需求量,如果现存的资源可以满足它的最大需求量时,就满足当前的申请

也就是系统像银行一样先评估你能不能归还资源,比如给进程A分配了x个资源,则进程A就可以释放所有资源,这样系统的资源数就可以增加了。再看系统里剩余的资源有没有x个,如果没有,那么分配资源给进程A的话,可能最后进程A 也获得不了x资源,则就不会去分配给A资源

eg:假设系统有三个进程P, Q, R,系统只有一类资源共10个,目前分配情况如下:

进程 已占资源 还需要申请数
P 4 4
Q 2 2
R 2 7

现在已经被占用的资源有4+2+2=8个,剩下10-8=2个资源

对P来说:4>2,不能分配资源

对Q来说:2<=2,利用分配资源

对R来说:7>2,不能分配资源

死锁的检测

对资源的分配不加限制,但系统定时运行一个“死锁检测”程序,判断系统内是否已出现死锁,若检测到死锁则设法加以解除

检测的一种方法:可设置两张表格来记录进程使用资源的情况

  • 等待资源表记录每个被阻塞进程等待的资源
  • 占用资源表记录每个进程占有的资源

进程申请资源时,先查该资源是否为其它进程所占用;若资源空闲,则把该资源分配给申请者且登入占用资源表;否则,则登入进程等待资源表

image-20200502152801114
检测

死锁检测程序定时检测这两张表, 若有进程Pi等待资源rk,且rk被进程Pj占用,则说Pi和Pj具有“等待占用关系”,记为W(Pi, Pj)

死锁检测程序反复检测这两张表,可以列出所有的“等待占用关系”

如果出现W(Pi, Pj), W(Pj, Pk), ……, W(Pm,Pn), W(Pn, Pi)时,显然,系统中存在一组循环等待资源的进程: Pi, Pj, Pk, ……, Pm, Pn,也就是说出现了死锁

进程 P1 P2 ...... Pn
P1 b11 b12 ...... bn2
P2 b21 b22 ...... bn2
... ... ... ...... ...
Pn bn1 bn2 ...... bn2

b_{ij} = \begin{cases} 1 ,&\text{当}P_i\text{与等待被}P_j\text{占用的资源时}\\[2ex] 0 ,&\text{当}P_i\text{与}P_j\text{不存在等待占用关系时} \end{cases}

然后这样一个表可以视为使用邻接矩阵表示的有向图,然后判断这个图是不是存在环路,如果存在则则存在死锁

处理

可以采用重新启动进程执行的办法,恢复工作应包含重启动一个或全部进程,以及从哪一点开始重启动

全部卷入死锁从头开始启动,但这样的代价是相当大的,在进程执行过程中定时设置校验点,从校验点开始重执行

中止一个卷入死锁的进程,以后重执行

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343