第一章, 简介
1.4 Basic Operating System Concepts
-
Unix-like操作系统隐藏了硬件细节,如果程序需要和硬件打交道,就必须向操作系统发出请求
-
Unix与CPU的
privileged mode
和nonprivileged mode
对应的是:内核模式(Kernel Mode)
和用户模式(User Mode)
Intel的x86处理器是通过Ring级别来进行访问控制的,级别共分4层,RING0,RING1,RING2,RING3。Windows只使用其中的两个级别RING0和RING3。RING0层拥有最高的权限,RING3层拥有最低的权限。按照Intel原有的构想,应用程序工作在RING3层,只能访问RING3层的数据,操作系统工作在RING0层,可以访问所有层的数据,而其他驱动程序位于RING1、RING2层,每一层只能访问本层以及权限更低层的数据。Linux操作系统内核态工作在Ring0层,而用户态工作在Ring3层。
参考: cpu的核心模式和用户模式是不是就是root和不root的区别? -
进程: 程序执行时的一个实例或者一个运行程序的执行上下文
-
多任务操作系统不一定是多用户的: 比如WIndows 98
-
Unix是具有
抢占式(preemptable)
进程的多任务操作系统 -
对每一个用户命令,shell进程都创建执行相应程序的另一个进程
-
类Unix操作系统采用
进程/内核模式(process/kernel model)
- 每个进程都自以为它是系统中唯一的进程,可以独占操作系统所提供的服务.
- 只要进程发出
系统调用 (system call)
,硬件就从用户模式
切换到内核模式
,执行完成再切换到用户模式
-
微内核(microkernel)
与单块结构(monolithic)
- 微内核: 只有很小的函数集
- 缺点
- 比
单块结构(monolithic)
效率低
- 比
- 优点
- 迫使系统程序员采用模块化的方法
- 更加充份的利用了RAM
- 缺点
单块结构(monolithic)
- Unix采用了这种
- 微内核: 只有很小的函数集
-
模块: 为了在理论上达到
微内核(microkernel)
优点,Unix采用了模块(module)
结构模块是一个
目标文件(object file)
,在运行时链接到内核或从内核解除链接,与任何其他静态链接的内核函数一样,它代表当前进程在内核模式
下执行
1.5 An Overview of the Unix Filesystem
- 文件被组织在一个树结构的
命名空间(namespace)
中,除了叶子节点之外,树的所有节点都表示目录名 - Unix的每一个进程都有一个当前工作目录,属于
进程执行上下文(execution context)
- 包含在目录中的文件名就是一个文件的
硬链接(hard link)
,硬链接有2方面的限制- 不允许用户给目录创建硬链接,因为这可能把目录树变为环形图
- 只有在同一文件系统中的文件才能创建
- 为了克服硬链接的缺点,引入了
软链接(soft link)
也称为
符号链接(symbolic link)
,是短文件,这些文件包含有另一个文件的任意一个路径名,路径名可以指向位于任意一个文件系统的任意文件或目录,甚至可以指向一个不存在的文件
- 文件类型:
- 普通文件,英文: Regular file
- 目录,英文: Direcotry
- 符号链接(也就是软链接),英文: Symbolic link
- 基于块的设备文件,英文: Block-oriented device file
- 基于字符的设备文件,英文: Character-oriented device file
- 管道和命名管道(也叫FIFO),英文: Pipe and named pipe(also called FIFO)
- 套接字,英文: Socket
File Descriptor and Inode
- Unix对文件内容和文件信息是作了区分的,文件内容不包含任何控制信息
- 文件系统处理文件需要的所有信息包含在一个名为索引节点(inode)的数据结构中
- 每个文件都有自己的节点索引
- 文件系统用索引节点来标识文件
- 每个实际的文件操作必须在内核态下进行
- 文件描述符:
- 表示进程与打开文件之间的交互,而打开文件对象包含了与这种交互相关的数据
- 同一打开文件对象也许由同一个进程中的几个文件描述符标识
- 重新命名或删除一个文件时,进程不需要打开它
- 只有当链接数为0时,文件才被真正的删除
进程/内核模式
- 假定:
请求内核服务的进程使用所谓系统调用(system call)的特殊编程机制.每个系统调用都设置了一组识别进程请求的参数,然后执行与硬件相关指令完成从用户态到内核态的转换
- 一些CPU有两种以上的执行状态,但是所有标准的Unix都仅仅用了
内核态
和用户态
- 一个程序执行时,大部分时间都处在用户态下,只有需要内核所提供的服务时才切换到内核态.当内核满足了用户程序的请求后,它让程序又回到了用户态下
- 内核本身并不是一个进程,而是进程的管理者
- 内核线程,是特权进程
- 它们以内核态运行在内核地址空间
- 它们不与用户直接交互,因此不需要终端设备
- 它们通常在系统启动时创建,然后一直处于活跃状态直到系统关闭
- 激活内核例程(kernel route)的几种方式
- 进程调用系统调用
- 正在执行进程的CPU发出异常信号
- 外围设备发出一个中断信号通知一个事件的发生
- 内核线程被执行
- 进程描述符
为了让内核管理进程,每个进程由一个进程描述符表示,包含有关进程的当前状态的信息
- 内核暂停进程,需要把几个相关的寄存器的内容保存在进程描述符中
- 程序计数器(PC)
- 栈指针(SP)寄存器
- 通用寄存器
- 浮点寄存器
- 处理器状态字
- 内存管理寄存器
- 内核是可重入的: 若干个进程可以同时在内核状态下执行
- 需要同步控制技术
- 信号量(semaphore)
- 自旋锁(spin locks)
- 进程地址空间
- 每个进程运行在它的私有地址空间
- 用户态下:
- 私有栈,数据区和代码区
- 内核态下:
- 访问内核的数据区和代码区
- 有时进程也分享地址空间
- 进程显示提出
- 内核自动完成以节约内存
如编辑程序由几个用户同时使用,程序只被装入内存一次,其
指令
由所有需要它的用户共享,当然数据不共享
- 一种由
System V
引入的共享内存
技术
- 进程管理
-
fork(): 创建一个新进程
-
_exit(): 中止一个进程
-
exec():装入一个新程序
-
父子进程能找到对方,以为描述每个进程的数据结构都包含有2个指针:一个指向它的父进程,一个指向它的子进程
-
僵尸进程(zombie processes)
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
-
孤儿进程:
一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
- init进程用以解决孤儿进程
系统初始化的时候被创建,当一个进程终止时,内核改变其所有现有子进程的进程描述符指针,使这些子进程成为init的孩子.init监控所有子进程的执行,并按常规发布wait4()系统调用,其副作用就是除掉所有的僵尸进程
- init进程用以解决孤儿进程
-
进程组: 用以表示作业(job)的抽象
ls | sort | more
这就是一个进程组
-
- 需要同步控制技术
内存管理
虚拟内存
处于应用程序的内存请求与硬件内存管理单元(
MMU
)之间
- 优点
- 若干个进程可以并发的执行
- 应用程序需要内存大于物理内存时也可以运行
- 程序只有部分代码装入内存时进程可以执行它
- 允许每个进程访问可用物理内存的子集
- 进程可以共享库函数或程序的一个单独内存映射
- 程序是可以重定位的,也就是说,可以把程序放在物理内存的任何地方
- 程序员可以编写与机器无关的代码,因为他们不关心有关物理内存的组织结构
- 虚拟地址空间(virtual address space)
- 当进程使用虚拟地址(virtual addres, 有的也叫 logical addresses), 内核和MMU合作找到对应的物理内存地址
- 一块连续的虚拟地址请求可以通过分配一组非连续的物理地址页框(
page frame
)获得- 页框(page frame): CPU的电路为了把虚拟地址转换成物理地址,把可用的RAM划分成长度为4KB或8KB的页框(page frame)
- Unix操作系统把RAM分为2块:
- 存储内核镜像(kernel image) ,比如 内核代码和内核静态数据结构
- 用作虚拟内存
- 主要问题: 内存碎片
内核内存分配器(kernel memory allocator,简称KMA)
> 一个子系统,试图满足系统中所有部分对内存的请求
- 应该具备的特点:
- 必须快
- 必须把内存浪费减到最少
- 必须努力减轻内存的碎片问题
- 必须能与其他内存管理子系统合作,以便接用和释放页框
进程的虚拟地址空间处理
进程的虚拟地址空间(The address space of a process)包含了进程可以引用的所有虚拟内存地址.
- 内核通常用一组内存区描述符描述进程虚拟地址空间
- 内核分配给进程的虚拟地址空间由以下内存区组成
- 程序的可执行代码
- 程序的初始化数据
- 程序的未初始化数据
- 初程序栈
- 所需共享库的可执行代码和数据
- 堆(由程序动态请求的内存)
deman paging
- 一个进程可以在其页(pages)没有存在于物理内存的情况下执行, 当其访问一个不存在的页面,MMU产生异常,并为其分配一个空闲页
- 调用malloc()或者brk()(brk()内部也是调用的malloc())系统调用,kernel只是更新进程的堆空间大小,只有在尝试引用这个虚拟地址而引发异常的时候才真正的为进程分配
page frame
- 写时复制(Copy on Write)
比如一个新进程被创建了,内核只是把父进程的
page frames
赋值给子进程,但是将其标识为只读(read-only),当父进程或子进程尝试更改页面内容的时候,exception handler会把一个新的page frame
给受影响的进程并带有原始页面的数据
- 缓存
- 硬盘缓存
- 其他块设备的缓存
- 策略: 尽可能的推迟写磁盘的时间,从磁盘读入内存的数据即使任何进程都不再使用它们,它们也继续留在RAM中
- 效果: 如果缓存被命中就不用读磁盘
- sync()系统调用会把脏的缓存(
dirty buffers
),也就是和内存不一致的数据,周期性的写入到磁盘
- 设备驱动程序
内核通过设备驱动程序(device driver)与I/O设备交互
- 设备驱动程序包含在内核中