磁盘调度算法
一次磁盘读写需要的时间
寻道时间
先来先服务FCFS
优点:公平
缺点:性能差,寻道时间长
最短寻道时间有限SSTF
缺点:可能产生饥饿现象
扫描算法SCAN
优点:不会产生饥饿现象
缺点:响应频率不平均
循环扫描算法C-SCAN
优点:磁道响应频率平均
缺点:平均寻道时间长
多道批处理系统 CPU I/O
FCFS先来先服务算法、SJF短作业优先、优缺点
处理机调度算法
先来先服务FCFS
(不会导致饥饿)
优点:算法简单,有利于长作业
缺点:平均等待时间波动大,I/O资源和CPU资源的利用率低
短进程优先SPF
优点:平均周转时间和平均带权周转时间小,缩短作业的等待时间,提高系统的吞吐量
缺点:对长进程不利,长时间得不到执行
优先权PSA
优点:体现进程的紧急程度,适合实时系统
缺点:无穷阻塞问题,会导致饥饿
高响应比HRRN
优点:有利于短作业,综合考虑等待时间和运行时间,避免长进程的饥饿问题
缺点:计算响应比,增加系统开销
时间片轮转RR
优点:响应快,公平
缺点:高频率的进程切换,有一定的开销
多级反馈队列
优点:兼顾I/O密集和CPU密集型
缺点:算法复杂
文件系统
系统调用 fwrite()
(1) 在内核栈保存大多数寄存器的内容
(2) 调用所谓系统调用服务例程的相应的C函数来处理系统调用。
(3) 通过ret_from_sys_call( )函数从系统调用返回(这个函数用汇编语言编写)。
xyz( )系统调用对应的服务例程的名字通常是sys_xyz( )
为了把系统调用号与相应的服务例程关联起来,内核利用了一个系统调用表;这个表存放在sys_call_table数组中,有NR_syscalls个表项(通常是256个):第n个表项包含系统调用号为n的服务例程的地址。NR_syscalls宏只是对可实现的系统调用最大个数的静态限制,并不表示实际已实现的系统调用个数。
1.从用户程序中调用fork
2.在libc库中把fork对应的系统调用号2放入寄存器eax
3.通过int 0x80陷入内核
4.在中断描述表IDT中查到系统调用的入口0x80
5.进入Linux内核的entry_32(64).S文件,从系统调用表sys_call_table中找到sys_fork的入口地址
6.执行fork.c中的do_fork代码
7 通过iret或者sysiret返回
物理地址的转化
请求分页存储管理
页面置换算法
最佳置换算法OPT
先进先出置换算法FIFO
最近最久未使用置换算法LRU
CLOCK置换算法
磁盘读写----最短寻道时间算法、扫描算法
进程状态转化
银行家算法
进程 Work Allocation Need W+A Finish P1 3 3 2 2 0 0 1 2 2 5 3 2 T P3 5 3 2 2 1 1 0 1 1 7 4 3 T P4 7 4 3 0 0 2 4 3 1 7 4 5 T P2 7 4 5 3 0 2 6 0 0 10 4 7 T P0 10 7 4 0 1 0 7 4 3 10 5 7 T (1)安全
(2)P1请求Request(1,0,2)小于Need(1,2,2),小于Available(3,3,2)
假设分配,找到安全序列P1、P3、P4、P0、P4
Available=Available-Request
Need=Need-Request
Allocation=Allocation+Requesr
Work=Work-Request
(3)P4请求Request(3,3,0)>Available 阻塞等待
(4)P0请求Request(1,0,2)小于Need(1,2,2),小于Available(3,3,2)
Available=(3,3,2)-(1,0,2)=(2,1,0)不满足任何进程的需要,找不到安全序列,P0请求被拒绝,P0阻塞,资源不予分配
安全序列
死锁
四个必要条件:互斥条件、请求和保持条件、不可剥夺条件、循环等待条件
死锁产生的原因:并发进程/线程的调度推进顺序不恰当
竞争非剥夺性资源、临时性资源可能产生死锁
死锁的处理策略:预防策略、避免策略、检测与解除、鸵鸟策略
破坏互斥条件---互斥虚拟为共享
破坏不接剥夺条件----主动放弃
破坏请求和保持条件----一次申请所有资源
破坏循环等待条件---资源有序使用
可变分区分配
首次适配算法
循环首次适配算法
最佳适配算法
最差适配算法
I/O层次结构
用户层I/O软件:实现与用户交互的接口,用户可直接调用在用户层提供的、与I/O操作有关的库函数,对设备进行操作。(按出现问题)
设备独立性软件:设备独立性也称设备无关性,使得应用程序独立于具体使用的物理设备,为了实现设备独立性,必须再在驱动程序之上设置一层设备独立性软件。优点 是方便用户,改善设备利用率,提高系统的可扩展性和可适应性。总的来说,设备独立性软件的主要功能可分以为以下两个方面:1、执行所有设备的公有操作;2、向用户层(或文件层)提供统一接口。
设备驱动程序:处于次底层,是进程和控制器之间的通信程序,其将上层发来的抽象I/O请求,转换为对I/O设备的具体命令和参数,并把它装入到设备控制其中的命令和参数寄存器中。负责具体实现系统对设备发出的操作指令,驱动 I/O设备工作的驱动程序。
中断处理程序:用于保存被中断进程的CPU环境,转入相应的中断处理程序进行处理,处理完并恢复被中断进程的现场后,返回到被中断进程。
优点:从位示图中很容易找到一个或一组相邻接的空闲盘块,占用空间少,节省许多磁盘的启动操作
DMA方式的进入:为了适应一次传送大量数据的应用要求,以及尽量减少CPU对高速外设的干预。
与“中断驱动方式”相比,DMA(Direct Memory Access,直接存储器存取。主要用于块设备的I/O控制)有这样几个改进:(1) 数据传送的单位是块,不再是一个字一个字的传送。
(2) 数据的流向是从设备直接放入内存,或者直接从内存到设备,不再需要CPU干预。
(3) 仅在一个块或多个块的开始和结束时,才需要CPU干预。
当我们在用户程序中调用read()函数时,陷入内核空间,实际上要通过内核的copy_to_user()函数把内核空间缓冲区中的数据拷贝到用户空间的缓冲区,反之,当我们调用write()函数时,内核通过调用copy_from_user()函数把用户空间的数据拷贝到内核缓冲区。
fd
文件描述符是用来描述打开的文件的。在内核中每个进程用一个files_struct结构来记录文件描述符的使用情况,这个files_struct结构称为用户打开文件表,它是进程的私有数据。对于在数组中有入口地址的每个文件来说,数组的索引就是文件描述符。通常,数组的第0,1,2三个下标分表表示三个标准的输入,输出和错误文件,3分配给新打开的文件(如图)。在这里强调,每个打开的文件,在内核 中都有file对象,每一个文件描述符都是指向file结构的。
read()、write()
文件系统
索引节点
文件系统需要将文件的磁盘索引结点载入内存,以便操作系统可以通过内存索引结点管理这个文件,通常使用open操作实现
如图所示,以Unix文件系统为例,使用线性检索目录的方式,介绍检索mbox文件的目录检索过程,其中(按)1、2、3部分是目录内容,1是根目录的内容、2是目录usr的内容、3是目录406的内容。
具体的检索过程是:
(1)操作系统首先找到根目录,根目录位于磁盘分区的固定位置,所以OS可以轻松的找到该目录。
(按) 查找根目录内容,发现usr对应的磁盘索引结点node是6号,从文件系统概述那节我们了解到,文件系统从创建起来的时候,操作系统就知道所有文件的磁盘FCB和磁盘inode的物理地址,因此很容找到usr目录的inode结点。
(2)(按)找到6号inode,读取内容,查询发现/usr目录内容存放在132号物理块中。
(3)(按)读取132号物理块内容,查询发现/usr/ast的inode编号为26。
(4)(按)接着找到26号inode,读取内容,/usr/ast目录内容存放在块406中。
(5)(按)找到406号物理块,读取内容,/usr/ast/mbox的磁盘inode编号是60。
至此,内核完成了按名找到mbox的FCB和inode的目标。
文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector)。每个扇区储存512字节。操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个"块"(block)。这种由多个扇区组成的"块",是文件存取的最小单位。"块"的大小,最常见的是4KB,即连续八个 扇区组成一个 block。
文件数据都储存在"块"中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的权限,文件的创建日期、文件的大小,文件数据块的位置等等。这种储存文件元信息的区域就叫做inode
读者写者
信号量,初值
伪代码
单生产者--消费者
首先定义信号量space和prod,然后在main函数中对这两个信号量进行初始化,接着依次创建生产者线程和消费者线程。
生产者线程为无穷循环,每放一个产品之前,先要申请存储位置资源,因此对信号量space进行sem_wait操作,放入产品后,新生成一个产品资源,故对信号量prod进行sem_post操作。消费者线程在取产品前,先对信号量prod进行sem_wait操作,申请产品资源,成功后取走产品,并通过sem_post(&space)释放一个缓冲区资源。为了大家查看生产者及消费者线程的运行效果,在这里我们使用printf语句分别模拟放产品和取产品。
分析该程序,不难看出在信号量的控制下,生产者线程与消费者线程在执行时,按照“生产者放1个产品->消费者取1个产品->生产者再放1个产品->……”这样的顺序有序执行,很好的解决了生产者与消费者间的同步问题。
先定义表示缓冲区大小的常量N,假设为10;然后定义信号量space和prod;另外,定义存储产品的数组Buffer,并定义循环队列的队头指针out和队尾指针in。main函数中,将信号量space初始化为N,其他代码跟前面一样。
生产者线程和消费者线程跟前面总体一致。生产者在放产品前先对信号量space进行sem_wait操作,申请存储位置资源,成功后将产品放到队尾in的位置,并修改队尾指针,完成后对信号量prod进行sem_post操作释放一个产品资源。消费者线程取产品依然需要通过sem_wait(&prod)操作获取一个产品资源,成功后从队头out的位置取产品,并修改队头指针,最后通过sem_post(&space)释放一个缓冲区资源。
该程序和单缓冲区程序的最大差别在于,生产者线程可以连续生产最多N个产品放到缓冲区中供消费者消费
增加表示生产者与消费者个数的常量M,令其为8;增加信号量buf。在main函数中,除增加对信号量buf进行初始化外,分别通过循环创建M个生产者和消费者线程。
生产者线程在放产品前,先通过sem_wait(&space)操作申请存储位置资源,然后再通过sem_wait(&buf)操作获取缓冲区的控制权,成功后将产品放到队尾的位置并修改队尾指针,完成后在通过sem_post(&prod)释放一个产品资源的同时,并通过sem_post(&buf)释放缓冲区资源。
与此对应,消费者线程在取产品前,先通过sem_wait(&prod)操作申请产品资源,然后再通过sem_wait(&buf)操作获取缓冲区的控制权,成功后从队头位置取产品并修改队头指针,完成后通过sem_post(&space)释放一个存储位置资源,并通过sem_post(&buf)释放缓冲区资源。
在信号量buf的控制下,任何时候只能有一个生产者或消费者进入缓冲区存放或者取产品,实现了生产者以及消费者之间对缓冲区的互斥访问。
大家请注意,在消费者线程中,先通过sem_wait(&prod)申请产品资源,然后再通过sem_wait(&buf)申请缓冲区资源,能否把这两条语句的顺序交换一下呢?
我们把这两条语句交换一下,分析看看结果是否还正确。假设第1个消费者线程先执行,对buf信号量进行了sem_wait操作后,buf的值从1变为0,资源分配成功,进一步对prod信号量进行sem_wait操作,此时prod的值从0变为-1,消费者线程被阻塞;在此之后,其它消费者线程若执行,也同样会被阻塞。 当第1个生产者线程执行时,虽然对space信号量进行sem_wait操作可以成功,但再对buf进行sem_wait操作时,由于此时buf的值已经小于等于0,减1后buf值小于0,生产者进程被阻塞,其它生产者进程陆续执行后也被阻塞。结果是生产者线程无法获取缓冲区资源存放产品,而消费者线程占据着缓冲区等待产品的到来,最终导致了生产者和消费者线程间发生了死锁,谁也无法继续执行。
通过这个例子,大家可以看到在使用记录型信号量申请多种临界资源时,必须仔细分析,稍有不慎会导致死锁的发生。
生产者线程在放产品前,先通过Swait(space,buf)操作同时申请存储位置资源和缓冲区资源,成功后将产品放到队尾的位置并修改队尾指针,完成后再通过Ssignal(prod,buf)释放产品资源和缓冲区资源。
相应的,消费者线程取产品前,先通过Swait(prod,buf)操作申请产品资源和缓冲区资源后,然后从队头取出产品并修改队头指针,完成后通过Ssignal(space,buf) 释放存储位置资源和缓冲区资源。
使用ADD信号量后,生产者线程及消费者线程一次性申请完所有执行所需要的资源,避免了死锁的发生。
除存储位置信号量space和产品信号量prod外,新定义队头和队尾信号量sin和sout。在main函数中,依然需要分别对这四个信号量进行初始化,并通过循环创建M个生产者和消费者线程。
按照前面讲的策略,生产者线程在放产品前,先通过sem_wait(&space)操作申请存储位置资源,然后再通过sem_wait(&sin)操作获取队列尾变量in资源的使用权,成功后将产品放到队尾位置并修改队尾指针,完成后在通过sem_post(&prod)释放一个产品资源的同时,通过sem_post(&sin)释放队尾变量资源。
与此对应,消费者线程在取产品前,先通过sem_wait(&prod)操作申请产品资源,然后再通过sem_wait(&sout)操作获取队头变量out的控制权,成功后从队头位置取产品并修改队头指针,完成后通过sem_post(&space)释放一个存储位置资源,并通过sem_post(&sout)释放队头变量资源。
分析该程序,我们可以看到,信号量space和prod的作用和前面单生产者-消费者问题相同,用于实现生产者与消费者之间的同步。另外,在信号量sin的控制下,在同一时刻只能有一个生产者线程访问临界资源in,将产品放到队列末尾位置,并修改队尾指针;相应的,在信号量sout的控制下,在同一时刻也只能有一个消费者线程从队头位置取产品。改进方案的最大优点是,在同一时刻允许一个生产者和一个消费者同时在缓冲区中放产品和取产品,提高了系统的运行效率。
在优化后的程序里,消费者线程先通过sem_wait(&prod)申请产品资源,然后再通过sem_wait(&sout)申请队列头资源,依然逐次申请了两个不同的资源