现在的位置: 首页 > 综合 > 正文

操作系统概念辨析

2013年08月07日 ⁄ 综合 ⁄ 共 9055字 ⁄ 字号 评论关闭

额,简历中有一句“熟悉操作系统原理”,底层公司、设备商公司应该会要问这些东东吧。有几个问题总结一下:

目录:

1.信号量(semaphore),互斥量(mutex),事件,临界区概念解析

2.自旋锁的概念
3.单内核和微内核

4.死锁的定义及其预防措施

5.多进程和多线程的概念以及如何选择他们?

6.linux系统通信机制

7.简述段式内存管理下得虚拟内地到物理地址的映射

8.什么是TLB(快表)?

9.linux 2.6 CFS(O(1)调度器的实现)

1.信号量(semaphore),互斥量(mutex),事件,临界区概念解析:

http://topic.csdn.net/u/20090317/01/6c1000a4-cd2f-458b-97e9-9e254461a37f.html  
http://hi.baidu.com/cnrjm/blog/item/5d217e54ff863550564e00b9.html

       信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在semtake的时候,就阻塞在哪里)。

        而互斥锁是用在多线程多任务互斥的,一个线程占用了某一个资源,那么别的线程就无法访问,直到这个线程unlock,其他的线程才开始可以利用这个资源。比如对全局变量的访问,有时要加锁,操作完了,在解锁。

       互斥锁一定会锁住资源,信号量不一定会,信号量就像红绿灯一样,“红灯停,绿灯行”。另外,互斥锁只有0、1。信号量不知这两种状态。信号量S是一个整数,S大于等于零时代表可供并发进程使用的资源实体数,但S小于零时则表示正在等待使用共享资源的进程数。常用的P/V操作:

P操作 申请资源:
     (1)S减1;
     (2)若S减1后仍大于等于零,则进程继续执行;
     (3)若S减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转入进程调度。
V操作 释放资源:
     (1)S加1;
     (2)若相加结果大于零,则进程继续执行;
     (3)若相加结果小于等于零,则从该信号的等待队列中唤醒一个等待进程,然后再返回原进程继续执行或转入进程调度。

1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。 
2、互斥量:为协调共同对一个共享资源的单独访问而设计的。 
3、信号量:为控制一个具有有限数量用户资源而设计。 
4、事 件:用来通知线程有一些事件已发生,从而启动后继任务的开始。

2.自旋锁的概念

  http://www.cnblogs.com/linxinshuo/archive/2009/12/08/1619771.html

        自旋锁是专为防止多处理器并发而引入的一种锁,它应用于中断处理等部分。对于单处理器来说,防止中断处理中的并发可简单采用关闭中断的方式,不需要自旋锁。

  自旋锁最多只能被一个内核任务持有,如果一个内核任务试图请求一个已被争用(已经被持有)的自旋锁,那么这个任务就会一直进行忙循环——旋转——等待锁重新可用。要是锁未被争用,请求它的内核任务便能立刻得到它并且继续进行。自旋锁可以在任何时刻防止多于一个的内核任务同时进入临界区,因此这种锁可有效地避免多处理器上并发运行的内核任务竞争共享资源。

  事实上,自旋锁的初衷就是:在短期间内进行轻量级的锁定。一个被争用的自旋锁使得请求它的线程在等待锁重新可用的期间进行自旋(特别浪费处理器时间),所以自旋锁不应该被持有时间过长。如果需要长时间锁定的话, 最好使用信号量。但是自旋锁节省了上下文切换的开销。

3.单内核和微内核

l 微内核(Microkernel kernel)

   
在微内核中,大部分内核都作为独立的进程在特权状态下运行,它们通过消息传递进行通讯。在典型情况下,每个概念模块都有一个进程。因此,如果在设计中有一个系统调用模块,那么就必然有一个相应的进程来接收系统调用,并和能够执行系统调用的其它进程(或模块)通讯以完成所需任务。 在这些设计中,微内核部分经常只不过是一个消息转发站:当系统调用模块要给文件系统模块发送消息时,消息直接通过内核转发。这种方式有助于实现模块间的隔离。(某些时候,模块也可以直接给其它模块传递消息。)在一些微内核的设计中,更多的功能,如I/O等,也都被封装在内核中了。但是最根本的思想还是要保持微内核尽量小,这样只需要把微内核本身进行移植就可以完成将整个内核移植到新的平台上。其它模块都只依赖于微内核或其它模块,并不直接直接依赖硬件。
微内核设计的一个优点是在不影响系统其它部分的情况下,用更高效的实现代替现有文件系统模块的工作将会更加容易。我们甚至可以在系统运行时将开发出的新系统模块或者需要替换现有模块的模块直接而且迅速的加入系统。另外一个优点是不需要的模块将不会被加载到内存中,因此微内核就可以更有效的利用内存。

2 单内核(Monolithic kernel)

   
单内核是一个很大的进程。它的内部又可以被分为若干模块(或者是层次或其它)。但是在运行的时候,它是一个独立的二进制大映象。其模块间的通讯是通过直接调用其它模块中的函数实现的,而不是消息传递。 单内核的支持者声称微内核的消息传递开销引起了效率的损失。微内核的支持者则认为因此而增加的内核设计的灵活性和可维护性可以弥补任何损失。
我并不想讨论这些问题,但必须说明非常有趣的一点是,这种争论经常会令人想到前几年CPU领域中RISC和CISC的斗争。现代的成功CPU设计中包含了所有这两种技术,就像Linux内核是微内核和单一内核的混合产物一样。Linux内核基本上是单一的,但是它并不是一个纯粹的集成内核。前面一章所介绍的内核模块系统将微内核的许多优点引入到Linux的单内核设计中。(顺便提一下,我考虑过一种有趣的情况,就是Linux的内核模块系统可以将系统内核转化成为简单的不传递消息的微内核设计。虽然我并不赞成,但是它仍然是一个有趣的想法。)为什么Linux必然是单内核的呢?一个方面是历史的原因:在Linus的观点看来,通过把内核以单一的方式进行组织并在最初始的空间中运行是相当容易的事情。这种决策避免了有关消息传递体系结构,计算模块装载方式等方面的相关工作。(内核模块系统在随后的几年中又进行了不断地改进。)
    另外一个原因是充足的开发时间的结果。Linux既没有开发时间的限制,也没有深受市场压力的发行进度。 所有的限制只有并不过分的对内核的修改与扩充。内核的单一设计在内部实现了充分的模块化,在这种条件下的修改或增加都并不怎么困难。而且问题还在于没有必要为了追求尚未证实的可维护性的微小增长而重写Linux的内核。(Linus曾多次特别强调了如下的观点:为了这点利益而损耗速度是不值得的。)后面章节中的部分内容将详细的重新考虑充足开发时间的效果。如果Linux是纯微内核设计,那么向其它体系结构上的移植将会比较容易。实际上,有一些微内核,如Mach微内核,就已经成功的证明了这种可移植性的优点。实际的情况是,Linux内核的移植虽然不是很简单,但也绝不是不可能的:大约的数字是,向一个全新的体系结构上的典型的移植工作需要30,000到60,000行代码,再加上不到20,000行的驱动程序代码。(并不是所有的移植都需要新的驱动程序代码。)粗略的计算一下,我估计一个典型的移植平均需要50,000行代码。这对于一个程序员或者最多一个程序小组来说是力所能及的,可以在一年之内完成。虽然这比微内核的移植需要更多的代码,但是Linux的支持者将会提出,这样的Linux内核移植版本比微内核更能够有效的利用底层硬件,因而移植过程中的额外工作是能够从系统性能的提高上得到补偿的。
这种特殊设计的权衡也不是很轻松就可以达到的,单内核的实现策略公然违背了传统的看法,后者认为微内核是未来发展的趋势。但是由于单一模式(大部分情况下)在Linux中运行状态良好,而且内核移植相对来说比较困难,但没有明显地阻碍程序员团体的工作,他们已经热情高涨地把内核成功的移植到了现存的大部分实际系统中,更不用说类似掌上型电脑的一些看起来很不实际的目标了。只要Linux的众多特点仍然值得移植,新的移植版本就会不断涌现。

4.死锁的定义及其预防措施

  死锁的定义:进程A占有资源a需要资源b,进程B占有资源b需要资源a,导致A、B进程都没有办法运行。都相互等待彼此释放资源。

死锁产生的有下列四个条件:

1、互斥
2、占有且等待
3、不可抢占
4、环路等待
这四个条件都是死锁发生所必须的条件,只要破坏这其中的某一个条件就行了

下面是对打破四种条件预防思索付出代价的分析:

1. 互斥条件

如果系统中的资源可以由多个进程共享,那么就永远不会发生死锁。然而,这种共享不切实际。例如,磁带机、绘图仪或打印机就不能在多个进程之间共享使用。这其中最好的情形就是对打印机使用假脱机技术,即所有打印请求都由一个单独的程序处理。这样可以消除共享请求。当假脱机程序占用打印机时,其他任何进程都不能发送打印机请求,也不能管理打印机。他们可以做的工作就是将数据提交给假脱机程序,以便以后打印。

不幸的是,并非所有的设备都可以使用相同的技术。而且,死锁涉及的除外部I/O设备之外,还包括各种操作系统表、磁盘区和记录等大量资源。此外,不是所有资源都可以采用假脱机这样简单的应用程序进行处理。因此,很难保证避免该条件。

2. 等待条件

禁止进程在已经占用某些资源时等待更多资源,这样可以预防死锁。

这要求进程一开始就声明它期望使用的全部资源。操作系统会检查这些资源是否全部可用,只有可用的时候,才允许该进程开始执行。这种情况下,显然操作系统在分配完资源之后必须更新自己的空闲及可用资源链表。该解决方案很吸引人,但显而易见的是,它不能达到预期效果而且很浪费。如果进程为了更新某些文件需要用8个小时时间,而最后只使用磁带机一分钟以更新控制记录,在整个过程中磁带机都要分配给那个进程。因此,磁带机会空闲8个小时。这期间没有其他的进程可以使用该磁带机。

可以采用该方法的另一种形式。操作系统必须让请求某些资源的进程先放弃已经占用的资源,然后再尝试请求所有要用的资源。如果尝试成功,被放弃的资源才可以重新分配给该进程,这样该进程才可以继续运行。如果失败,被放弃的资源恢复空闲,而进程则必须一直等到那些资源可用为止。每次检查后,进程都放弃已占用的资源,这样就永远不会出现死锁。

该方案也存在很多问题。当进程放弃现有资源之后,也许会有其他一些进程长时间占用一个或多个资源。很容易想象,该策略会导致长时间的延迟、无限期的推迟以及其他不可预测的问题。同样地,这种技术可用于表、信号量等共享资源,但不适用于打印机和磁带机这类资源。想象一下,某个进程在打印到一半的时候放弃使用打印机,而某个其他进程占用该打印机将产生什么样的后果。

3. 非抢占条件

确保不满足"非抢占"条件很困难。如果允许将资源分配给可以强制夺取该资源的进程,也许可以解决死锁问题,但会出现更糟糕的问题。从一个只处理了部分记录的进程强制性地夺走磁带机--因为其他进程请求使用该资源,由此带来的加载/卸载、定位等问题一定令人无法接受。对打印机而言,情形更糟糕。

4. 循环等待条件

显然,克服前三个条件很难,这样就只留下了最后一个条件。如果阻止了循环等待条件,那么也可以阻止死锁。

一种方法是强制进程每次只能占用一种资源。如果它希望使用另一种资源,就必须先放弃占用的资源,然后再请求其他资源。显然,该方法也存在第(3)点中提到的相同缺点。如果进程P占用R1,并且想要使用R2,它就必须先放弃R1,因此另一个进程P2可以获取资源R1。这时将再次出现在进程P1处理了记录的一半之后,将磁带机分配给P2的问题。因此,该解决方案也不能令人满意。

解决该问题的一个更好的方法就是对所有的资源编号,如图7-10所示。

图7-10  对资源的编号

编号

资源名称

0

磁带机

1

打印机

2

绘图仪

3

读卡机

4

卡片穿孔机

现在可以采用一个简单的规则处理循环等待条件。任何进程都必须在执行期间按照编号递增的顺序请求所需的资源。这里再次假定一开始就能获取所有资源的解决方案并不存在。例如,如果进程P1在执行期间的某个时刻需要使用打印机和绘图仪,它必须先请求打印机,然后是绘图仪,因为1<2。

这样可以预防死锁。下面介绍其中的工作原理。假定两个进程P1和P2都在等待使用磁带机和绘图仪。只有当P1占用磁带机并且想用绘图仪,而P2占用绘图仪并且想用磁带机,并且两个进程请求资源的顺序正好反过来,才会出现死锁。这与假设恰好矛盾。因为0<2,每个进程都要在请求使用绘图仪前先请求使用磁带机,无论该进程是P1还是P2。因此,不可能产生死锁。

该方法对两个进程成立,对多个进程也成立。不过,该方案也存在一些或大或小的问题。

想象一下,系统中有两个磁带机T1和T2以及两个进程P1和P2。如果P1占用T1,并请求使用T2,而P2占用T2并请求使用T1,就会产生死锁。这两个都是磁带机,那么应该采用什么样的编号方案呢?给这两个磁带机相同的编号(例如0),即使有一个当前请求资源的编号必须大于等于前一个请求资源编号的原则,也会产生死锁。

这个小问题可以通过使用某种资源编码方案解决。第1位表示资源类型,第2位表示该资源类型中的资源编号。因此,数字00、01、02等表示不同的磁带机,而10、11表示不同的打印机。进程只请求资源类型(如磁带机)。操作系统在内部将该请求转换成对特定资源如00或01的请求。采用上述解决方案,人们认识到如果发生上面这种情况,就会违反前面提出的假设。例如,P1占用00并且请求使用01,因为00<01所以这是可以接受的请求。同时,P2占用01而且请求使用00,因为00<01,所以不可能发生。

不仅要对外部I/O介质编号,而且还要对包括所有进程表、磁盘区(如假脱机文件)在内的全部资源编号。这是很麻烦的工作。

让所有的进程按照预先确定的顺序请求资源几乎是不可能的,因为实际上进程是按照各种顺序请求资源的。等待时间以及因此造成的浪费会很大,这才是主要的问题。

因此,对预防死锁而言,目前还没有令人满意的通用方法,对此还需要深入研究。

5.多进程和多线程的概念以及如何选择他们?

http://software.intel.com/zh-cn/blogs/2010/07/20/400004478/

关于多进程和多线程,教科书上最经典的一句话是“进程是资源分配的最小单位,线程是CPU调度的最小单位”,这句话应付考试基本上够了,但如果在工作中遇到类似的选择问题,那就没有这么简单了,选的不好,会让你深受其害。
经常在网络上看到有的XDJM问“多进程好还是多线程好?”、“Linux下用多进程还是多线程?”等等期望一劳永逸的问题,我只能说:没有最好,只有更好。根据实际情况来判断,哪个更加合适就是哪个好。
我们按照多个不同的维度,来看看多线程和多进程的对比(注:因为是感性的比较,因此都是相对的,不是说一个好得不得了,另外一个差的无法忍受)。


看起来比较简单,优势对比上是“线程 3.5 v 2.5 进程”,我们只管选线程就是了?
呵呵,有这么简单我就不用在这里浪费口舌了,还是那句话,没有绝对的好与坏,只有哪个更加合适的问题。我们来看实际应用中究竟如何判断更加合适。
1)需要频繁创建销毁的优先用线程
原因请看上面的对比。
这种原则最常见的应用就是Web服务器了,来一个连接建立一个线程,断了就销毁线程,要是用进程,创建和销毁的代价是很难承受的
2)需要进行大量计算的优先使用线程
所谓大量计算,当然就是要耗费很多CPU,切换频繁了,这种情况下线程是最合适的。
这种原则最常见的是图像处理、算法处理。
3)强相关的处理用线程,弱相关的处理用进程
什么叫强相关、弱相关?理论上很难定义,给个简单的例子就明白了。
一般的Server需要完成如下任务:消息收发、消息处理。“消息收发”和“消息处理”就是弱相关的任务,而“消息处理”里面可能又分为“消息解码”、“业务处理”,这两个任务相对来说相关性就要强多了。因此“消息收发”和“消息处理”可以分进程设计,“消息解码”、“业务处理”可以分线程设计。
当然这种划分方式不是一成不变的,也可以根据实际情况进行调整。
4)可能要扩展到多机分布的用进程,多核分布的用线程
原因请看上面对比。
5)都满足需求的情况下,用你最熟悉、最拿手的方式
至于“数据共享、同步”、“编程、调试”、“可靠性”这几个维度的所谓的“复杂、简单”应该怎么取舍,我只能说:没有明确的选择方法。但我可以告诉你一个选择原则:如果多进程和多线程都能够满足要求,那么选择你最熟悉、最拿手的那个。
需要提醒的是:虽然我给了这么多的选择原则,但实际应用中基本上都是“进程+线程”的结合方式,千万不要真的陷入一种非此即彼的误区。

另外,http://my.oschina.net/xyxlxd/blog/9730

答案一:

1,进程:子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。

2,线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。

两者都可以提高程序的并发度,提高程序运行效率和响应时间。

线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。

答案二:

根本区别就一点:用多进程每个进程有自己的地址空间(address space),线程则共享地址空间。所有其它区别都是由此而来的:

1。速度:线程产生的速度快,线程间的通讯快、切换快等,因为他们在同一个地址空间内。

2。资源利用率:线程的资源利用率比较好也是因为他们在同一个地址空间内。

3。同步问题:线程使用公共变量/内存时需要使用同步机制还是因为他们在同一个地址空间内。

6.linux系统通信机制

1管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
2有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
3信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
4消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
5信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
6共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
7 套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。

7.简述段式内存管理下得虚拟内地到物理地址的映射

基本映射过程
1.给定一个逻辑地址(包括逻辑页号与页内地址),通过逻辑地址确定页号(如逻辑地址是X,页大小为1024KB,那么页号就是Xmod1024)
2.比较逻辑页号与页表长度寄存器的内容(是否越界)
3.根据页表始址寄存器的内容得到页表在内存的位置,并根据逻辑页号在页表中查到物理页号
4.将物理页号与页内地址拼接,得到物理地址

可以参见本系列操作系统的习题的第一道题目。

页表里面存放的是页号和物理块号的对应关系。这样访问物理地址就需要两次访问内存:第一次访问一级页表以得到物理内存地址,第二次才是真正在物理内存上进行读、写数据。于是就引出了TLB(块表)


8.什么是TLB(快表)?

        CPU有了MMU的部件后,可以使我们从用户的角度上看,内存空间大了许多,同时也可以进行访问权限的检查,这样也安全了许多。但是,访问物理内存的过程也复杂了,如:使用一级页表进行映射时,对内存需进行两次访问,第一次访问一级页表以得到物理内存地址,第二次才是真正在物理内存上进行读、写数据;如果使用二级页表进行映射,那么需要三次访问才可以。这样就大大降低了CPU的使用效率。

     出现了以上问题后,人们开发研究,后发现:程序中所用的指令、数据大多数都局限在一个很小的范围内,其中的地址、数据经常多次使用,这也称为“程序的局限性”。因此,我们可以把近期使用页表条目缓存在一个很小的、高速的存储器中,以避免每次都到主内存(物理内存)中去查找,这样就可以大幅度的提高CPU的性能,而这个存储器就是TLBTranslation
Lookaside Buffers
)。

     这样CPU访问内存空间的过程就变成:CPU首先发出虚拟地址,MMU首先访问TLB,如果TLB中有所需要的虚拟地址描述符,则直接利用此描述符进行地址转换和检查;如果TLB中没有找到该描述符,MMU则访问页表找到该描述符进行地址转换和权限检查,并将此描述符填入TLB中以备下次使用,如果TLB已满,则利用round-robin算法找到一个条目,覆盖它。

9  linux 2.6 O(1)调度的实现

         O(1)调度就是查找时间恒定,并不受系统到底此时有多少执行进程的影响。两个重要的概念:优先级数组。

       优先级数组是一种能够提供O(1)级算法复杂度的数据结构,拥有一个优先级别位图。一开始,所有的位都会被置0。当某个拥有一定优先级线程开始准备执行时候,位图中的相应为就会被置1.比如,优先级为7的进程变为可执行状态的时候,第七位就被置为1.这样,查找系统最高优先级的工作就变成了查找位图中被设置为一个位是1。因为优先级的个数是定制,所以查找时间恒定,并不受系统到底有多少可执行的进程的影响。此外,linux对于每一个体系结构都实现了相应的快速查找算法。

抱歉!评论已关闭.