1.请分别简单说一说进程和线程以及它们的区别和联系:
进程是对运行时程序的封装,是系统进行资源调度和分配的最小单位,实现了操作系统的并发
线程是进程的子任务,是CPU调度和分配的基本单位,用于保证程序的实时性,实现进程内部的并发
一个进程可以有多个线程,多个线程也可以并发执行
进程:线程,内存,文件/网络句柄组成https://www.cnblogs.com/coder-programming/p/10595804.html
进程和线程的关系
(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程是操作系统可识别的最小执行和调度单位。
(2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。 同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。
(3)处理机分给线程,即真正在处理机上运行的是线程。
(4)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
进程与线程的区别?
(1)进程有自己的独立地址空间,线程没有
(2)进程是资源分配的最小单位,线程是CPU调度的最小单位
(3)进程和线程通信方式不同(线程之间的通信比较方便。同一进程下的线程共享数据(比如全局变量,静态变量),通过这些数据来通信不仅快捷而且方便,当然如何处理好这些访问的同步与互斥正是编写多线程程序的难点。而进程之间的通信只能通过进程通信的方式进行。)
(5)一个进程挂掉了不会影响其他进程,而线程挂掉了会影响其他线程
2.进程的常见状态?以及各种状态之间的转换条件?
进程在其生命周期内,由于系统中个进程之间的相互制约关系以及系统的运行环境的变化,
使的进程的状态也在不断地发生着变化。通常进程有以下五种状态。前三种是进程的基本状
态。
1) 运行状态:进程正在处理器上运行。在单处理器的环境下,每一时刻最多只有一个进程处
于运行状态。
2) 就绪状态:进程已处于准备运行的状态,即进程获得了除CPU之外的一切所需资源,一
旦得到处理器即可运行。
3) 阻塞状态:又称为等待状态:进程正在等待某一事件而暂停运行,如等待某资源为可用
(不包括处理器),或等待输入输出的完成。即使处理器空闲,该进程也不能运行。
4) 创建状态:进程正在被创建,尚未转到就绪状态。创建进程通常需要多个步骤:首先申请
一个空白的PCB,并向PCB中填写一些控制和管理进程的信息;然后由系统为该进程分配运
行时所必须的资源;最后把该进程转入到就绪状态。
5) 结束状态:进程正在从系统中消失,这可能是进程正常结束或其他原因中断退出运行。当
进程需要结束运行时,系统首先必须置该进程为结束状态,然后再进一步处理资源释放和回
收工作。
注意区别就绪状态和等待状态:就绪状态是指进程仅缺少处理器,只要活得处理器资源就立
即执行;而等待状态是指进程需要其他资源或等待某一事件,及时处理器空闲也不能运行。
3.进程同步
进程同步的主要任务:是对多个相关进程在执行次序上进行协调,以使并发执行的诸进程之间能有效地共享资源和相互合作,从而使程序的执行具有可再现性。
同步机制遵循的原则:
(1)空闲让进;
(2)忙则等待(保证对临界区的互斥访问);
(3)有限等待(有限代表有限的时间,避免死等);
(4)让权等待,(当进程不能进入自己的临界区时,应该释放处理机,以免陷入忙等状态)。
4.进程间通信
https://www.cnblogs.com/zgq0/p/8780893.html
5.有了进程为什么还要线程?
https://www.cnblogs.com/Berryxiong/p/6429723.html
6.线程同步方式
互斥量 Synchronized/Lock:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问
信号量 Semphare:它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量
事件(信号),Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作
7.同步和异步 线程同步和线程互斥
同步:是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么,这个进程将会一直等待下去,直到收到返回信息才继续执行下去。
异步:是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。
多线程共享一个进程的地址空间虽然线程间通信容易进行,但是多线程同时访问共享对象时需要引入同步和互斥机制。
互斥:某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的,线程间不需要知道彼此的存在。
同步:在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问,线程间知道彼此的存在。
多线程之间同步机制:
临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开,临界区在被释放后,其他线程才可以抢占。(互斥,不可跨进程)
互斥量:互斥锁是用在多线程多任务互斥的,一个线程占用了某一个资源,那么别的线程就无法访问,直到这个线程unlock,其他的线程才开始可以利用这个资源。(互斥,可跨进程)
事件:又叫线程触发器,通过线程触发事件实现同步互斥,不可以跨进程,通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作 (同步,不可跨进程)
信号量:可以跨进程,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目(同步,可跨进程)
临界区和互斥量都有“线程所有权”的概念,所以它们是不能用来实现线程间的同步的,只能用来实现互斥。事件和信号量都可以实现线程和进程间的互斥和同步。就使用效率来说,临界区的效率是最高的,因为它不是内核对象,而其它的三个都是核心对象,要借助操作系统来实现,效率相对来说就比较低。但如果要跨进程使用还是要用到互斥器、事件对象和信号量。
8.死锁
死锁是指多个进程在执行过程中,因争夺资源而造成相互等待的现象。
死锁发生的四个必要条件如下:
互斥条件:进程对所分配到的资源不允许其他进程访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源;
请求和保持条件:进程获得一定的资源后,又对其他资源发出请求,但是该资源可能被其他进程占有,此时请求阻塞,但该进程不会释放自己已经占有的资源
不可剥夺条件:进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用后自己释放
环路等待条件:进程发生死锁后,必然存在一个进程-资源之间的环形链
预防死锁:
资源一次性分配:一次性分配所有资源,这样就不会再有请求了:(破坏请求条件)
只要有一个资源得不到分配,也不给这个进程分配其他的资源:(破坏请求和保持条件)
可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)
资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)
避免死锁:
预防死锁的几种策略,会严重地损害系统性能。因此在避免死锁时,要施加较弱的限制,从而获得 较满意的系统性能。由于在避免死锁的策略中,允许进程动态地申请资源。因而,系统在进行资源分配之前预先计算资源分配的安全性。若此次分配不会导致系统进入不安全的状态,则将资源分配给进程;否则,进程等待。其中最具有代表性的避免死锁算法是银行家算法。
银行家算法:首先需要定义状态和安全状态的概念。系统的状态是当前给进程分配的资源情况。因此,状态包含两个向量Resource(系统中每种资源的总量)和Available(未分配给进程的每种资源的总量)及两个矩阵Claim(表示进程对资源的需求)和Allocation(表示当前分配给进程的资源)。安全状态是指至少有一个资源分配序列不会导致死锁。当进程请求一组资源时,假设同意该请求,从而改变了系统的状态,然后确定其结果是否还处于安全状态。如果是,同意这个请求;如果不是,阻塞该进程知道同意该请求后系统状态仍然是安全的。
避免多次锁定。尽量避免同一个线程对多个 Lock 进行锁定。例如上面的死锁程序,主线程要对 A、B 两个对象的 Lock 进行锁定,副线程也要对 A、B 两个对象的 Lock 进行锁定,这就埋下了导致死锁的隐患。
具有相同的加锁顺序。(给锁添加顺序)如果多个线程需要对多个 Lock 进行锁定,则应该保证它们以相同的顺序请求加锁。比如上面的死锁程序,主线程先对 A 对象的 Lock 加锁,再对 B 对象的 Lock 加锁;而副线程则先对 B 对象的 Lock 加锁,再对 A 对象的 Lock 加锁。这种加锁顺序很容易形成嵌套锁定,进而导致死锁。如果让主线程、副线程按照相同的顺序加锁,就可以避免这个问题。
使用定时锁。程序在调用 acquire() 方法加锁时可指定 timeout 参数,该参数指定超过 timeout 秒后会自动释放对 Lock 的锁定,这样就可以解开死锁了。
死锁检测。死锁检测是一种依靠算法机制来实现的死锁预防机制,它主要是针对那些不可能实现按序加锁,也不能使用定时锁的场景的。
解除死锁:
当发现有进程死锁后,便应立即把它从死锁状态中解脱出来,常采用的方法有:
剥夺资源:从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态;
撤消进程:可以直接撤消死锁进程或撤消代价最小的进程,直至有足够的资源可用,死锁状态.消除为止;所谓代价是指优先级、运行代价、进程的重要性和价值等。
进程回退:让一(多)个进程回退到足以回避死锁的地步,进程回退时自愿释放资源而不是被剥夺。要求系统保持进程的历史信息,设置还原点。
10.进程、线程,协程
一个进程占用一个CPU,占用一定的内存空间。一般CPU配置都是4核,如果开的进程太多,其他的程序就得等着。CPU是用来计算的。所以在CPU密集运算的情况下,才使用多进程。
进程里面才有线程,一个进程里面默认一个线程。多线程适合IO操作,但是也不是开启的线程越多越好,需要根据实际情况而定。太多的线程会占用过多的系统资源(内存开销,CPU开销)。
协程:又称微线程纤程。通俗的讲就是比线程还要小的线程,所以才叫微线程。
优点:
1. 使用高并发、高扩展、低性能的;一个CPU支持上万的协程都不是问题。所以很适用于高并发处理。
2. 无需线程的上下文切换开销;(乍一看,什么意思呢?我们都知道python实际上是就是单线程,那都是怎么实现高并发操作呢,就是CPU高速的切换,每个任务都干一点,最后看上去是一起完事儿的,肉眼感觉就是多线程、多进程)
缺点:
1. 无法利用CPU的多核优点,这个好理解,进程里面包含线程,而协程就是细分后的协程,也就是说一个进程里面首先是线程其后才是协程,那肯定是用不了多核了,不过可以多进程配合,使用CPU的密集运算,平时我们用不到。
11.io多路复用
IO多路复用(IO Multiplexing)是指单个进程/线程就可以同时处理多个IO请求。
实现原理:用户将想要监视的文件描述符(File Descriptor)添加到select/poll/epoll函数中,由内核监视,函数阻塞。一旦有文件描述符就绪(读就绪或写就绪),或者超时(设置timeout),函数就会返回,然后该进程可以进行相应的读/写操作。
总结,区别主要在于:
2. 什么时候使用select/poll,什么时候使用epoll?
当连接数较多并且有很多的不活跃连接时,epoll的效率比其它两者高很多;但是当连接数较少并且都十分活跃的情况下,由于epoll需要很多回调,因此性能可能低于其它两者。
3. 什么是文件描述符?
文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。
内核通过文件描述符来访问文件。文件描述符指向一个文件。
4. 有哪些常见的IO模型?
https://blog.csdn.net/SkydivingWang/article/details/74917897
12.并行和并发
并行:多个处理器或者多核处理器同时执行多个不同的任务。
并发:一个处理器处理多个任务。
https://www.cnblogs.com/xc-chejj/p/10813692.html
13.进程池,线程池,内存池
首先介绍一个概念“池化技术 ”。池化技术就是:提前保存大量的资源,以备不时之需以及重复使用。池化技术应用广泛,如内存池,线程池,连接池等等。内存池相关的内容,建议看看Apache、Nginx等开源web服务器的内存池实现。
由于在实际应用当做,分配内存、创建进程、线程都会设计到一些系统调用,系统调用需要导致程序从用户态切换到内核态,是非常耗时的操作。因此,当程序中需要频繁的进行内存申请释放,进程、线程创建销毁等操作时,通常会使用内存池、进程池、线程池技术来提升程序的性能。
线程池:线程池的原理很简单,类似于操作系统中的缓冲区的概念,它的流程如下:先启动若干数量的线程,并让这些线程都处于睡眠状态,当需要一个开辟一个线程去做具体的工作时,就会唤醒线程池中的某一个睡眠线程,让它去做具体工作,当工作完成后,线程又处于睡眠状态,而不是将线程销毁。
进程池与线程池同理。
内存池:内存池是指程序预先从操作系统申请一块足够大内存,此后,当程序中需要申请内存的时候,不是直接向操作系统申请,而是直接从内存池中获取;同理,当程序释放内存的时候,并不真正将内存返回给操作系统,而是返回内存池。当程序退出(或者特定时间)时,内存池才将之前申请的内存真正释放。
14.什么是缓冲区溢出
缓冲区溢出是指当计算机向缓冲区内填充数据位数时超过了缓冲区本身的容量溢的数据覆盖在合法数据上,
危害:在当前网络与分布式系统安全中,被广泛利用的50%以上都是缓冲区溢出,其中最著名的例子是1988年利用fingerd漏洞的蠕虫。而缓冲区溢出中,最为危险的是堆栈溢出,因为入侵者可以利用堆栈溢出,在函数返回时改变返回程序的地址,让其跳转到任意地址,带来的危害一种是程序崩溃导致拒绝服务,另外一种就是跳转并且执行一段恶意代码,比如得到shell,然后为所欲为。通过往程序的缓冲区写超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,使程序转而执行其它指令,以达到攻击的目的。
造成缓冲区溢出的原因是程序中没有仔细检查用户输入的参数
15.虚拟内存,物理内存和共享内存
https://www.jianshu.com/p/3281463cb3bb
16.分页和分段(见18)
17.上下文切换
对于单核单线程CPU而言,在某一时刻只能执行一条CPU指令。上下文切换(Context Switch)是一种将CPU资源从一个进程分配给另一个进程的机制。从用户角度看,计算机能够并行运行多个进程,这恰恰是操作系统通过快速上下文切换造成的结果。在切换的过程中,操作系统需要先存储当前进程的状态(包括内存空间的指针,当前执行完的指令等等),再读入下一个进程的状态,然后执行此进程。
进程切换分两步:
1.切换页目录以使用新的地址空间
2.切换内核栈和硬件上下文
对于linux来说,线程和进程的最大区别就在于地址空间,对于线程切换,第1步是不需要做的,第2是进程和线程切换都要做的。
切换的性能消耗:
1、线程上下文切换和进程上下问切换一个最主要的区别是线程的切换虚拟内存空间依然是相同的,但是进程切换是不同的。这两种上下文切换的处理都是通过操作系统内核来完成的。内核的这种切换过程伴随的最显著的性能损耗是将寄存器中的内容切换出。
2、另外一个隐藏的损耗是上下文的切换会扰乱处理器的缓存机制。简单的说,一旦去切换上下文,处理器中所有已经缓存的内存地址一瞬间都作废了。还有一个显著的区别是当你改变虚拟内存空间的时候,处理的页表缓冲(processor's Translation Lookaside Buffer (TLB))或者相当的神马东西会被全部刷新,这将导致内存的访问在一段时间内相当的低效。但是在线程的切换中,不会出现这个问题。
18.分页和分段有什么区别(内存管理)
虚拟内存允许执行进程不必完全在内存中。虚拟内存的基本思想是:每个进程拥有独立的地址空间,这个空间被分为大小相等的多个块,称为页(Page),每个页都是一段连续的地址。这些页被映射到物理内存,但并不是所有的页都必须在内存中才能运行程序。当程序引用到一部分在物理内存中的地址空间时,由硬件立刻进行必要的映射;当程序引用到一部分不在物理内存中的地址空间时,由操作系统负责将缺失的部分装入物理内存并重新执行失败的命令。这样,对于进程而言,逻辑上似乎有很大的内存空间,实际上其中一部分对应物理内存上的一块(称为帧,通常页和帧大小相等),还有一些没加载在内存中的对应在硬盘上,如图5所示。
注意,请求分页系统、请求分段系统和请求段页式系统都是针对虚拟内存的,通过请求实现内存与外存的信息置换。
由图5可以看出,虚拟内存实际上可以比物理内存大。当访问虚拟内存时,会访问MMU(内存管理单元)去匹配对应的物理地址(比如图5的0,1,2)。如果虚拟内存的页并不存在于物理内存中(如图5的3,4),会产生缺页中断,从磁盘中取得缺的页放入内存,如果内存已满,还会根据某种算法将磁盘中的页换出。
段式存储管理是一种符合用户视角的内存分配管理方案。在段式存储管理中,将程序的地址空间划分为若干段(segment),如代码段,数据段,堆栈段;这样每个进程有一个二维地址空间,相互独立,互不干扰。段式管理的优点是:没有内碎片(因为段大小可变,改变段大小来消除内碎片)。但段换入换出时,会产生外碎片(比如4k的段换5k的段,会产生1k的外碎片)
页式存储管理方案是一种用户视角内存与物理内存相分离的内存分配管理方案。在页式存储管理中,将程序的逻辑地址划分为固定大小的页(page),而物理内存划分为同样大小的帧,程序加载时,可以将任意一页放入内存中任意一个帧,这些帧不必连续,从而实现了离散分离。页式存储管理的优点是:没有外碎片(因为页的大小固定),但会产生内碎片(一个页可能填充不满)。
两者的不同点:
目的不同:分页是由于系统管理的需要而不是用户的需要,它是信息的物理单位;分段的目的是为了能更好地满足用户的需要,它是信息的逻辑单位,它含有一组其意义相对完整的信息;
大小不同:页的大小固定且由系统决定,而段的长度却不固定,由其所完成的功能决定;
地址空间不同: 段向用户提供二维地址空间;页向用户提供的是一维地址空间;
信息共享:段是信息的逻辑单位,便于存储保护和信息的共享,页的保护和共享受到限制;
内存碎片:页式存储管理的优点是没有外碎片(因为页的大小固定),但会产生内碎片(一个页可能填充不满);而段式管理的优点是没有内碎片(因为段大小可变,改变段大小来消除内碎片)。但段换入换出时,会产生外碎片(比如4k的段换5k的段,会产生1k的外碎片)
作者:应届生老王
链接:https://zhuanlan.zhihu.com/p/124639863
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
面置换算法的主要目标是使页面置换频率最低(也可以说缺页率最低)。
颠簸本质上是指频繁的页调度行为。进程发生缺页中断时必须置换某一页。然而,其他所有的页都在使用,它置换一个页,但又立刻再次需要这个页。因此会不断产生缺页中断,导致整个系统的效率急剧下降,这种现象称为颠簸。内存颠簸的解决策略包括:
19.操作系统中进程调度策略有哪几种?
FCFS(先来先服务,队列实现,非抢占的):先请求CPU的进程先分配到CPU
SJF(最短作业优先调度算法):平均等待时间最短,但难以知道下一个CPU区间长度
优先级调度算法(可以是抢占的,也可以是非抢占的):优先级越高越先分配到CPU,相同优先级先到先服务,存在的主要问题是:低优先级进程无穷等待CPU,会导致无穷阻塞或饥饿;解决方案:老化
时间片轮转调度算法(可抢占的):队列中没有进程被分配超过一个时间片的CPU时间,除非它是唯一可运行的进程。如果进程的CPU区间超过了一个时间片,那么该进程就被抢占并放回就绪队列。
多级队列调度算法:将就绪队列分成多个独立的队列,每个队列都有自己的调度算法,队列之间采用固定优先级抢占调度。其中,一个进程根据自身属性被永久地分配到一个队列中。
多级反馈队列调度算法:与多级队列调度算法相比,其允许进程在队列之间移动:若进程使用过多CPU时间,那么它会被转移到更低的优先级队列;在较低优先级队列等待时间过长的进程会被转移到更高优先级队列,以防止饥饿发生。
20.什么是用户态和内核态
为了限制不同程序的访问能力,防止一些程序访问其它程序的内存数据,CPU划分了用户态和内核态两个权限等级。
所有用户程序都运行在用户态,但有时需要进行一些内核态的操作,比如从硬盘或者键盘读数据,这时就需要进行系统调用,使用陷阱指令,CPU切换到内核态,执行相应的服务,再切换为用户态并返回系统调用的结果。
2. 如何从用户态切换到内核态?
原子操作、信号量机制、自旋锁管程、会合、分布式系统
重要联接:https://zhuanlan.zhihu.com/p/126097769