操作系统:

本文探讨了进程的定义、与程序的区别,进程状态与调度机制,以及进程调度算法、孤儿和僵尸进程、守护进程。同时解析了线程的概念、与进程的关系,状态和实现方式,以及并发与并行的区别。涵盖了内存管理、死锁、多线程模型和通信手段等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是进程?

进程就是正在执行的程序,是操作系统资源分配的基本单位。一般来说,进程包含指令、数据和PCB。

进程和程序的区别和联系:

区别:

    1)进程是动态的;程序是静态的。

    (2)进程有独立性,能并发执行;程序不能并发执行。

    (3)二者无一一对应关系。

    (4)进程异步运行,会相互制约;程序不具备此特征。

    但是,进程与程序又有密切的联系: 进程不能脱离具体程序而虚设, 程序规定了相应进程所要完成的动作。

    (5)组成不同。进程包含PCB、程序段、数据段。程序包含数据和指令代码。

    (6)程序是一个包含了所有指令和数据的静态实体。本身除占用磁盘的存储空间外,并不占用系统如CPU、内存等运行资源。

    (7)进程由程序段、数据段和PCB构成,会占用系统如CPU、内存等运行资源。

    (8)一个程序可以启动多个进程来共同完成。

    联系:

进程不能脱离具体程序而虚设, 程序规定了相应进程所要完成的动作。

延伸问题:孤儿进程和僵尸进程有什么区别?

    孤儿进程就是说一个父进程退出,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程。孤儿进程将被 init 进程(进程ID为1的进程)所收养,并由 init 进程对它们完成状态收集工作。因为孤儿进程会被 init 进程收养,所以孤儿进程不会对系统造成危害。

    僵尸进程就是一个子进程的进程描述符在子进程退出时不会释放,只有当父进程通过 wait() 或 waitpid() 获取了子进程信息后才会释放。如果子进程退出,而父进程并没有调用 wait() 或 waitpid(),那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵尸进程。僵尸进程通过 ps 命令显示出来的状态为 Z。

系统所能使用的进程号是有限的,如果产生大量僵尸进程,可能会因为没有可用的进程号而导致系统不能产生新的进程。如果要消灭系统中大量的僵尸进程,只需要将其父进程杀死,此时僵尸进程就会变成孤儿进程,从而被 init 进程所收养,这样 init 进程就会释放所有的僵尸进程所占有的资源,从而结束僵尸进程。

延伸问题:什么是守护进程?

守护进程是运行在后台的一种特殊进程,它是独立于控制终端的,并周期性地执行某些任务。

进程有哪些状态?

在五状态模型里面,进程一共有5中状态,分别是创建、就绪、运行、终止、阻塞。

    运行状态就是进程正在CPU上运行。在单处理机环境下,每一时刻最多只有一个进程处于运行状态。

    就绪状态就是说进程已处于准备运行的状态,即进程获得了除CPU之外的一切所需资源,一旦得到CPU即可运行。

    阻塞状态就是进程正在等待某一事件而暂停运行,比如等待某资源为可用或等待I/O完成。即使CPU空闲,该进程也不能运行。

运行态→阻塞态:往往是由于等待外设,等待主存等资源分配或等待人工干预而引起的。

阻塞态→就绪态:则是等待的条件已满足,只需分配到处理器后就能运行。

运行态→就绪态:不是由于自身原因,而是由外界原因使运行状态的进程让出处理器,这时候就变成就绪态。例如时间片用完,或有更高优先级的进程来抢占处理器等。

就绪态→运行态:系统按某种策略选中就绪队列中的一个进程占用处理器,此时就变成了运行态。

注意:不能由阻塞态直接转换为运行态,也不能由就绪态直接转换为阻塞态(因为进入阻塞态是进程主动请求的,必然需要进程在运行时才能发出这种请求)

什么是调度?

当一堆任务要处理时,需要确定某种规则来决定处理这些任务的顺序,这就是“调度”研究的问题。处理机调度:就是从就绪队列中按照一定的算法选择一个进程并将处理机分配给他运行,以实现进程的并发执行。

调度的三个层次:

(1)高级调度( 作业调度)外存与内存之间的调度

(2)中级调度(内存调度)

(3)进程的挂起状态与七状态模型

(4)低级调度(进程调度)

(5)三层调度的联系和对比

进程调度算法有哪些?

先来先服务:(FCFS)

另外,对I/O密集型进程也不利,因为这种进程每次进行I/O操作之后又得重新排队。

短作业优先(SJF)

 最短剩余时间优先:(最短作业优先的抢占式版本

按剩余运行时间的顺序进行调度。 当一个新的作业到达时,其整个运行时间与当前进程的剩余时间作比较。如果新的进程需要的时间更少,则挂起当前进程,运行新的进程。否则新的进程等待。

高响应比优先(HRRN)

 上述三种比较:

 时间片轮转(可抢占的 RR)

时间片轮转算法的效率和时间片的大小有很大关系:

    因为进程切换都要保存进程的信息并且载入新进程的信息,如果时间片太小,会导致进程切换得太频繁,在进程切换上就会花过多时间。

而如果时间片过长,那么实时性就不能得到保证。

优先级调度算法:

 

多级队列调度算法:

将就绪队列分成多个独立的队列,每个队列都有自己的调度算法,队列之间采用固定优先级抢占调度。其中,一个进程根据自身属性被永久地分配到一个队列中。

多级反馈队列调度算法:

与多级队列调度算法相比,其允许进程在队列之间移动:若进程使用过多CPU时间,那么它会被转移到更低的优先级队列;在较低优先级队列等待时间过长的进程会被转移到更高优先级队列,以防止饥饿发生。

对比:

延伸问题:什么是抢占式调度?什么是非抢占式调度?

    抢占式就是说操作系统将正在运行的进程强行暂停,由调度器将CPU分配给其他就绪进程。

非抢占式是调度器一旦把处理机分配给某进程后便让它一直运行下去,直到进程完成或发生进程调度进程调度某事件而阻塞时,才把处理机分配给另一个进程。 (只允许进程主动放弃处理机)

什么时候进行进程调度?

(2)什么时候不能进行进程调度?

什么是线程?

线程是进程内部的不同的执行路径,是操作系统独立调度的基本单位。一个进程中可以有多个线程,它们共享进程资源。比如说,微信和浏览器是两个进程,浏览器进程里面有很多线程,例如 HTTP 请求线程、事件响应线程、渲染线程等等,线程的并发执行使得在浏览器中点击一个新链接从而发起 HTTP 请求时,浏览器还可以响应用户的其它事件。

引入线程带来的变化及进程与线程的比较

线程的属性

进程与线程有什么区别?

拥有资源

进程是资源分配的基本单位,但是线程不拥有资源,线程可以访问隶属于进程的资源。

调度

线程是独立调度的基本单位,在同一进程中,线程的切换不会引起进程切换,从一个进程中的线程切换到另一个进程中的线程时,会引起进程切换。

系统开销

由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O 设备等,所付出的开销远大于创建或撤销线程时的开销。类似地,在进行进程切换时,涉及当前执行进程 CPU 环境的保存及新调度进程 CPU 环境的设置,而线程切换时只需保存和设置少量寄存器内容,开销很小。

通信方面

线程间可以通过直接读写同一进程中的数据进行通信,但是进程通信需要借助 IPC。

  • 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
  • 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。

进程和线程的关系

(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程是操作系统可识别的最小执行和调度单位。

(2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。 同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。

(3)处理机分给线程,即真正在处理机上运行的是线程。

(4)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。

进程与线程的区别?

(1)进程有自己的独立地址空间,线程没有

(2)进程是资源分配的最小单位,线程是CPU调度(程序执行流)的最小单位

(3)进程和线程通信方式不同(线程之间的通信比较方便。同一进程下的线程共享数据(比如全局变量,静态变量),通过这些数据来通信不仅快捷而且方便,当然如何处理好这些访问的同步与互斥正是编写多线程程序的难点。而进程之间的通信只能通过进程通信的方式进行。)

(4)进程上下文切换开销大,线程开销小

(5)一个进程挂掉了不会影响其他进程,而线程挂掉了会影响其他线程

(6)对进程进程操作一般开销都比较大,对线程开销就小了 

延伸问题:线程有哪两种即线程如何实现?

用户级线程(user level thread):

对于这类线程,有关线程管理的所有工作都由应用程序完成,内核意识不到线程的存在。在应用程序启动后,操作系统分配给该程序一个进程号,以及其对应的内存空间等资源。应用程序通常先在一个线程中运行,该线程被成为主线程。在其运行的某个时刻,可以通过调用线程库中的函数创建一个在相同进程中运行的新线程。用户级线程的好处是非常高效,不需要进入内核空间,但并发效率不高。

内核级线程(kernel level thread):

        对于这类线程,有关线程管理的所有工作由内核完成,应用程序没有进行线程管理的代码,只能调用内核线程的接口。内核维护进程及其内部的每个线程,调度也由内核基于线程架构完成。内核级线程的好处是,内核可以将不同线程更好地分配到不同的CPU,以实现真正的并行计算。

        事实上,在现代操作系统中,往往使用组合方式实现多线程,即线程创建完全在用户空间中完成,并且一个应用程序中的多个用户级线程被映射到一些内核级线程上,相当于是一种折中方案。

多线程模型:

(1)多对一模型:

 (2)一对一模型:

 (3)多对多模型:三种中最好

线程有几种状态?

1. 新建(NEW):

新创建了一个线程对象。

2. 可运行(RUNNABLE):

线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。

3. 运行(RUNNING):

可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。

4. 阻塞(BLOCKED):

阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种:

    (一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。

    (二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。

    (三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。

5. 死亡(DEAD):

线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

并发和并行有什么区别?

并发

就是在一段时间内,多个任务都会被处理;但在某一时刻,只有一个任务在执行。单核处理器可以做到并发。比如有两个进程A和B,A运行一个时间片之后,切换到B,B运行一个时间片之后又切换到A。因为切换速度足够快,所以宏观上表现为在一段时间内能同时运行多个程序。

并行

就是在同一时刻,有多个任务在执行。这个需要多核处理器才能完成,在微观上就能同时执行多条指令,不同的程序被放到不同的处理器上运行,这个是物理上的多个进程同时进行。

进程同步与互斥

互斥:指某一个临界资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。临界资源是指物理设备(摄像头等),此外还有许多变量,数据,内存缓冲区等。

同步:是指在互斥的基础上(大多数情况下),通过其它机制实现访问者对资源的有序访问。大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。

在多道程序环境下,进程是并发执行的,不同进程之间存在着不同的相互制约关系。为了协调进程之间的相互制约关系,如等待、传递信息等,引入了进程同步的概念。进程同步是为了解决进程的异步问题

同步:体现的是一种协作性。互斥:体现的是排它性。

进程同步的方式有哪些?

进程同步的主要任务:是对多个相关进程在执行次序上进行协调,以使并发执行的诸进程之间能有效地共享资源和相互合作,从而使程序的执行具有可再现性。

同步机制遵循的原则:也是互斥的原则:

临界区:

临界区是一段代码,在临界区内进程将访问临界资源(摄像头等)。任何时候最多只有一个进程可以进入临界区,也就是说,临界区具有排他性。所以,为了互斥访问临界资源,每个进程在进入临界区之前,需要先进行检查。

互斥量:

就是使用一个互斥的变量来直接制约多个进程,每个进程只有拥有这个变量才具有访问公共资源的权限,因为互斥量只有一个,所以能保证资源的正确访问。

信号量:

信号量(Semaphore)是一个整型变量,可以对其执行自增和自减操作,自减操作通常也叫做P操作,自增操作也称为V操作。这两个操作需要被设计成原语,是不可分割,通常的做法是在执行这些操作的时候屏蔽中断。进程使用这两个操作进行同步。

    对于P操作,如果执行操作后信号量小于 0,那么执行该操作的进程就会阻塞,否则继续执行;

对于V操作,如果操作之后的信号量小于等于0,那么就会从阻塞队列唤醒一个进程。

为什么引入信号量机制:为了更好的解决进程互斥与同步的问题

什么是信号量机制

整型信号量:

记录型信号量:

信号量机制同步和互斥。

信号量机制实现进程互斥

 信号量机制实现进程同步

信号量机制实现前驱关系

管程

        管程使用的是面向对象思想,将表示共享资源的数据结构还有相关的操作,包括同步机制,都集中并封装到一起。所有进程都只能通过管程间接访问临界资源,而管程只允许一个进程进入并执行操作,从而实现进程互斥。管程中设置了多个条件变量,表示多个进程被阻塞或挂起的条件。对条件变量执行 wait() 操作会导致调用进程阻塞,把管程让出来给另一个进程持有。signal() 操作用于唤醒被阻塞的进程。管程有一个重要特性,就是在一个时刻只能有一个进程使用管程。进程在无法继续执行的时候不能一直占用管程,否则其它进程永远不能使用管程。

为什么要引入管程:信号量机制存在问题:编程程序困难、易出错

管程的定义和基本特征

 进程间通信的方式有哪些?

 什么是进程通信?

 共享存储

共享一块大家都可以访问的空间,一次只能有一个进程进行读或写操作

管道通信

消息传递

Socket(嵌套字)

    Socket就是套接字,套接字也是一种通信机制,凭借这种机制,可以让不在同一台主机上的两个进程,通过网络进行通信,一般可以用在客户端和服务器之间的通信。

实际上,Socket 是在应用层和传输层之间的一个抽象层,它把 TCP/IP 协议的传输层里面复杂的操作,抽象为几个简单的接口,供应用层调用实现进程在网络中的通信。

几种方式的比较:

管道:

速度慢、容量有限

消息队列:

容量收到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题。

信号量:

不能传递复杂信息,只能用来同步。

共享内存:

能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全

延伸问题:Socket通信流程是怎样的?

        概括地说,就是通信的两端都建立了一个 Socket ,然后通过 Socket 对数据进行传输。通常服务器处于一个无限循环,等待客户端的连接。

    对于客户端,它的的过程比较简单,首先创建 Socket,通过TCP连接服务器,将 Socket 与远程主机的某个进程连接,然后就发送数据,或者读取响应数据,直到数据交换完毕,关闭连接,结束 TCP 对话。

    对于服务端,先初始化 Socket,建立流式套接字,与本机地址及端口进行绑定,然后通知 TCP,准备好接收连接,调用 accept() 阻塞,等待来自客户端的连接。如果这时客户端与服务器建立了连接,客户端发送数据请求,服务器接收请求并处理请求,然后把响应数据发送给客户端,客户端读取数据,直到数据交换完毕。最后关闭连接,交互结束。

延伸问题:从TCP连接的角度说说Socket通信流程。

首先是三次握手的Socket交互流程。

    服务器调用 socket()、bind()、listen() 完成初始化后,调用 accept() 阻塞等待;

    客户端 Socket 对象调用 connect() 向服务器发送了一个 SYN 并阻塞;

    服务器完成了第一次握手,即发送 SYN 和 ACK 应答;

    客户端收到服务端发送的应答之后,从 connect() 返回,再发送一个 ACK 给服务器;

    服务器 Socket 对象接收客户端第三次握手 ACK 确认,此时服务端从 accept() 返回,建立连接。

接下来就是两个端的连接对象互相收发数据。

然后是四次挥手的Socket交互流程。

    某个应用进程调用 close() 主动关闭,发送一个 FIN;

    另一端接收到 FIN 后被动执行关闭,并发送 ACK 确认;

    之后被动执行关闭的应用进程调用 close() 关闭 Socket,并也发送一个 FIN;

    接收到这个 FIN 的一端向另一端 ACK 确认。

线程间通信

(1)同步

多个线程通过synchronized通讯,类似于共享内存

(2)while轮询

线程A不断改变条件,线程B不断查看条件是否满足需求(比方说=5),从而实现通讯。

效率不高,因为B一直在查看,没做别的

(3)wait/notify

进入阻塞,而不是像轮询一样一直占用CPU资源

(4)管道通信

通过管道,将一个线程的消息发送个另一个线程

大内核和微内核有什么区别?

    大内核,就是将操作系统的全部功能都放进内核里面,包括调度、文件系统、网络、设备驱动器、存储管理等等,组成一个紧密连接整体。大内核的优点就是效率高,但是很难定位bug,拓展性比较差,每次需要增加新的功能,都要将新的代码和原来的内核代码重新编译。

    微内核与单体内核不同,微内核只是将操作中最核心的功能加入内核,包括IPC、地址空间分配和基本的调度,这些东西都在内核态运行,其他功能作为模块被内核调用,并且是在用户空间运行。微内核比较好维护和拓展,但是效率可能不高,因为需要频繁地在内核态和用户态之间切换。

分时系统和实时系统有什么区别?

    分时系统(Sharing time system)就是系统把CPU时间分成很短的时间片,轮流地分配给多个作业。它的优点就是对多个用户的多个作业都能保证足够快的响应时间,并且有效提高了资源的利用率。

    实时系统(Real-time system)是系统对外部输入的信息,能够在规定的时间内(截止期限)处理完毕并做出反应。它的优点是能够集中地及时地处理并作出反应,高可靠性,安全性。

    通常计算机采用的是分时,就是多个进程/用户之间共享CPU,从形势上实现多任务。各个用户/进程之间的调度并非精准度特别高,如果一个进程被锁住,可以给它分配更多的时间。而实时操作系统则不同,软件和硬件必须遵从严格的时间限制,超过时限的进程可能直接被终止。在这样的操作系统中,每次加锁都需要仔细考虑。

静态链接和动态链接有什么区别?

    静态链接就是在编译期间,由编译器和连接器将静态库集成到应用程序内,并制作成目标文件以及可以独立运作的可执行文件。静态库一般是一些外部函数与变量的集合。

    静态库很方便,但是如果我们只是想用库中的某一个函数,却仍然得把所有的内容都链接进去。一个更现代的方法是使用共享库,避免了在文件中静态库的大量重复。

    动态链接可以在首次载入的时候执行,也可以在程序开始执行的时候完成。这个是由动态链接器完成,比方标准 C 库(libc.so) 通常就是动态链接的,这样所有的程序可以共享同一个库,而不用分别进行封装。

编译有哪些阶段?

    预处理阶段:处理以 # 开头的预处理命令;

    编译阶段:翻译成汇编文件;

    汇编阶段:将汇编文件翻译成可重定位目标文件;

    链接阶段:将可重定位目标文件和 printf.o 等单独预编译好的目标文件进行合并,得到最终的可执行目标文件。

什么是上下文切换?

对于单核单线程CPU而言,在某一时刻只能执行一条CPU指令。上下文切换(Context Switch)是一种将CPU资源从一个进程分配给另一个进程的机制。从用户角度看,计算机能够并行运行多个进程,这恰恰是操作系统通过快速上下文切换造成的结果。在切换的过程中,操作系统需要先存储当前进程的状态(包括内存空间的指针,当前执行完的指令等等),再读入下一个进程的状态,然后执行此进程。

系统调用和库函数有什么区别?

    系统调用是应用程序向系统内核请求服务的方式。可以包括硬件相关的服务(例如,访问硬盘等),或者创建新进程,调度其他进程等。系统调用是程序和操作系统之间的重要接口。

    库函数就是说把一些常用的函数编写完放到一个文件里,编写应用程序时调用,这是由第三方提供的,发生在用户地址空间。

    在移植性方面,不同操作系统的系统调用一般是不同的,移植性差;库函数会相对好一些。比如说在所有的ANSI C编译器版本中,C库函数是相同的。

    在调用开销方面,系统调用需要在用户空间和内核环境间切换,开销较大;而库函数调用开销较小。

什么是死锁?

在两个或多个并发进程中,如果一个进程集合中的每个进程都在等待只能由该进程集合中的其他进程才能引发的事件,那么该进程集合就产生了死锁。

哲学家问题:

每位哲学家都在等待自己右边的人放下筷子,这些哲学家都因等待筷子资源而被阻塞。即发生“死锁”

死锁、饥饿、死循环的区别

延伸问题:死锁产生有哪些条件?

死锁产生的根本原因是多个进程竞争资源时,进程的推进顺序出现不正确。四个必要条件

互斥:

每个资源已经分配给了一个进程,需要对互斥使用的资源进行争抢

占有和等待

进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其他进程占有,此时请求进程被阻塞,但又对自己已有的资源保持不放。

不可抢占:

已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释放。

循环等待:

存在一种进程资源的循环等待链,链中的每一个进程已获得的资源同时被下一个进程所请求。

注意:发生死锁时一定有循环等待,但是发生循环等待时未必死锁(循环等待时死锁的必要不充分条件)

延伸问题:怎么解决死锁?

对于死锁,主要有4种解决策略。 预防死锁、避免死锁、死锁检查与恢复、

鸵鸟策略(啥都不管)

死锁预防

死锁预防是指通过破坏死锁产生的四个必要条件中的一个或多个,以避免发生死锁。

破坏互斥:

不让资源被一个进程独占,可通过假脱机技术允许多个进程同时访问资源;

缺点:不是所有资源可以这么处理

破坏占有和等待:

静态分配方法:

一种实现方法是要求进程在开始执行前请求需要的所有资源。在它的资源为满足前,不让它投入运行。一旦投入运行后,这些资源就一直归它所有,该进程就不会在请求别的资源。缺点:资源利用率低

破坏不可抢占:

方法一:当某个进程请求新的资源得不到满足时,它必须立即释放所有的资源,待以后需要时,在重新申请。

方法二:当某个进程需要的资源被其他进程所占有的时候,可以由操作系统协助,将想要的资源强行剥夺。

缺点:一般只适用于易保存和恢复状态的资源。反复低申请和释放资源会增加系统开销,降低系统吞吐量。

破坏循环等待:

顺序资源分配法:将所有资源进行统一编号,进程可以在任何时刻请求资源,但要求进程必须按照编号顺序请求资源。

死锁避免

为了避免因为预防死锁而导致所有线程变慢,死锁避免采用了与死锁预防相反的措施。它允许三个必要条件,但通过算法判断资源请求是否可能导致循环等待的形成并相应决策,来避免死锁点的产生。因此,其前提是知道当前资源使用的整体情况,以及申请资源线程本身所占有的资源细节。

判断和决策中,主要使用两种避免方法。

    线程启动拒绝:如果一个线程的请求会引发死锁,则不允许其启动。

    资源分配拒绝:如果一个线程增加的资源请求会导致死锁,则不允许此申请。

整体来看,死锁避免是从资源和线程相互间关系着手,避免形成循环等待是其主要任务。

死锁检测和恢复

可以允许系统进入死锁状态,但会维护一个系统的资源分配图,定期调用死锁检测算法来检测途中是否存在死锁,检测到死锁发生后,采取死锁恢复算法进行恢复。

死锁检测方法如下:

检测到死锁后,就需要解决死锁。目前操作系统中主要采用如下几种方法:

有哪些磁盘调度算法?

先来先服务

    按照磁盘请求的顺序进行调度。

    优点是公平和简单。缺点也很明显,因为未对寻道做任何优化,使平均寻道时间可能较长。

最短寻道时间优先

    优先调度与当前磁头所在磁道距离最近的磁道。

    虽然平均寻道时间比较低,但是不够公平。如果新到达的磁道请求总是比一个在等待的磁道请求近,那么在等待的磁道请求会一直等待下去,也就是出现饥饿现象。一般来说,两端的磁道请求更容易出现饥饿现象。

电梯算法

    也叫SCAN扫描算法。电梯算法就是说读写磁头总是保持一个方向运行,直到该方向没有请求为止,然后改变运行方向。

    因为考虑了移动方向,因此所有的磁盘请求都会被满足,解决了最短寻道时间优先的饥饿问题。

什么是虚拟内存?

        虚拟内存就是说,让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。虚拟内存使用部分加载的技术,让一个进程或者资源的某些页面加载进内存,从而能够加载更多的进程,甚至能加载比内存大的进程,这样看起来好像内存变大了,这部分内存其实包含了磁盘或者硬盘,并且就叫做虚拟内存。

        虚拟内存允许执行进程不必完全在内存中。虚拟内存的基本思想是:每个进程拥有独立的地址空间,这个空间被分为大小相等的多个块,称为页(Page),每个页都是一段连续的地址。这些页被映射到物理内存,但并不是所有的页都必须在内存中才能运行程序。当程序引用到一部分在物理内存中的地址空间时,由硬件立刻进行必要的映射;当程序引用到一部分不在物理内存中的地址空间时,由操作系统负责将缺失的部分装入物理内存并重新执行失败的命令。这样,对于进程而言,逻辑上似乎有很大的内存空间,实际上其中一部分对应物理内存上的一块(称为帧,通常页和帧大小相等),还有一些没加载在内存中的对应在硬盘上,如图所示

 虚拟内存的应用与优点

  虚拟内存很适合在多道程序设计系统中使用,许多程序的片段同时保存在内存中。当一个程序等待它的一部分读入内存时,可以把CPU交给另一个进程使用。虚拟内存的使用可以带来以下好处:

    在内存中可以保留多个进程,系统并发度提高

解除了用户与内存之间的紧密约束,进程可以比内存的全部空间还大

操作系统的组成

1、驱动程序是最底层的、直接控制和监视各类硬件的部分,它们的职责是隐藏硬件的具体细节,并向其他部分提供一个抽象的、通用的接口。

2、内核是操作系统之最内核部分,通常运行在最高特权级,负责提供基础性、结构性的功能。

3、支承库是一系列特殊的程序库,它们职责在于把系统所提供的基本服务包装成应用程序所能够使用的编程接口(API),是最靠近应用程序的部分。例如,GNU C运行期库就属于此类,它把各种操作系统的内部编程接口包装成ANSI C和POSIX编程接口的形式。

4、外围是指操作系统中除以上三类以外的所有其他部分,通常是用于提供特定高级服务的部件。例如,在微内核结构中,大部分系统服务,以及UNIX/Linux中各种守护进程都通常被划归此列。

操作系统中的缓存

缓存(cache),原始意义是指访问速度比一般随机存取存储器(RAM)快的一种高速存储器,可以进行高速数据交换的存储器,它先于内存与CPU交换数据。

一个程序从开始运行到结束的完整过程(四个过程)

1、预处理:条件编译,头文件包含,宏替换的处理,生成.i文件。

2、编译:将预处理后的文件转换成汇编语言,生成.s文件

3、汇编:汇编变为目标代码(机器代码)生成.o的文件

4、链接:连接目标代码,生成可执行程序

内存池、进程池、线程池。(c++程序员必须掌握)

    首先介绍一个概念“池化技术 ”。池化技术就是:提前保存大量的资源,以备不时之需以及重复使用。池化技术应用广泛,如内存池,线程池,连接池等等。内存池相关的内容,建议看看Apache、Nginx等开源web服务器的内存池实现。
    由于在实际应用当做,分配内存、创建进程、线程都会设计到一些系统调用,系统调用需要导致程序从用户态切换到内核态,是非常耗时的操作。因此,当程序中需要频繁的进行内存申请释放,进程、线程创建销毁等操作时,通常会使用内存池、进程池、线程池技术来提升程序的性能。

    线程池:线程池的原理很简单,类似于操作系统中的缓冲区的概念,它的流程如下:先启动若干数量的线程,并让这些线程都处于睡眠状态,当需要一个开辟一个线程去做具体的工作时,就会唤醒线程池中的某一个睡眠线程,让它去做具体工作,当工作完成后,线程又处于睡眠状态,而不是将线程销毁。

    进程池与线程池同理。

    内存池:内存池是指程序预先从操作系统申请一块足够大内存,此后,当程序中需要申请内存的时候,不是直接向操作系统申请,而是直接从内存池中获取;同理,当程序释放内存的时候,并不真正将内存返回给操作系统,而是返回内存池。当程序退出(或者特定时间)时,内存池才将之前申请的内存真正释放。

动态链接库与静态链接库的区别

静态库

  • 静态库是一个外部函数与变量的集合体。静态库的文件内容,通常包含一堆程序员自定的变量与函数,其内容不像动态链接库那么复杂,在编译期间由编译器与链接器将它集成至应用程序内,并制作成目标文件以及可以独立运作的可执行文件。而这个可执行文件与编译可执行文件的程序,都是一种程序的静态创建(static build)。

https://images2018.cnblogs.com/blog/1118296/201809/1118296-20180902162023684-811662571.jpg

动态库

  • 静态库很方便,但是如果我们只是想用库中的某一个函数,却仍然得把所有的内容都链接进去。一个更现代的方法则是使用共享库,避免了在文件中静态库的大量重复。
  • 动态链接可以在首次载入的时候执行(load-time linking),这是 Linux 的标准做法,会由动态链接器ld-linux.so 完成,比方标准 C 库(libc.so) 通常就是动态链接的,这样所有的程序可以共享同一个库,而不用分别进行封装。
  • 动态链接也可以在程序开始执行的时候完成(run-time linking),在 Linux 中使用 dlopen()接口来完成(会使用函数指针),通常用于分布式软件,高性能服务器上。而且共享库也可以在多个进程间共享。
  • 链接使得我们可以用多个对象文件构造我们的程序。可以在程序的不同阶段进行(编译、载入、运行期间均可),理解链接可以帮助我们避免遇到奇怪的错误

https://images2018.cnblogs.com/blog/1118296/201809/1118296-20180902162103595-917107800.png

区别:

  1. 使用静态库的时候,静态链接库要参与编译,在生成执行文件之前的链接过程中,要将静态链接库的全部指令直接链接入可执行文件中。而动态库提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个.dll文件中,该dll包含一个或多个已被编译,链接并与使用它们的进程分开储存的函数。
  2. 静态库中不能再包含其他动态库或静态库,而在动态库中还可以再包含其他动态或者静态库。
  3. 静态库在编译的时候,就将库函数装在到程序中去了,而动态库函数必须在运行的时候才被装载,所以使用静态库速度快一些。

IO多路复用

IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用如下场合:

  • 当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用。
  • 当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现。
  • 如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用。
  • 如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用。
  • 如果一个服务器要处理多个服务或多个协议,一般要使用I/O复用。
  • 与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值