Skip to content

操作系统

1、操作系统特点

并发性、共享性、虚拟性、不确定性。

2、什么是进程

  1. 进程是指在系统中正在运⾏的⼀个应⽤程序,程序⼀旦运⾏就是进程;

  2. 进程可以认为是程序执⾏的⼀个实例,进程是系统进⾏资源分配的最⼩单位,且每个进程

拥有独⽴的地址空间;

  1. ⼀个进程⽆法直接访问另⼀个进程的变量和数据结构,如果希望⼀个进程去访问另⼀个进

程的资源,需要使⽤进程间的通信,⽐如:管道、消息队列等

  1. 线程是进程的⼀个实体,是进程的⼀条执⾏路径;⽐进程更⼩的独⽴运⾏的基本单位,线

程也被称为轻量级进程,⼀个程序⾄少有⼀个进程,⼀个进程⾄少有⼀个线程;

3、进程

进程是程序的⼀次执⾏,该程序可以与其他程序并发执⾏;

进程有运⾏、阻塞、就绪三个基本状态;

进程调度算法:先来先服务调度算法、短作业优先调度算法、⾮抢占式优先级调度算法、抢占式优先级调度算法、⾼响应⽐优先调度算法、时间⽚轮转法调度算法;

4、进程与线程的区别

  1. 同⼀进程的线程共享本进程的地址空间,⽽进程之间则是独⽴的地址空间;

  2. 同⼀进程内的线程共享本进程的资源,但是进程之间的资源是独⽴的;

  3. ⼀个进程崩溃后,在保护模式下不会对其他进程产⽣影响,但是⼀个线程崩溃整个进程崩

溃,所以多进程⽐多线程健壮;

  1. 进程切换,消耗的资源⼤。所以涉及到频繁的切换,使⽤线程要好于进程;

  2. 两者均可并发执⾏;

  3. 每个独⽴的进程有⼀个程序的⼊⼝、程序出⼝。但是线程不能独⽴执⾏,必须依存在应⽤

程序中,由应⽤程序提供多个线程执⾏控制。

5、进程状态转换图

操作系统 图 105

  1. 新状态:进程已经创建

  2. 就绪态:进程做好了准备,准备执⾏,等待分配处理机

  3. 执⾏态:该进程正在执⾏;

  4. 阻塞态:等待某事件发⽣才能执⾏,如等待I/O完成;

  5. 终⽌状态

6、进程的创建过程?需要哪些函数?需要哪些数据结构?

  1. fork函数创造的⼦进程是⽗进程的完整副本,复制了⽗亲进程的资源,包括内存的内容

task_struct内容;

  1. vfork创建的⼦进程与⽗进程共享数据段,⽽且由vfork创建的⼦进程将先于⽗进程运⾏;

  2. linux上创建线程⼀般使⽤的是pthread库,实际上linux也给我们提供了创建线程的系统调

⽤,就是clone;

7、进程创建⼦进程,fork详解

  1. 函数原型

pid_t fork(void); //void代表没有任何形式参数

  1. 除了0号进程(系统创建的)之外,linux系统中都是由其他进程创建的。创建新进程的进

程,即调⽤fork函数的进程为⽗进程,新建的进程为⼦进程。

  1. fork函数不需要任何参数,对于返回值有三种情况:

①对于⽗进程,fork函数返回新建⼦进程的pid;

②对于⼦进程,fork函数返回 0;

③如果出错, fork 函数返回 -1。

cpp
int pid=fork();
if(pid < 0){

//失败,⼀般是该⽤户的进程数达到限制或者内存被⽤光了

cpp
........
}
elseif(pid == 0){

//⼦进程执⾏的代码

cpp
......
}
else{

//⽗进程执⾏的代码

cpp
.........
}

8、⼦进程和⽗进程怎么通信?

  1. 在 Linux 系统中实现⽗⼦进程的通信可以采⽤ pipe() 和 fork() 函数进⾏实现;

  2. 对于⽗⼦进程,在程序运⾏时⾸先进⼊的是⽗进程,其次是⼦进程,在此我个⼈认为,在

创建⽗⼦进程的时候程序是先运⾏创建的程序,其次在复制⽗进程创建⼦进程。fork() 函数主要是以⽗进程为蓝本复制⼀个进程,其 ID 号和⽗进程的 ID 号不同。对于结果 fork出来的⼦进程的⽗进程 ID 号是执⾏ fork() 函数的进程的 ID 号。

  1. 管道:是指⽤于连接⼀个读进程和⼀个写进程,以实现它们之间通信的共享⽂件,⼜称

pipe ⽂件。

  1. 写进程在管道的尾端写⼊数据,读进程在管道的⾸端读出数据。

9、进程和作业的区别?

  1. 进程是程序的⼀次动态执⾏,属于动态概念;

  2. ⼀个进程可以执⾏⼀个或⼏个程序,同⼀个程序可由⼏个进程执⾏;

  3. 程序可以作为⼀种软件资源⻓期保留,⽽进程是程序的⼀次执⾏;

  4. 进程具有并发性,能与其他进程并发执⾏;

  5. 进程是⼀个独⽴的运⾏单位;

10、死锁是什么?必要条件?如何解决?

所谓死锁,是指多个进程循环等待它⽅占有的资源⽽⽆限期地僵持下去的局⾯。很显然,如果没有外⼒的作⽤,那麽死锁涉及到的各个进程都将永远处于封锁状态。当两个或两个以上的进程同时对多个互斥资源提出使⽤要求时,有可能导致死锁。

〈1〉互斥条件。即某个资源在⼀段时间内只能由⼀个进程占有,不能同时被两个或两个以上的进程占有。这种独占资源如CD-ROM驱动器,打印机等等,必须在占有该资源的进程主动释放它之后,其它进程才能占有该资源。这是由资源本身的属性所决定的。如独⽊桥就是⼀种独占资源,两⽅的⼈不能同时过桥。

〈2〉不可抢占条件。进程所获得的资源在未使⽤完毕之前,资源申请者不能强⾏地从资源占有者⼿中夺取资源,⽽只能由该资源的占有者进程⾃⾏释放。如过独⽊桥的⼈不能强迫对⽅后退,也不能⾮法地将对⽅推下桥,必须是桥上的⼈⾃⼰过桥后空出桥⾯(即主动释放占有资源),对⽅的⼈才能过桥。

〈3〉占有且申请条件。进程⾄少已经占有⼀个资源,但⼜申请新的资源;由于该资源已被另外进程占有,此时该进程阻塞;但是,它在等待新资源之时,仍继续占⽤已占有的资源。还以过独⽊桥为例,甲⼄两⼈在桥上相遇。甲⾛过⼀段桥⾯(即占有了⼀些资源),还需要⾛其余的桥⾯(申请新的资源),但那部分桥⾯被⼄占有(⼄⾛过⼀段桥⾯)。甲过不去,前进不能,⼜不后退;⼄也处于同样的状况。

〈4〉循环等待条件。存在⼀个进程等待序列{P1,P2,...,Pn},其中P1等待P2所占有的某⼀资源,P2等待P3所占有的某⼀源,......,⽽Pn等待P1所占有的的某⼀资源,形成⼀个进程循环等待环。就像前⾯的过独⽊桥问题,甲等待⼄占有的桥⾯,⽽⼄⼜等待甲占有的桥⾯,从⽽彼此循环等待。

死锁的预防是保证系统不进⼊死锁状态的⼀种策略。它的基本思想是要求进程申请资源时遵循某种协议,从⽽打破产⽣死锁的四个必要条件中的⼀个或⼏个,保证系统不会进⼊死锁状态。

<1>打破互斥条件。即允许进程同时访问某些资源。但是,有的资源是不允许被同时访问的,像打印机等等,这是由资源本身的属性所决定的。所以,这种办法并⽆实⽤价值。

<2>打破不可抢占条件。即允许进程强⾏从占有者那⾥夺取某些资源。就是说,当⼀个进程已占有了某些资源,它⼜申请新的资源,但不能⽴即被满⾜时,它必须释放所占有的全部资源,以后再重新申请。它所释放的资源可以分配给其它进程。这就相当于该进程占有的资源被隐蔽地强占了。这种预防死锁的⽅法实现起来困难,会降低系统性能。

<3>打破占有且申请条件。可以实⾏资源预先分配策略。即进程在运⾏前⼀次性地向系统申请它所需要的全部资源。如果某个进程所需的全部资源得不到满⾜,则不分配任何资源,此进程暂不运⾏。只有当系统能够满⾜当前进程的全部资源需求时,才⼀次性地将所申请的资源全部分配给该进程。由于运⾏的进程已占有了它所需的全部资源,所以不会发⽣占有资源⼜申请资源的现象,因此不会发⽣死锁。

<4>打破循环等待条件,实⾏资源有序分配策略。采⽤这种策略,即把资源事先分类编号,按号分配,使进程在申请,占⽤资源时不会形成环路。所有进程对资源的请求必须严格按资源序号递增的顺序提出。进程占⽤了⼩号资源,才能申请⼤号资源,就不会产⽣环路,从⽽预防了死锁死锁避免:银⾏家算法

11、鸵⻦策略

假设的前提是,这样的问题出现的概率很低。⽐如,在操作系统中,为应对死锁问题,可以采⽤这样的⼀种办法。当系统发⽣[死锁](时不会对⽤户造成多⼤影响,或系统很少发⽣[死锁]的场合采⽤允许死锁发⽣的鸵⻦算法,这样⼀来可能开销⽐不允许发⽣死锁及检测和解除死锁的⼩。如果[死锁]很⻓时间才发⽣⼀次,⽽系统每周都会因硬件故障、[编译器]错误或操作系统错误⽽崩溃⼀次,那么⼤多数⼯程师不会以性能损失或者易⽤性损失的代价来设计较为复杂的死锁解决策略,来消除死锁。鸵⻦策略的实质:出现死锁的概率很⼩,并且出现之后处理死锁会花费很⼤的代价,还不如不做处理,OS中这种置之不理的策略称之为鸵⻦策略(也叫鸵⻦算法)。

12、银⾏家算法

在避免[死锁]的⽅法中,所施加的限制条件较弱,有可能获得令⼈满意的系统性能。在该⽅法中把系统的状态分为安全状态和不安全状态,只要能使系统始终都处于安全状态,便可以避免发⽣[死锁]。

银⾏家算法的基本思想是分配资源之前,判断系统是否是安全的;若是,才分配。它是最具有代表性的避免[死锁]的算法。

设进程cusneed提出请求REQUEST [i],则银⾏家算法按如下规则进⾏判断。

(1)如果REQUEST [cusneed] [i]<= NEED[cusneed][i],则转(2);否则,出错。

(2)如果REQUEST [cusneed] [i]<= AVAILABLE[i],则转(3);否则,等待。

(3)系统试探分配资源,修改相关数据:

AVAILABLE[i]-=REQUEST[cusneed][i];ALLOCATION[cusneed][i]+=REQUEST[cusneed][i];NEED[cusneed][i]-=REQUEST[cusneed][i];

(4)系统执⾏安全性检查,如安全,则分配成⽴;否则试探险性分配作废,系统恢复原状,进

程等待。

13、进程间通信⽅式有⼏种,他们之间的区别是什么?

  1. 管道

管道,通常指⽆名管道。

①半双⼯的,具有固定的读端和写端;

②只能⽤于具有亲属关系的进程之间的通信;

③可以看成是⼀种特殊的⽂件,对于它的读写也可以使⽤普通的read、write函数。但是它不是普通的⽂件,并不属于其他任何⽂件系统,只能⽤于内存中。

④ Int pipe(int fd[2]);当⼀个管道建⽴时,会创建两个⽂件⽂件描述符,要关闭管道只需将这两个⽂件描述符关闭即可。

  1. FiFO(有名管道)

① FIFO可以再⽆关的进程之间交换数据,与⽆名管道不同;

② FIFO有路径名与之相关联,它以⼀种特殊设备⽂件形式存在于⽂件系统中;

③ Int mkfifo(const char* pathname,mode_t mode);

  1. 消息队列

①消息队列,是消息的连接表,存放在内核中。⼀个消息队列由⼀个标识符来标识;

②消息队列是⾯向记录的,其中的消息具有特定的格式以及特定的优先级;

③消息队列独⽴于发送与接收进程。进程终⽌时,消息队列及其内容并不会被删除;

④消息队列可以实现消息的随机查询

  1. 信号量

①信号量是⼀个计数器,信号量⽤于实现进程间的互斥与同步,⽽不是⽤于存储进程间通信数据;

②信号量⽤于进程间同步,若要在进程间传递数据需要结合共享内存;

③信号量基于操作系统的PV操作,程序对信号量的操作都是原⼦操作;

  1. 共享内存

①共享内存,指两个或多个进程共享⼀个给定的存储区;

②共享内存是最快的⼀种进程通信⽅式,因为进程是直接对内存进⾏存取;

③因为多个进程可以同时操作,所以需要进⾏同步;

④信号量+共享内存通常结合在⼀起使⽤。

14、线程同步的⽅式?怎么⽤?

  1. 线程同步是指多线程通过特定的设置来控制线程之间的执⾏顺序,也可以说在线程之间通

过同步建⽴起执⾏顺序的关系;

  1. 主要四种⽅式,临界区、互斥对象、信号量、事件对象;其中临界区和互斥对象主要⽤于

互斥控制,信号量和事件对象主要⽤于同步控制;

  1. 临界区:通过对多线程的串⾏化来访问公共资源或⼀段代码,速度快、适合控制数据访

问。在任意⼀个时刻只允许⼀个线程对共享资源进⾏访问,如果有多个线程试图访问公共资源,那么在有⼀个线程进⼊后,其他试图访问公共资源的线程将被挂起,并⼀直等到进⼊临界区的线程离开,临界区在被释放后,其他线程才可以抢占。

  1. 互斥对象:互斥对象和临界区很像,采⽤互斥对象机制,只有拥有互斥对象的线程才有访

问公共资源的权限。因为互斥对象只有⼀个,所以能保证公共资源不会同时被多个线程同时访问。当前拥有互斥对象的线程处理完任务后必须将线程交出,以便其他线程访问该资源。

  1. 信号量:它允许多个线程在同⼀时刻访问同⼀资源,但是需要限制在同⼀时刻访问此资源

的最⼤线程数⽬。在⽤CreateSemaphore()创建信号量时即要同时指出允许的最⼤资源计数和当前可⽤资源计数。⼀般是将当前可⽤资源计数设置为最⼤资源计数,每增加⼀个线程对共享资源的访问,当前可⽤资源计数就会减1 ,只要当前可⽤资源计数是⼤于0 的,就可以发出信号量信号。但是当前可⽤计数减⼩到0 时则说明当前占⽤资源的线程数已经达到了所允许的最⼤数⽬,不能在允许其他线程的进⼊,此时的信号量信号将⽆法发出。线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore ()函数将当前可⽤资源计数加1 。在任何时候当前可⽤资源计数决不可能⼤于最⼤资源计数。

  1. 事件对象:通过通知操作的⽅式来保持线程的同步,还可以⽅便实现对多个线程的优先级

⽐较的操作。

15、⻚和段的区别?

  1. ⻚是信息的物理单位,分⻚是由于系统管理的需要。段是信息的逻辑单位,分段是为了满

⾜⽤户的要求。

  1. ⻚的⼤⼩固定且由系统决定,段的⻓度不固定,决定于⽤户所编写的程序,通常由编译程

序在对源程序紧进⾏编译时,根据信息的性质来划分。

  1. 分⻚的作业的地址空间是⼀维的,程序员只需要利⽤⼀个记忆符,即可表示⼀个地址。分

段的作业地址空间则是⼆维的,程序员在标识⼀个地址时,既需要给出段名,⼜需要给出段的地址值。

16、孤⼉进程和僵⼫进程的区别?怎么避免这两类进程?守护进程?

1、⼀般情况下,⼦进程是由⽗进程创建,⽽⼦进程和⽗进程的退出是⽆顺序的,两者之间都

不知道谁先退出。正常情况下⽗进程先结束会调⽤ wait 或者 waitpid 函数等待⼦进程完成再退出,⽽⼀旦⽗进程不等待直接退出,则剩下的⼦进程会被init(pid=1)进程接收,成会孤⼉进程。(进程树中除了init都会有⽗进程)。

2、如果⼦进程先退出了,⽗进程还未结束并且没有调⽤ wait 或者 waitpid 函数获取⼦进程的

状态信息,则⼦进程残留的状态信息( task_struct 结构和少量资源信息)会变成僵⼫进程。

⼦进程退出时向⽗进程发送SIGCHILD信号,⽗进程处理SIGCHILD信号。在信号处理函数中调⽤wait进⾏处理僵⼫进程。

原理是将⼦进程成为孤⼉进程,从⽽其的⽗进程变为init进程,通过init进程可以处理僵⼫进程。

3、守护进程( daemon) 是指在后台运⾏,没有控制终端与之相连的进程。它独⽴于控制终

端,通常周期性地执⾏某种任务。守护进程脱离于终端是为了避免进程在执⾏过程中的信息在任何终端上显示并且进程也不会被任何终端所产⽣的终端信息所打断。

17、守护进程是什么?怎么实现?

  1. 守护进程(Daemon)是运⾏在后台的⼀种特殊进程。它独⽴于控制终端并且周期性地执⾏某种任务或等待处理某些发⽣的事件。守护进程是⼀种很有⽤的进程。

  2. 守护进程特点

  1. 守护进程最重要的特性是后台运⾏。

  2. 守护进程必须与其运⾏前的环境隔离开来。这些环境包括未关闭的⽂件描述符,控制终

端,会话和进程组,⼯作⽬录以及⽂件创建掩模等。这些环境通常是守护进程从执⾏它的⽗进程(特别是shell)中继承下来的。

  1. 守护进程的启动⽅式有其特殊之处。它可以在Linux系统启动时从启动脚本/etc/rc.d中启

动,可以由作业规划进程crond启动,还可以由⽤户终端(shell)执⾏。

  1. 实现
  1. 在⽗进程中执⾏fork并exit推出;

  2. 在⼦进程中调⽤setsid函数创建新的会话;

  3. 在⼦进程中调⽤chdir函数,让根⽬录 ”/” 成为⼦进程的⼯作⽬录;

  4. 在⼦进程中调⽤umask函数,设置进程的umask为0;

  5. 在⼦进程中关闭任何不需要的⽂件描述符

18、线程和进程的区别?线程共享的资源是什么?

  1. ⼀个程序⾄少有⼀个进程,⼀个进程⾄少有⼀个线程

  2. 线程的划分尺度⼩于进程,使得多线程程序的并发性⾼

  3. 进程在执⾏过程中拥有独⽴的内存单元,⽽多个线程共享内存,从⽽极⼤地提⾼了程序的

运⾏效率

  1. 每个独⽴的线程有⼀个程序运⾏的⼊⼝、顺序执⾏序列和程序的出⼝。但是线程不能够独

⽴执⾏,必须依存在应⽤程序中,由应⽤程序提供多个线程执⾏控制

  1. 多线程的意义在于⼀个应⽤程序中,有多个执⾏部分可以同时执⾏。但操作系统并没有将

多个线程看做多个独⽴的应⽤,来实现进程的调度和管理以及资源分配

  1. ⼀个进程中的所有线程共享该进程的地址空间,但它们有各⾃独⽴的(/私有的)栈

(stack),Windows 线程的缺省堆栈⼤⼩为1M。堆(heap)的分配与栈有所不同,⼀般是⼀个进程有⼀个C运⾏时堆,这个堆为本进程中所有线程共享,windows 进程还有所谓进程默认堆,⽤户也可以创建⾃⼰的堆。

操作系统 图 114

线程私有:线程栈,寄存器,程序寄存器共享:堆,地址空间,全局变量,静态变量进程私有:地址空间,堆,全局变量,栈,寄存器共享:代码段,公共数据,进程⽬录,进程ID

19、线程⽐进程具有哪些优势?

  1. 线程在程序中是独⽴的,并发的执⾏流,但是,进程中的线程之间的隔离程度要⼩;

  2. 线程⽐进程更具有更⾼的性能,这是由于同⼀个进程中的线程都有共性:多个线程将共享

同⼀个进程虚拟空间;

  1. 当操作系统创建⼀个进程时,必须为进程分配独⽴的内存空间,并分配⼤量相关资源;

20、什么时候⽤多进程?什么时候⽤多线程?

  1. 需要频繁创建销毁的优先⽤线程;

  2. 需要进⾏⼤量计算的优先使⽤线程;

  3. 强相关的处理⽤线程,弱相关的处理⽤进程;

  4. 可能要扩展到多机分布的⽤进程,多核分布的⽤线程;

21、协程是什么?

  1. 是⼀种⽐线程更加轻量级的存在。正如⼀个进程可以拥有多个线程⼀样,⼀个线程可以拥

有多个协程;协程不是被操作系统内核管理,⽽完全是由程序所控制。

  1. 协程的开销远远⼩于线程;

  2. 协程拥有⾃⼰寄存器上下⽂和栈。协程调度切换时,将寄存器上下⽂和栈保存到其他地

⽅,在切换回来的时候,恢复先前保存的寄存器上下⽂和栈。

  1. 每个协程表示⼀个执⾏单元,有⾃⼰的本地数据,与其他协程共享全局数据和其他资源。

  2. 跨平台、跨体系架构、⽆需线程上下⽂切换的开销、⽅便切换控制流,简化编程模型;

  3. 协程⼜称为微线程,协程的完成主要靠yeild关键字,协程执⾏过程中,在⼦程序内部可中

断,然后转⽽执⾏别的⼦程序,在适当的时候再返回来接着执⾏;

  1. 协程极⾼的执⾏效率,和多线程相⽐,线程数量越多,协程的性能优势就越明显;

  2. 不需要多线程的锁机制;

22、递归锁?

  1. 线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引⼊互斥锁。互斥锁

为资源引⼊⼀个状态:锁定/⾮锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“⾮锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有⼀个线程进⾏写⼊操作,从⽽保证了多线程情况下数据的正确性。

  1. 读写锁从⼴义的逻辑上讲,也可以认为是⼀种共享版的互斥锁。如果对⼀个临界区⼤部分

是读操作⽽只有少量的写操作,读写锁在⼀定程度上能够降低线程互斥产⽣的代价。

  1. Mutex可以分为递归锁(recursive mutex)和⾮递归锁(non-recursive mutex)。可递归锁也可

称为可重⼊锁(reentrant mutex),⾮递归锁⼜叫不可重⼊锁(non-reentrant mutex)。⼆者唯⼀的区别是,同⼀个线程可以多次获取同⼀个递归锁,不会产⽣死锁。⽽如果⼀个线程多次获取同⼀个⾮递归锁,则会产⽣死锁。

23、⽤户态到内核态的转化原理?

  1. 系统调⽤

这是⽤户态进程主动要求切换到内核态的⼀种⽅式,⽤户态进程通过系统调⽤申请使⽤操作系统提供的服务程序完成⼯作,⽐如前例中fork()实际上就是执⾏了⼀个创建新进程的系统调⽤。⽽系统调⽤的机制其核⼼还是使⽤了操作系统为⽤户特别开放的⼀个中断来实现,例如Linux 的 int 80h 中断。

  1. 异常

当 CPU 在执⾏运⾏在⽤户态下的程序时,发⽣了某些事先不可知的异常,这时会触发由当前运⾏进程切换到处理此异常的内核相关程序中,也就转到了内核态,⽐如缺⻚异常。

  1. 外围设备的中断

当外围设备完成⽤户请求的操作后,会向 CPU 发出相应的中断信号,这时 CPU 会暂停执⾏下⼀条即将要执⾏的指令转⽽去执⾏与中断信号对应的处理程序,如果先前执⾏的指令是⽤户态下的程序,那么这个转换的过程⾃然也就发⽣了由⽤户态到内核态的切换。⽐如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执⾏后续操作等。

17. 中断的实现与作⽤,中断的实现过程?①关中断,进⼊不可再次响应中断的状态,由硬件实现。

②保存断点,为了在[中断处理]结束后能正确返回到中断点。由硬件实现。

③将[中断服务程序]⼊⼝地址送PC,转向[中断服务程序]。可由硬件实现,也可由软件实现。

④保护现场、置屏蔽字、开中断,即保护CPU中某些寄存器的内容、设置[中断处理]次序、允许更⾼级的中断请求得到响应,实现中断嵌套由软件实现。

⑤设备服务,实际上有效的中断处理⼯作是在此程序段中实现的。由软件程序实现⑥退出中断。在退出时,⼜应进⼊不可中断状态,即关中断、恢复屏蔽字、恢复现场、开中断、中断返回。由软件实现。

25、系统中断是什么,⽤户态和内核态的区别

  1. 内核态与⽤户态是操作系统的两种运⾏级别,当程序运⾏在3级特权级上时,就可以称之为运

⾏在⽤户态,因为这是最低特权级,是普通的⽤户进程运⾏的特权级,⼤部分⽤户直接⾯对的程序都是运⾏在⽤户态;反之,当程序运⾏在0级特权级上时,就可以称之为运⾏在内核态。

运⾏在⽤户态下的程序不能直接访问操作系统内核数据结构和程序。当我们在系统中执⾏⼀个程序时,⼤部分时间是运⾏在⽤户态下的,在其需要操作系统帮助完成某些它没有权⼒和能⼒完成的⼯作时就会切换到内核态。

  1. 这两种状态的主要差别是:处于⽤户态执⾏时,进程所能访问的内存空间和对象受到限

制,其所处于占有的处理机是可被抢占的;⽽处于核⼼态执⾏中的进程,则能访问所有的内存空间和对象,且所占有的处理机是不允许被抢占的。

26、 CPU中断

  1. CPU中断是什么

①计算机处于执⾏期间;

②系统内发⽣了⾮寻常或⾮预期的急需处理事件;

③ CPU暂时中断当前正在执⾏的程序⽽转去执⾏相应的事件处理程序;

④处理完毕后返回原来被中断处继续执⾏;

  1. CPU中断的作⽤

①可以使CPU和外设同时⼯作,使系统可以及时地响应外部事件;

②可以允许多个外设同时⼯作,⼤⼤提⾼了CPU的利⽤率;

③可以使CPU及时处理各种软硬件故障。

27、执⾏⼀个系统调⽤时,OS 发⽣的过程,越详细越好

1.执⾏⽤户程序(如:fork)2.根据glibc中的函数实现,取得系统调⽤号并执⾏int $0x80产⽣中断。

3.进⾏地址空间的转换和堆栈的切换,执⾏SAVE_ALL。(进⾏内核模式)4.进⾏中断处理,根据系统调⽤表调⽤内核函数。

5.执⾏内核函数。

6.执⾏ RESTORE_ALL 并返回⽤户模式

28、函数调⽤和系统调⽤的区别?

  1. 系统调⽤

①操作系统提供给⽤户程序调⽤的⼀组特殊的接⼝。⽤户程序可以通过这组特殊接⼝来获得操作系统内核提供的服务;

②系统调⽤可以⽤来控制硬件;设置系统状态或读取内核数据;进程管理,系统调⽤接⼝⽤来保证系统中进程能以多任务在虚拟环境下运⾏;

③ Linux中实现系统调⽤利⽤了0x86体系结构中的软件中断;

  1. 函数调⽤

①函数调⽤运⾏在⽤户空间;

②它主要是通过压栈操作来进⾏函数调⽤;

  1. 区别

操作系统 图 119

29、虚拟内存?使⽤虚拟内存的优点?什么是虚拟地址空间?

  1. 虚拟内存,虚拟内存是⼀种内存管理技术,它会使程序⾃⼰认为⾃⼰拥有⼀块很⼤且连续

的内存,然⽽,这个程序在内存中不是连续的,并且有些还会在磁盘上,在需要时进⾏数据交换;

  1. 优点:可以弥补物理内存⼤⼩的不⾜;⼀定程度的提⾼反应速度;减少对物理内存的读取

从⽽保护内存延⻓内存使⽤寿命;

  1. 缺点:占⽤⼀定的物理硬盘空间;加⼤了对硬盘的读写;设置不得当会影响整机稳定性与

速度。

  1. 虚拟地址空间是对于⼀个单⼀进程的概念,这个进程看到的将是地址从0000开始的整个内

存空间。虚拟存储器是⼀个抽象概念,它为每⼀个进程提供了⼀个假象,好像每⼀个进程都在独占的使⽤主存。每个进程看到的存储器都是⼀致的,称为虚拟地址空间。从最低的地址看起:程序代码和数据,堆,共享库,栈,内核虚拟存储器。⼤多数计算机的字⻓都是32位,这就限制了虚拟地址空间为4GB。

24. 线程安全?如何实现?1) 如果你的代码所在的进程中有多个线程在同时运⾏,⽽这些线程可能会同时运⾏这段代

码。如果每次运⾏结果和[单线程]运⾏的结果是⼀样的,⽽且其他的变量的值也和预期的是⼀样的,就是线程安全的。

  1. 线程安全问题都是由[全局变量及[静态变量]引起的。

  2. 若每个线程中对全局变量、静态变量只有读操作,⽽⽆写操作,⼀般来说,这个全局变量

是线程安全的;若有多个线程同时执⾏写操作,⼀般都需要考虑[线程同步],否则的话就可能影响线程安全。

  1. 对于线程不安全的对象我们可以通过如下⽅法来实现线程安全:

①加锁利⽤Synchronized或者ReenTrantLock来对不安全对象进⾏加锁,来实现线程执⾏的串⾏化,从⽽保证多线程同时操作对象的安全性,⼀个是语法层⾯的互斥锁,⼀个是API层⾯的互斥锁.

②⾮阻塞同步来实现线程安全。原理就是:通俗点讲,就是先进性操作,如果没有其他线程争⽤共享数据,那操作就成功了;如果共享数据有争⽤,产⽣冲突,那就再采取其他措施(最常⻅的措施就是不断地重试,知道成功为⽌)。这种⽅法需要硬件的⽀持,因为我们需要操作和冲突检测这两个步骤具备原⼦性。通常这种指令包括CAS SC,FAI TAS等。

③线程本地化,⼀种⽆同步的⽅案,就是利⽤Threadlocal来为每⼀个线程创造⼀个共享变量的副本来(副本之间是⽆关的)避免⼏个线程同时操作⼀个对象时发⽣线程安全问题。

31、常⻅的IO模型,五种?异步IO应⽤场景?有什么缺点?

  1. 同步

就是在发出⼀个功能调⽤时,在没有得到结果之前,该调⽤就不返回。*也就是必须⼀件⼀件

事做*,等前⼀件做完了才能做下⼀件事。就是我调⽤⼀个功能,该功能没有结束前,我死等结果。

  1. 异步

当⼀个异步过程调⽤发出后,调⽤者不能⽴刻得到结果。实际处理这个调⽤的部件在完成后,通过状态、通知和回调来通知调⽤者。就是我调⽤⼀个功能,不需要知道该功能结果,该功能有结果后通知我(回调通知)

  1. 阻塞

阻塞调⽤是指调⽤结果返回之前,当前线程会被挂起(线程进⼊⾮可执⾏状态,在这个状态下,cpu不会给线程分配时间⽚,即线程暂停运⾏)。函数只有在得到结果之后才会返回。对于同步调⽤来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回⽽已。就是调⽤我(函数),我(函数)没有接收完数据或者没有得到结果之前,我不会返回。

  1. ⾮阻塞

指在不能⽴刻得到结果之前,该函数不会阻塞当前线程,⽽会⽴刻返回。就是调⽤我(函数),我(函数)⽴即返回,通过select通知调⽤者。

  1. 阻塞I/O

应⽤程序调⽤⼀个IO函数,导致应⽤程序阻塞,等待数据准备好。如果数据没有准备好,⼀直等待….数据准备好了,从内核拷⻉到⽤户空间,IO函数返回成功指示。

  1. ⾮阻塞I/O

我们把⼀个SOCKET接⼝设置为⾮阻塞就是告诉内核,当所请求的I/O操作⽆法完成时,不要将进程睡眠,⽽是返回⼀个错误。这样我们的I/O操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为⽌。在这个不断测试的过程中,会⼤量的占⽤CPU的时间。

  1. I/O复⽤

I/O复⽤模型会⽤到select、poll、epoll函数,这⼏个函数也会使进程阻塞,但是和阻塞I/O所不同的的,这三个函数可以同时阻塞多个I/O操作。⽽且可以同时对多个读操作,多个写操作的I/O函数进⾏检测,直到有数据可读或可写时,才真正调⽤I/O操作函数。

  1. 信号驱动I/O

⾸先我们允许套接⼝进⾏信号驱动I/O,并安装⼀个信号处理函数,进程继续运⾏并不阻塞。当数据准备好时,进程会收到⼀个SIGIO信号,可以在信号处理函数中调⽤I/O操作函数处理数据。

  1. 异步I/O

当⼀个异步过程调⽤发出后,调⽤者不能⽴刻得到结果。实际处理这个调⽤的部件在完成后,通过状态、通知和回调来通知调⽤者的输⼊输出操作。

32、IO复⽤的原理?零拷⻉?三个函数?epoll 的 LT 和 ET 模式的理解。

  1. IO复⽤是Linux中的IO模型之⼀,IO复⽤就是进程预先告诉内核需要监视的IO条件,使得内

核⼀旦发现进程指定的⼀个或多个IO条件就绪,就通过进程进程处理,从⽽不会在单个IO上阻塞了。Linux中,提供了select、poll、epoll三种接⼝函数来实现IO复⽤。

  1. Select

select的缺点:

①单个进程能够监视的⽂件描述符的数量存在最⼤限制,通常是1024。由于select采⽤轮询的⽅式扫描⽂件描述符,⽂件描述符数量越多,性能越差;

②内核/⽤户空间内存拷⻉问题,select需要⼤量句柄数据结构,产⽣巨⼤开销;

③ Select返回的是含有整个句柄的数组,应⽤程序需要遍历整个数组才能发现哪些句柄发⽣事件;

④ Select的触发⽅式是⽔平触发,应⽤程序如果没有完成对⼀个已经就绪的⽂件描述符进⾏IO操作,那么每次select调⽤还会将这些⽂件描述符通知进程。

  1. Poll

与select相⽐,poll使⽤链表保存⽂件描述符,⼀你才没有了监视⽂件数量的限制,但其他三个缺点依然存在

  1. Epoll

上⾯所说的select缺点在epoll上不复存在,epoll使⽤⼀个⽂件描述符管理多个描述符,将⽤户关系的⽂件描述符的事件存放到内核的⼀个事件表中,这样在⽤户空间和内核空间的copy只需⼀次。Epoll是事件触发的,不是轮询查询的。没有最⼤的并发连接限制,内存拷⻉,利⽤mmap()⽂件映射内存加速与内核空间的消息传递。

区别总结:

  1. ⽀持⼀个进程所能打开的最⼤连接数

① Select最⼤1024个连接,最⼤连接数有FD_SETSIZE宏定义,其⼤⼩是32位整数表示,可以改变宏定义进⾏修改,可以重新编译内核,性能可能会影响;

② Poll没有最⼤连接限制,原因是它是基于链表来存储的;

③连接数限数有上限,但是很⼤;

  1. FD剧增后带来的IO效率问题

①因为每次进⾏线性遍历,所以随着FD的增加会造成遍历速度下降,效率降低;

② Poll同上;

③因为epool内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调⽤callback,所以在活跃socket较少的情况下,使⽤epoll没有前⾯两者的现象下降的性能问题。

  1. 消息传递⽅式

① Select内核需要将消息传递到⽤户空间,都需要内核拷⻉;

② Poll同上;

③ Epoll通过内核和⽤户空间共享来实现的。

epoll 的 LT 和 ET 模式的理解:

epoll对⽂件描述符的操作有两种模式:LT(level trigger)和ET(edge trigger),LT是默认模式。

区别:

LT模式:当epoll_wait检测到描述符事件发⽣并将此事件通知应⽤程序,应⽤程序可以不⽴即处理该事件。下次调⽤epoll_wait时,会再次响应应⽤程序并通知此事件。

ET模式:当epoll_wait检测到描述符事件发⽣并将此事件通知应⽤程序,应⽤程序必须⽴即处理该事件。如果不处理,下次调⽤epoll_wait时,不会再次响应应⽤程序并通知此事件。

在 select/poll中,进程只有在调⽤⼀定⽅法后,内核才对所有监视的⽂件描述符进⾏扫描,⽽epoll事先通过epoll_ctl()来注册⼀个⽂件描述符,⼀旦某个⽂件描述符就绪时,内核会采⽤类似callback的回调机制,迅速激活这个⽂件描述符,当进程调⽤epoll_wait时便得到通知(此处去掉了遍历⽂件描述符,⽽是通过监听回调的机制,这也是epoll的魅⼒所在)。

Epoll 的优点主要体现咋如下⼏个⽅⾯:1. 监视的描述符不受限制,它所⽀持的FD上限是最⼤可以打开⽂件的数⽬,这个数字⼀般远⼤于2048,举个栗⼦,具体数⽬可以在cat/proc/sys/fs/file-max 查看,⼀般来说,这个数⽬和内存关系很⼤。

  1. Select最⼤的缺点是进程打开的fd数⽬是有限制的,这对于连接数⽬较⼤的服务器来说根本不能满⾜,虽然也可以选择多进程的解决⽅案(Apache就是如此);不过虽然linux上⾯创建进程的代价较⼩,但仍旧不可忽视,加上进程间数据同步远⽐不上线程间同步⾼效,所以并不是⼀种完美的解决⽅案。

  2. IO的效率不会随着监视fd的数量的增⻓⽽下降,epoll不同于select和poll的轮询⽅式,⽽是通过每个fd定义的回调函数来实现,只有就绪的fd才会执⾏回调函数。

  3. 如果没有⼤量的idle -connection或者dead-connection,epoll的效率并不会⽐select/poll⾼很多,但是当遇到⼤量的idle- connection,就会发现epoll的效率⼤⼤⾼于select/poll。

哈喽,我是⼩贺哥,就爱分享编程知识,如果觉得⽂章对你有帮助,别忘记关注我哦!

操作系统 图 125

用心记录,持续成长