百战c++(os2)
Posted 兔老大RabbitMQ
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了百战c++(os2)相关的知识,希望对你有一定的参考价值。
死锁是一组进程因为抢占资源导致相互等待,无法运行下去。
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 占有且等待:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不可强行占有:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
*死锁预防:通过设置某些限制条件,去破坏死锁的四个条件中的一个或几个条件,来预防发生死锁。但由于所施加的限制条件往往太严格,因而导致系统资源利用率和系统吞吐量降低。
*死锁避免:允许前三个必要条件,但通过明智的选择,确保永远不会到达死锁点,因此死锁避免比死锁预防允许更多的并发。
*死锁检测:不须实现采取任何限制性措施,而是允许系统在运行过程发生死锁,但可通过系统设置的检测机构及时检测出死锁的发生,并精确地确定于死锁相关的进程和资源,然后采取适当的措施,从系统中将已发生的死锁清除掉。(简化资源分配图)
*死锁解除:与死锁检测相配套的一种措施。当检测到系统中已发生死锁,需将进程从死锁状态中解脱出来。常用方法:撤销或挂起一些进程,以便回收一些资源,再将这些资源分配给已处于阻塞状态的进程。死锁检测盒解除有可能使系统获得较好的资源利用率和吞吐量,但在实现上难度也最大。
死锁避免 银行家算法
可用资源向量 Available 最大需求矩阵Max 分配矩阵Allocation 需求矩阵
需求=最大需求-已分配
第一步:
请求<需要的
请求<可分配的
然后分配 可用的=可用的-请求 已分=已分+请求 需要的=需要的-请求
第二部:安全性检验
Finish向量 每一个进程都为false work 向量=可用资源向量 Available
找到一个进程 finish为false 且 need<=work
然后work=work+allocation (分给它的资源)
重复该步骤 直到每一个finish 都为true. 则系统出于安全状态,否则撤销第一步的分配。
线程产生的原因:
进程可以使多个程序能并发执行,以提高资源的利用率和系统的吞吐量;但是其具有一些缺点:
进程在同一时间只能干一件事
进程在执行的过程中如果阻塞,整个进程就会挂起,即使进程中有些工作不依赖于等待的资源,仍然不会执行。
因此,操作系统引入了比进程粒度更小的线程,作为并发执行的基本单位,从而减少程序在并发执行时所付出的时空开销,提高并发性。和进程相比,线程的优势如下:
从资源上来讲,线程是一种非常"节俭"的多任务操作方式。在linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。
从切换效率上来讲,运行于一个进程中的多个线程,它们之间使用相同的地址空间,而且线程间彼此切换所需时间也远远小于进程间切换所需要的时间。据统计,一个进程的开销大约是一个线程开销的30倍左右。(
从通信机制上来讲,线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过进程间通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进城下的线程之间贡献数据空间,所以一个线程的数据可以直接为其他线程所用,这不仅快捷,而且方便。
除以上优点外,多线程程序作为一种多任务、并发的工作方式,还有如下优点:
1、使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。
2、改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序才会利于理解和修改。
1、FIFO(先进先出淘汰算法)
思想:最近刚访问的,将来访问的可能性比较大。
实现:使用一个队列,新加入的页面放入队尾,每次淘汰队首的页面,即最先进入的数据,最先被淘汰。
弊端:无法体现页面冷热信息
2、LFU(最不经常访问淘汰算法)
思想:如果数据过去被访问多次,那么将来被访问的频率也更高。
实现:每个数据块一个引用计数,所有数据块按照引用计数排序,具有相同引用计数的数据块则按照时间排序。每次淘汰队尾数据块。
开销:排序开销。
弊端:缓存颠簸。
3、LRU(最近最少使用替换算法)
思想:如果数据最近被访问过,那么将来被访问的几率也更高。
实现:使用一个栈,新页面或者命中的页面则将该页面移动到栈底,每次替换栈顶的缓存页面。
优点:LRU算法对热点数据命中率是很高的。
缺陷:
1)缓存颠簸,当缓存(1,2,3)满了,之后数据访问(0,3,2,1,0,3,2,1。。。)。
2)缓存污染,突然大量偶发性的数据访问,会让内存中存放大量冷数据。
4、LRU-K(LRU-2、LRU-3)
思想:最久未使用K次淘汰算法。
LRU-K中的K代表最近使用的次数,因此LRU可以认为是LRU-1。LRU-K的主要目的是为了解决LRU算法“缓存污染”的问题,其核心思想是将“最近使用过1次”的判断标准扩展为“最近使用过K次”。
相比LRU,LRU-K需要多维护一个队列,用于记录所有缓存数据被访问的历史。只有当数据的访问次数达到K次的时候,才将数据放入缓存。当需要淘汰数据时,LRU-K会淘汰第K次访问时间距当前时间最大的数据。
实现:
1)数据第一次被访问,加入到访问历史列表;
2)如果数据在访问历史列表里后没有达到K次访问,则按照一定规则(FIFO,LRU)淘汰;
3)当访问历史队列中的数据访问次数达到K次后,将数据索引从历史队列删除,将数据移到缓存队列中,并缓存此数据,缓存队列重新按照时间排序;
4)缓存数据队列中被再次访问后,重新排序;
5)需要淘汰数据时,淘汰缓存队列中排在末尾的数据,即:淘汰“倒数第K次访问离现在最久”的数据。
针对问题:
LRU-K的主要目的是为了解决LRU算法“缓存污染”的问题,其核心思想是将“最近使用过1次”的判断标准扩展为“最近使用过K次”。
大端是指低字节存储在高地址;小端存储是指低字节存储在低地址。我们可以根据联合体来判断该系统是大端还是小端。因为联合体变量总是从低地址存储。
1)正常进程
正常情况下,子进程是通过父进程创建的,子进程再创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束。 当一个进程完成它的工作终止之后,它的父进程需要调用wait()或者waitpid()系统调用取得子进程的终止状态。
unix提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息, 就可以得到:在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。 但是仍然为其保留一定的信息,直到父进程通过wait / waitpid来取时才释放。保存信息包括:
1进程号the process ID
2退出状态the termination status of the process
3运行时间the amount of CPU time taken by the process等
2)孤儿进程
一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
3)僵尸进程
一个进程使用fork创建子进程,如果子进程退出,而父进程因为异常退出并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程。
僵尸进程是一个进程必然会经过的过程:这是每个子进程在结束时都要经过的阶段。
如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。
如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。
危害:
如果进程不调用wait / waitpid的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程。
外部消灭:
通过kill发送SIGTERM或者SIGKILL信号消灭产生僵尸进程的进程,它产生的僵死进程就变成了孤儿进程,这些孤儿进程会被init进程接管,init进程会wait()这些孤儿进程,释放它们占用的系统进程表中的资源
内部解决:
1、子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait进行处理僵尸进程。
以上是关于百战c++(os2)的主要内容,如果未能解决你的问题,请参考以下文章