10+小故事揭秘高頻「操作系統(tǒng)面試題」
面試的過(guò)程中,為了考察面試者的基礎(chǔ)功力,除了算法以外,操作系統(tǒng)將會(huì)占比很大的權(quán)重,本文給大家分享我在面試過(guò)程中出現(xiàn)的非常高頻的面試題,我基本上會(huì)從兩個(gè)角度來(lái)闡述,一個(gè)是"官話",一個(gè)是“大白話”。希望對(duì)即將面試的你有所幫助
1、為什么有了進(jìn)程,還要有線程呢?
為了提高系統(tǒng)資源的利用率和系統(tǒng)的吞吐量,通常進(jìn)程可讓多個(gè)程序并發(fā)的執(zhí)行,但是也會(huì)帶來(lái)一些問(wèn)題
官話
進(jìn)程如果在執(zhí)行的過(guò)程被阻塞,那這個(gè)進(jìn)程將被掛起,這時(shí)候進(jìn)程中有些等待的資源得不到執(zhí)行:
進(jìn)程在同一時(shí)間只能做一件事兒
基于以上的缺點(diǎn),操作系統(tǒng)引入了比進(jìn)程粒度更小的線程,作為并發(fā)執(zhí)行的基本單位,從而減少程序在并發(fā)執(zhí)行時(shí)所付出的時(shí)間和空間開銷,提高并發(fā)性能。
舉個(gè)例子
小Q當(dāng)年開發(fā)了一個(gè)聊天軟件,給女朋友說(shuō):咋們以后不用什么qq,微信了,我寫個(gè)聊天工具,咱兩正兒八經(jīng)的兩人世界可好。
說(shuō)干就干,小Q很快就完成了開發(fā),然后開始測(cè)試,打電話讓女朋友讓她發(fā)個(gè)信息,小Q就等著,等啊等啊,怎么還沒(méi)有發(fā)信息過(guò)來(lái),沒(méi)顯示啊,一臉懵逼,單步調(diào)試吧,發(fā)現(xiàn)程序卡在了wati for user input不動(dòng)了。牛逼,程序不輸入就沒(méi)辦法執(zhí)行后面的任務(wù)。咋搞?要不設(shè)置個(gè)1s,用戶1s不輸入直接跳過(guò)進(jìn)行后面的接收階段和顯示階段,牛皮牛皮,果然好使,好使個(gè)錘錘,這樣用戶輸入信息不就很可能丟失,咋搞?
能不能將輸入和顯示這兩個(gè)動(dòng)作給分開,一個(gè)負(fù)責(zé)輸入,發(fā)送消息,一個(gè)負(fù)責(zé)讀信息和顯示。不夸夸你自己?jiǎn)?,直接開干呈現(xiàn)兩個(gè)窗口。
回來(lái)回來(lái),這就是我們的多進(jìn)程。不過(guò)多進(jìn)程也還是有些問(wèn)題需要注意,開多個(gè)窗口沒(méi)問(wèn)題,無(wú)腦開窗口撩騷直接被榨干(內(nèi)存耗盡),而且想要幾個(gè)窗口交換個(gè)數(shù)據(jù)也是賊麻煩,這是為啥呢
多進(jìn)程的程序,每個(gè)進(jìn)程都有自己的獨(dú)立內(nèi)存空間,都穿了衣服,不能相互亂看,要想通信就要接觸系統(tǒng)層面來(lái)通信,所以肯定就會(huì)造成較多的資源消耗和時(shí)間浪費(fèi)。怎么整?
幾個(gè)進(jìn)程為了方便,干脆商量一波,能不能開辟一塊內(nèi)存空間,共樂(lè)其中,這就是線程非常重要的意義,不過(guò)共享了不代表我們就是"裸"的,個(gè)人保密還是要做到,也不要吵架,可不可以通過(guò)鎖的方式保密呢,這就涉及到了線程的同步。這樣我猜測(cè)你應(yīng)該了解進(jìn)程和線程了吧。
2、簡(jiǎn)單說(shuō)下你對(duì)并發(fā)和并行的理解?
官話從概念出發(fā):
并發(fā)
在一個(gè)時(shí)間段中多個(gè)程序都啟動(dòng)運(yùn)行在同一個(gè)處理機(jī)中
并行
假設(shè)目前A,B兩個(gè)進(jìn)程,兩個(gè)進(jìn)程分別由不同的 CPU ?管理執(zhí)行,兩個(gè)進(jìn)程不搶占 CPU ?資源且可以同時(shí)運(yùn)行,這叫做并行。
例子
并發(fā)是指多個(gè)任務(wù)在一段時(shí)間內(nèi)發(fā)生。比如今日成都某家老火鍋店做活動(dòng),全場(chǎng)5折(這還是比較狠),但是只有200個(gè)位置,但是來(lái)的人太多了,來(lái)了250個(gè)人,此時(shí)多出的50個(gè)人只好等待著或者去另外家火鍋店。那么火鍋店老板對(duì)這250的安排不是同一時(shí)刻安排而是一段時(shí)間去處理,其實(shí)這就是并發(fā)。這個(gè)例子好像整的不算生動(dòng),我們?cè)賮?lái)一個(gè)
到了周末就是我們"開黑"的時(shí)光,奈何到了周一不遲到怎么對(duì)得起自己,但是遲到了被逮住就要被"BB"。好嘛,我們作為學(xué)生娃兒只好認(rèn)慫,常規(guī)操作,兩腳一蹬,起床,刷牙,上廁所,拿包包沖。就是這樣類似復(fù)讀機(jī)的習(xí)慣操作是怎么回事?莫非我們就能同時(shí)干這么多事?其實(shí)不是的,我們大腦下達(dá)指令,起床,刷牙這些操作早形成了肌肉記憶,所以我們?cè)谶@小段時(shí)間完成了這么多事兒,還可以多幾分鐘出來(lái)看看美女不香?
平時(shí)玩兒電腦的時(shí)候,邊寫代碼邊聽音樂(lè),計(jì)算機(jī)同時(shí)處理了這么多任務(wù)。如果是單核 CPU ?,在我們看來(lái)這些事兒是同時(shí)發(fā)生的,其實(shí)那是因?yàn)榈讓?CPU ?切換的速度太快以致于我們完全感受不到它的切換,僅此為錯(cuò)覺(jué)而已。但是如果是多核 ?CPU ? ?,各個(gè) CPU ?負(fù)責(zé)不同的進(jìn)程,各個(gè)進(jìn)程不搶占 CPU ?,這樣同時(shí)進(jìn)行,這就是真正意義上的并行。
說(shuō)了這么多,那并行和并發(fā)到底啥區(qū)別?
兩者區(qū)別在于是否"同時(shí)"發(fā)生。是在一段時(shí)間同時(shí)發(fā)生還是多個(gè)事情在同一個(gè)時(shí)間點(diǎn)同時(shí)發(fā)生。
3、同步、異步、阻塞、非阻塞的概念
首先大家應(yīng)該知道同步異步,阻塞非阻塞是兩個(gè)不同層面的問(wèn)題,一個(gè)是operation層面,一個(gè)是kernal層面。同步異步最大的區(qū)別在于是否需要底層的響應(yīng)再執(zhí)行。阻塞非阻塞最大的區(qū)別在于是否立即給出響應(yīng)。
官話
同步:當(dāng)一個(gè)同步調(diào)用發(fā)出后,調(diào)用者要一直等待返回結(jié)果。通知后,才能進(jìn)行后續(xù)的執(zhí)行。
異步:當(dāng)一個(gè)異步過(guò)程調(diào)用發(fā)出后,調(diào)用者不能立刻得到返回結(jié)果。實(shí)際處理這個(gè)調(diào)用的部件在完成后,通過(guò)狀態(tài)、通知和回調(diào)來(lái)通知調(diào)用者。
阻塞:是指調(diào)用結(jié)果返回前,當(dāng)前線程會(huì)被掛起,即阻塞。
非阻塞:是指即使調(diào)用結(jié)果沒(méi)返回,也不會(huì)阻塞當(dāng)前線程。
形象比喻:
小Q去釣魚,拋完線后就傻傻的看著有沒(méi)有動(dòng)靜,有則拉桿(同步阻塞)
小Q去釣魚,拿魚網(wǎng)撈一下,有沒(méi)有魚立即知道,不用等,直接就撈(同步非阻塞)
小Q去釣魚,這個(gè)魚缸比較牛皮,扔了后自己就打王者榮耀去了,因?yàn)轸~上鉤了這個(gè)魚缸帶的報(bào)警器會(huì)通知我。這樣實(shí)現(xiàn)異步(異步非阻塞)
4、進(jìn)程和線程的相關(guān)題
官話:
進(jìn)程:進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位,是系統(tǒng)中的并發(fā)執(zhí)行的單位。
線程:線程是進(jìn)程的一個(gè)實(shí)體,也是 ?CPU ? 調(diào)度和分派的基本單位,它是比進(jìn)程更小的能獨(dú)立運(yùn)行的基本單位,有時(shí)又被稱為輕量級(jí)進(jìn)程。
進(jìn)程是資源分配的最小單位,而線程是 ** CPU ? 調(diào)度**的最小單位;
創(chuàng)建進(jìn)程或撤銷進(jìn)程,系統(tǒng)都要為之分配或回收資源,操作系統(tǒng)開銷遠(yuǎn)大于創(chuàng)建或撤銷線程時(shí)的開銷;
不同進(jìn)程地址空間相互獨(dú)立,同一進(jìn)程內(nèi)的線程共享同一地址空間。一個(gè)進(jìn)程的線程在另一個(gè)進(jìn)程內(nèi)是不可見(jiàn)的;
進(jìn)程間不會(huì)相互影響,而一個(gè)線程掛掉將可能導(dǎo)致整個(gè)進(jìn)程掛掉;
別背了,我知道你們都會(huì)背,舉個(gè)例子
計(jì)算機(jī)中的核心是 CPU ?,說(shuō)它是我們的大腦一點(diǎn)不為過(guò)。在此用一個(gè)連鎖火鍋店舉例,因?yàn)橐咔榈挠绊?,今天到目前營(yíng)業(yè)額一般,只好開一家店,其他疫情較為嚴(yán)重的店只好先關(guān)閉,這里涉及的含義:?jiǎn)蝹€(gè) CPU ?一次只運(yùn)行一個(gè)任務(wù)。進(jìn)程就類似這家火鍋店,它代表 CPU ?所能處理的單個(gè)任務(wù),其他地方火鍋店只能處于非運(yùn)行狀態(tài)。
一個(gè)火鍋店有很多服務(wù)員,一起協(xié)同工作,將火鍋店做的紅紅火火,那么線程就好比這些服務(wù)員,一個(gè)進(jìn)程可有多個(gè)線程。、
火鍋店各個(gè)工作房間是共享的,所有服務(wù)員都可以進(jìn)出,這意味著進(jìn)程的內(nèi)存空間共享,每個(gè)線程都可以使用這些共享內(nèi)存。但是不是每個(gè)房間都可以容納相同的人數(shù),比如衛(wèi)生間就只有一個(gè)人,其他人不能同時(shí)進(jìn)去。這意味著一個(gè)線程在使用共享內(nèi)存的時(shí)候,其他線程需要等待結(jié)束才能使用這塊內(nèi)存。那怎么防止別人進(jìn)入呢?很直接的辦法就是進(jìn)去之后記得關(guān)門(上鎖),上了鎖,其他人想進(jìn)來(lái)發(fā)現(xiàn)是上鎖了,那就等鎖打開后再進(jìn)去,這就叫做互斥鎖,防止多個(gè)線程同時(shí)讀寫某一塊內(nèi)存區(qū)域。
ok到這里,我們總結(jié)一波
如果以多進(jìn)程的方式運(yùn)行,那么允許多個(gè)任務(wù)同時(shí)運(yùn)行
如果以多線程的方式運(yùn)行,那么允許將單個(gè)任務(wù)分成不同的部分運(yùn)行
為了防止進(jìn)程/線程之間產(chǎn)生沖突和允許進(jìn)程之間的共享資源,需要提供一種協(xié)調(diào)機(jī)制。
多線程與多進(jìn)程的基本概念
不知道大家經(jīng)歷過(guò)食堂打菜的場(chǎng)景沒(méi)有,如果學(xué)校食堂就開設(shè)一個(gè)窗口,打菜的阿姨也沒(méi)辦法,只好一個(gè)個(gè)給大家依次打菜,這就好比"單線程",效率非常低(此處可以考慮為什么redis使用單線程卻這么牛逼)
為了提高效率,在食堂多加了幾個(gè)窗口,這就類似"多線程"形式。
那么又想起一個(gè)問(wèn)題,“多線程一定就比單線程效率高麥?”(ps Memcache是多線程模型而Redis是單線程模型)
貌似我們一提到高并發(fā),分布式,就不得不想起多線程,那么多線程一定比單線程效率高?
上面說(shuō)了采用多線程多核效果更好,但是多線程對(duì) CPU ?,內(nèi)存等都要求比較高,如果存在的上下文切換過(guò)于耗時(shí),互斥時(shí)間太久,效率反而會(huì)低。
不一定。我不從專業(yè)術(shù)語(yǔ)來(lái)將,舉個(gè)例子,假設(shè)目前接水房有四個(gè)水管可以接水,我如果用4個(gè)桶分別對(duì)應(yīng)4個(gè)水管,那么就比較完美,如果少一個(gè)則閑置一個(gè),多一個(gè)則會(huì)出現(xiàn)搶占。如果此時(shí)我的水桶個(gè)數(shù)大于水管數(shù),為了每個(gè)桶都有水,我們就需要切換水桶,這個(gè)過(guò)程實(shí)際上就是線程的上下文切換,代價(jià)一樣不小。
多線程與多進(jìn)程的應(yīng)用場(chǎng)景
多線程的優(yōu)點(diǎn)
更加高效的內(nèi)存共享。多進(jìn)程下內(nèi)存共享不便
較輕的上下文切換。因?yàn)椴挥们袚Q地址空間,CR3寄存器和清空TLB
多進(jìn)程的優(yōu)點(diǎn):
各個(gè)進(jìn)程有自己內(nèi)存空間,所以具有更強(qiáng)的容錯(cuò)性,不至于一個(gè)集成crash導(dǎo)致系統(tǒng)崩潰
具有更好的多核可伸縮性,因?yàn)檫M(jìn)程將地址空間,頁(yè)表等進(jìn)行了隔離,在多核的系統(tǒng)上可伸縮性更強(qiáng)
如何提升多線程的效率
盡量使用池化技術(shù),也就是線程池,從而不用頻繁的創(chuàng)建,銷毀線程
減少線程之間的同步和通信
通過(guò)Huge Page的方式避免產(chǎn)生大量的缺頁(yè)異常
避免需要頻繁共享寫的數(shù)據(jù)
5、進(jìn)程的狀態(tài)轉(zhuǎn)換
在Linux中,進(jìn)程的狀態(tài)有七種
可運(yùn)行狀態(tài)
英文名詞為TASK_RUNNING,其實(shí)這個(gè)狀態(tài)雖然是RUNING,實(shí)際上并不一定會(huì)占有 CPU ?,可能修改TASK_RUNABLE會(huì)更妥當(dāng)。TASK_RUNGING根據(jù)是否在在 CPU ?上運(yùn)行分為RUNGING和READY兩種狀態(tài)。處于READY狀態(tài)的進(jìn)程隨時(shí)可以運(yùn)行,只不過(guò)因?yàn)榇藭r(shí) CPU ?資源受限,調(diào)度器沒(méi)選中運(yùn)行
可中斷睡眠狀態(tài)與不可中斷睡眠狀態(tài)
我們知道進(jìn)程不可能一直處于可運(yùn)行的狀態(tài)。假設(shè)A進(jìn)程需要讀取磁盤中的文件,這樣的系統(tǒng)調(diào)用消耗時(shí)間較長(zhǎng),進(jìn)程需要等待較長(zhǎng)的時(shí)間才能執(zhí)行后面的命令,而且等待的時(shí)間還是不可估算的,這樣的話進(jìn)程還占用 CPU ?就不友好了,因此內(nèi)核就會(huì)將其更改為其他的狀態(tài)并從 CPU ?可運(yùn)行的隊(duì)列移除。
Linux中存在兩種睡眠狀態(tài),分別為:可中斷的睡眠狀態(tài)和不可中斷的狀態(tài)。兩者最大的區(qū)別為是否響應(yīng)收到的信號(hào),那么從可中斷的睡眠的進(jìn)程是如何返回到可運(yùn)行的狀態(tài)呢
等待的事情發(fā)生且繼續(xù)運(yùn)行的條件滿足
收到了沒(méi)有被屏蔽的信號(hào)
處于此狀態(tài)的進(jìn)程,收到信號(hào)會(huì)返回EINTR給用戶空間。開發(fā)者通過(guò)檢測(cè)返回值的方式進(jìn)行后續(xù)邏輯處理
但是對(duì)于不可中斷的睡眠狀態(tài),就只有一種方式返回到可運(yùn)行狀態(tài),即等待事情發(fā)生了繼續(xù)運(yùn)行
上圖中為什么出現(xiàn)個(gè) TASK_UNINTERRUPTIBLE 狀態(tài),主要是因?yàn)閮?nèi)核中的進(jìn)程不是什么進(jìn)程都可以被打斷,假設(shè)響應(yīng)的是異步信號(hào),程序在執(zhí)行的過(guò)程中插入一段用于處理異步信號(hào)的而流程,原來(lái)的流程就會(huì)被中斷。所以當(dāng)進(jìn)程在和硬件打交道的時(shí)候,需要使用 TASK_UNINTERRUPTIBLE 狀態(tài)將進(jìn)程保護(hù)起來(lái),從而避免進(jìn)程和設(shè)備打交道的過(guò)程中被打斷導(dǎo)致設(shè)備處于不可控的狀態(tài)。
那么TASK_UNINTERRUPTIBLE狀態(tài)會(huì)出現(xiàn)多久呢?
其實(shí) TASK_UNINTERRUPTIBLE 狀態(tài)是很危險(xiǎn)的狀態(tài),因?yàn)樗稑尣蝗耄銦o(wú)法通過(guò)信號(hào)殺死這樣一個(gè)不可中斷的休眠狀態(tài),正常情況,TASK_UNINTERRUPTIBLE狀態(tài)存在時(shí)間很短,但是不排除存在此狀態(tài)進(jìn)程比較持久的情況,真的刀槍不入了?可不可以進(jìn)行提前的預(yù)防?
可以的,早就考慮了。內(nèi)核提供了hung task機(jī)制,它會(huì)啟動(dòng)一個(gè)khungtaskd內(nèi)核線程對(duì)TASK_UNINTERRUPTIBLE狀態(tài)進(jìn)行檢測(cè),不能讓他失控了。khungtaskd會(huì)定期的喚醒,如果超過(guò)120s都還沒(méi)有調(diào)度,內(nèi)核就會(huì)通過(guò)打印警告和堆棧信息。當(dāng)然,不一定就是120s,可以通過(guò)下面選項(xiàng)進(jìn)行定制
sysctl?kernel.hung_task_timeout_secs
說(shuō)了這么多,我們?cè)趺粗赖降子袥](méi)有出現(xiàn)這個(gè)狀態(tài),哪里看?可以通過(guò)/proc和ps進(jìn)行查看
睡眠進(jìn)程和等待隊(duì)列
不管是上面提到的可中斷的睡眠進(jìn)程還是不可中斷的睡眠進(jìn)程,都離不開一種數(shù)據(jù)結(jié)構(gòu)---隊(duì)列。哦?假設(shè)進(jìn)程A因?yàn)槟衬吃蛐枰菝?,為啥要休眠,等待的資源遲遲拿不到或者等待的事件總是不來(lái),沒(méi)法進(jìn)行下一步操作,這個(gè)時(shí)候內(nèi)核來(lái)了,"行吧,我不會(huì)拋棄你,我一定會(huì)想辦法讓你和等待的資源(事件)扯上關(guān)系",只要等待的時(shí)機(jī)到來(lái)我就喚醒你,這采用的方法即"等待隊(duì)列"。如果進(jìn)一步深究,想了解它的底層實(shí)現(xiàn)(采用了雙向鏈表),文末我會(huì)給大家推薦基本書籍。
TASK_STOPPED狀態(tài)于TASK_TRACED狀態(tài)
TASK_STOPPED狀態(tài)屬于比較特殊的狀態(tài),可以通過(guò)SIGCONT信號(hào)回復(fù)進(jìn)程的執(zhí)行
TASK_TRACED是被跟蹤的狀態(tài),進(jìn)程會(huì)停下來(lái)等待跟蹤它的進(jìn)程對(duì)它進(jìn)行進(jìn)一步的操作
EXIT_ZOMBIE狀態(tài)與EXIT_DEAD狀態(tài)
當(dāng)進(jìn)程儲(chǔ)于這兩種的任意一種,就可以宣布"死亡" 。
就緒 —> 執(zhí)行:準(zhǔn)備就緒,調(diào)度器滿足了的需求,給我一種策略,我就可從就緒變?yōu)閳?zhí)行的狀態(tài);
執(zhí)行 —> 阻塞:不是每個(gè)進(jìn)程都是那么一帆風(fēng)順,就像我們每次考試,不管是中考,高考還是考研,難免都會(huì)出現(xiàn)磕磕盼盼,遇到了可能暫時(shí)會(huì)阻擋我們前行的小事兒,可是要相信不會(huì)一直的阻擋我們,只要我們有恒心堅(jiān)持,時(shí)機(jī)到來(lái),你也準(zhǔn)備好了,那就美哉。回到這里,對(duì)于進(jìn)程而言,當(dāng)需要等到某個(gè)事情發(fā)生而無(wú)法執(zhí)行的時(shí)候,進(jìn)程就變?yōu)?strong style="font-size: inherit;line-height: inherit;color: rgb(255, 69, 0);">阻塞的狀態(tài)。比如當(dāng)前進(jìn)程提出輸入請(qǐng)求,如進(jìn)程提出輸入/輸出請(qǐng)求,進(jìn)程所申請(qǐng)資源(主存空間或外部設(shè)備)得不到滿足時(shí)變成等待資源狀態(tài),進(jìn)程運(yùn)行中出現(xiàn)了故障(程序出錯(cuò)或主存儲(chǔ)器讀寫錯(cuò)等)變成等待干預(yù)狀態(tài)等等;
阻塞 —> 就緒:處于阻塞狀態(tài)的進(jìn)程,在其等待的事件已經(jīng)發(fā)生,如輸入/輸出完成,資源得到滿足或錯(cuò)誤處理完畢時(shí),處于等待狀態(tài)的進(jìn)程并不馬上轉(zhuǎn)入執(zhí)行狀態(tài),而是先轉(zhuǎn)入就緒狀態(tài),然后再由系統(tǒng)進(jìn)程調(diào)度程序在適當(dāng)?shù)臅r(shí)候?qū)⒃撨M(jìn)程轉(zhuǎn)為執(zhí)行狀態(tài);
執(zhí)行 —> 就緒:正在執(zhí)行的進(jìn)程,因時(shí)間片用完而被暫停執(zhí)行,或在采用搶先式優(yōu)先級(jí)調(diào)度算法的系統(tǒng)中,當(dāng)有更高優(yōu)先級(jí)的進(jìn)程要運(yùn)行而被迫讓出處理機(jī)時(shí),該進(jìn)程便由執(zhí)行狀態(tài)轉(zhuǎn)變?yōu)榫途w狀態(tài)。
6、進(jìn)程間的通信方式有哪些?
管道
學(xué)習(xí)軟件工程規(guī)范的時(shí)候,我們知道瀑布模型,在整個(gè)項(xiàng)目開發(fā)過(guò)程分為多個(gè)階段,上一階段的輸出作為下一階段的輸入。各個(gè)階段的具體內(nèi)容如下圖所示
最初我們?cè)趯W(xué)習(xí)Linux基本命令使用的時(shí)候,我們經(jīng)常通過(guò)多個(gè)命令的組合來(lái)完成我們的需求。比如說(shuō)我們想知道如何查看進(jìn)程或者端口是否在使用,會(huì)使用下面的這條命令
netstat -nlp | grep XXX
這里的"|"實(shí)際上就是管道的意思。"|"前面部分作為"|"后面的輸入,很明顯是單向的傳輸,這樣的管道我們叫做"匿名管道",自行創(chuàng)建和銷毀。既然有匿名管道,應(yīng)該就有帶名字的管道"命名管道"。如果你想雙向傳輸,可以考慮使用兩個(gè)管道拼接即可。
創(chuàng)建命名管道的方式
mkfifo test
test即為管道的名稱,在Linux中一切皆文件,管道也是以文件的方式存在,咋們可以使用ls -l 查看下文件的屬性,它會(huì)"p"標(biāo)識(shí)。
下面我們向管道寫入內(nèi)容
echo "666" > test
此時(shí)按道理來(lái)說(shuō)咋們已經(jīng)將內(nèi)容寫入了test,沒(méi)有直接輸出是因?yàn)槲覀冃枰_啟另一個(gè)終端進(jìn)行輸出(可以理解為暫存管道)
cat < test
ok,我們發(fā)現(xiàn)管道內(nèi)容被讀出來(lái),同時(shí)echo退出。那么管道這種通信方式有什么缺點(diǎn)?我們知道瀑布模型的軟件開發(fā)模式是非常低下的,同理采用管道進(jìn)行通信的效率也很低,因?yàn)榧僭O(shè)現(xiàn)在有AB兩個(gè)進(jìn)程,A進(jìn)程將數(shù)據(jù)寫入管道,B進(jìn)程需要等待A進(jìn)程將信息寫完以后才能讀出來(lái),所以這種方案不適合頻繁的通信。那優(yōu)點(diǎn)是什么?
最明顯的優(yōu)點(diǎn)就是簡(jiǎn)單,我們平時(shí)經(jīng)常使用以致于都不知道這是管道。鑒于上面的缺點(diǎn),我們?cè)趺慈浹a(bǔ)呢?接著往下看
消息隊(duì)列
管道通信屬于一股腦的輸入,能不能稍微溫柔點(diǎn)有規(guī)矩點(diǎn)的發(fā)送消息?
答:可以的。消息隊(duì)列在發(fā)送數(shù)據(jù)的時(shí)候,按照一個(gè)個(gè)獨(dú)立單元(消息體)進(jìn)行發(fā)送,其中每個(gè)消息體規(guī)定大小塊,同時(shí)發(fā)送方和接收方約定好消息類型或者正文的格式。
在管道中,其大小受限且只能承載無(wú)格式字節(jié)流的方式,而消息隊(duì)列允許不同進(jìn)程以消息隊(duì)列的形式發(fā)送給任意的進(jìn)程。
但是當(dāng)發(fā)送到消息隊(duì)列的數(shù)據(jù)太大,需要拷貝的時(shí)間也就越多,所以還有其他的方式?繼續(xù)看
共享內(nèi)存
使用消息隊(duì)列可以達(dá)到不錯(cuò)的效果,但是如果我們兩個(gè)部門需要交換比較大的數(shù)據(jù)的時(shí)候,一發(fā)一收還是不能及時(shí)的感知數(shù)據(jù)。能不能更好的辦法,雙方能很快的分享內(nèi)容數(shù)據(jù),答:有的,共享內(nèi)存
我們知道每個(gè)進(jìn)程都有自己的虛擬內(nèi)存空間,不同的進(jìn)程映射到不同的物理內(nèi)存空間。那么我們可不可以申請(qǐng)一塊虛擬地址空間,不同進(jìn)程通過(guò)這塊虛擬地址空間映射到相同的屋里地址空間呢?這樣不同進(jìn)程就可以及時(shí)的感知進(jìn)程都干了啥,就不需要再拷貝來(lái)拷貝去。
我們可以通過(guò)shmget創(chuàng)建一份共享內(nèi)存,并可以通過(guò)ipcs命令查看我們創(chuàng)建的共享內(nèi)存。此時(shí)如果一個(gè)進(jìn)程需要訪問(wèn)這段內(nèi)存,需要將這個(gè)內(nèi)存加載到自己虛擬地址空間的一個(gè)位置,讓內(nèi)核給它一個(gè)合法地址。使用完畢接觸板頂并刪除內(nèi)存對(duì)象。
那么問(wèn)題來(lái)了,這么多進(jìn)程都共享這塊內(nèi)存,如果同時(shí)都往里面寫內(nèi)容,難免會(huì)出現(xiàn)沖突的現(xiàn)象,比如A進(jìn)程寫了數(shù)字5,B進(jìn)程同樣的地址寫了6就直接給覆蓋了,這樣就不友好了,怎么辦?繼續(xù)往下看
信號(hào)量
為了防止沖突,我們得有個(gè)約束或者說(shuō)一種保護(hù)機(jī)制。使得同一份共享的資源只能一個(gè)進(jìn)程使用,這里就出現(xiàn)了信號(hào)量機(jī)制。
信號(hào)量實(shí)際上是一個(gè)計(jì)數(shù)器,這里需要注意下,信號(hào)量主要實(shí)現(xiàn)進(jìn)程之間的同步和互斥,而不是存儲(chǔ)通信內(nèi)容。
信號(hào)量定義了兩種操作,p操作和v操作,p操作為申請(qǐng)資源,會(huì)將數(shù)值減去M,表示這部分被他使用了,其他進(jìn)程暫時(shí)不能用。v操作是歸還資源操作,告知?dú)w還了資源可以用這部分。
信號(hào)
從管道----消息隊(duì)列-共享內(nèi)存/信號(hào)量,有需要等待的管道機(jī)制,共享內(nèi)存空間的進(jìn)程通信方式,還有一種特殊的方式--信號(hào)
我們或許聽說(shuō)過(guò)運(yùn)維或者部分開發(fā)需要7 * 24小時(shí)值守(項(xiàng)目需要上線的時(shí)候),當(dāng)然也有各種監(jiān)管,告警系統(tǒng),一旦出現(xiàn)系統(tǒng)資源緊張等問(wèn)題就會(huì)告知開發(fā)或運(yùn)維人員,對(duì)應(yīng)到操作系統(tǒng)中,這就是信號(hào)。
在操作系統(tǒng)中,不同信號(hào)用不同的值表示,每個(gè)信號(hào)設(shè)置相應(yīng)的函數(shù),一旦進(jìn)程發(fā)送某一個(gè)信號(hào)給另一個(gè)進(jìn)程,另一進(jìn)程將執(zhí)行相應(yīng)的函數(shù)進(jìn)行處理。也就是說(shuō)把可能出現(xiàn)的異常等問(wèn)題準(zhǔn)備好,一旦信號(hào)產(chǎn)生就執(zhí)行相應(yīng)的邏輯即可。
套接字
上面的幾種方式都是單機(jī)情況下多個(gè)進(jìn)程的通信方式,如果我想和相隔幾千里的小姐姐通信怎么辦?
這就需要套接字socket了。其實(shí)這玩意隨處可見(jiàn),我們平時(shí)的聊天,我們天天請(qǐng)求瀏覽器給予的響應(yīng)等,都是這老鐵。
小結(jié)
分享了一下幾種進(jìn)程間通信方式,希望大家能知其然并知其所以然,機(jī)械式的記憶容易忘記哦。
管道
消息隊(duì)列
共享內(nèi)存
信號(hào)量
信號(hào)
套接字
7、進(jìn)程的調(diào)度算法有哪些?
調(diào)度算法是指:調(diào)度程序是內(nèi)核的重要組成部分,決定這下一個(gè)要運(yùn)行的進(jìn)程。那么根據(jù)系統(tǒng)的資源分配策略所規(guī)定的資源分配算法。常用的調(diào)度算法有:先來(lái)先服務(wù)調(diào)度算法、時(shí)間片輪轉(zhuǎn)調(diào)度法、短作業(yè)優(yōu)先調(diào)度算法、最短剩余時(shí)間優(yōu)先、高響應(yīng)比優(yōu)先調(diào)度算法、優(yōu)先級(jí)調(diào)度算法等等。
先來(lái)先服務(wù)調(diào)度算法
先來(lái)先服務(wù)讓我們想起了隊(duì)列的先進(jìn)先出特性,每一次的調(diào)度都從隊(duì)列中選擇最先進(jìn)入隊(duì)列的投入運(yùn)行。
時(shí)間片輪轉(zhuǎn)調(diào)度算法
先來(lái)理解輪轉(zhuǎn),假設(shè)當(dāng)前進(jìn)程A、B、C、D,按照進(jìn)程到達(dá)的時(shí)間排序,而且每個(gè)進(jìn)行都有著同樣大小的時(shí)間片。如果這個(gè)進(jìn)程在當(dāng)前的時(shí)間片運(yùn)行結(jié)束,啥事兒沒(méi)有,直接將進(jìn)程從隊(duì)列移除完事兒。如果進(jìn)程在這個(gè)時(shí)間片跑完都沒(méi)有結(jié)束,進(jìn)程變?yōu)榈却隣顟B(tài),放在進(jìn)程尾部直到所有進(jìn)程執(zhí)行完畢。
為什么進(jìn)程要切換,切換無(wú)外乎是時(shí)間片夠用或者不夠用。如果時(shí)間片夠用,那么進(jìn)程可以運(yùn)行到結(jié)束,結(jié)束后刪除啟動(dòng)新的時(shí)間片。如果時(shí)間片不夠用,對(duì)不起,暫時(shí)只能完成一部分任務(wù)(變?yōu)榈却隣顟B(tài)),過(guò)后再等待 CPU ?的調(diào)度。網(wǎng)上開源的代碼太多,怎么實(shí)現(xiàn),大家可以參照加深影響。
短作業(yè)優(yōu)先調(diào)度算法
短作業(yè)優(yōu)先調(diào)度算法,從名稱可以清晰的知道「短作業(yè)」意味著執(zhí)行時(shí)間比較短,「優(yōu)先」代表執(zhí)行順序。結(jié)合就是"短者吃香"。那么多短吃香?進(jìn)程不可能都短,也有需要執(zhí)行時(shí)間比較長(zhǎng)的進(jìn)程怎么辦?一直等待,直到餓死麥?而且有些進(jìn)程比較緊急,能夠得到先執(zhí)行?這些都是此算法所出現(xiàn)的問(wèn)題,然后出現(xiàn)下面的一些算法
最短剩余時(shí)間優(yōu)先調(diào)度算法
最短剩余時(shí)間是針對(duì)最短進(jìn)程優(yōu)先增加了搶占機(jī)制的版本。在這種情況下,進(jìn)程調(diào)度總是選擇預(yù)期剩余時(shí)間最短的進(jìn)程。當(dāng)一個(gè)進(jìn)程加入到就緒隊(duì)列時(shí),他可能比當(dāng)前運(yùn)行的進(jìn)程具有更短的剩余時(shí)間,因此只要新進(jìn)程就緒,調(diào)度程序就能可能搶占當(dāng)前正在運(yùn)行的進(jìn)程。像最短進(jìn)程優(yōu)先一樣,調(diào)度程序正在執(zhí)行選擇函數(shù)是必須有關(guān)于處理時(shí)間的估計(jì),并且存在長(zhǎng)進(jìn)程饑餓的危險(xiǎn)。
高響應(yīng)比優(yōu)先調(diào)度算法
什么是高響應(yīng)比,有響應(yīng)之前應(yīng)該會(huì)有請(qǐng)求,相當(dāng)于是請(qǐng)求+響應(yīng)+優(yōu)先,算是一種綜合的調(diào)度算法。也就是它結(jié)合了短作業(yè)優(yōu)先,先來(lái)先服務(wù)以及長(zhǎng)作業(yè)的一些特性。ok,那么這三種是如何體現(xiàn)出來(lái)的
首先來(lái)說(shuō)短作業(yè)優(yōu)先。等待時(shí)間我們假設(shè)相等,服務(wù)時(shí)間很短,這樣的話短作業(yè)就會(huì)有更高的優(yōu)先權(quán)。
再來(lái)看先來(lái)先服務(wù)。假設(shè)服務(wù)時(shí)間相同,先來(lái)的自然等待時(shí)間較長(zhǎng),優(yōu)先級(jí)越高。
上面說(shuō)長(zhǎng)作業(yè)很可能因?yàn)榈却龝r(shí)間過(guò)長(zhǎng),容易餓死。在這里不會(huì),仿佛像醫(yī)生的這個(gè)職業(yè),工作越久資歷越老,優(yōu)先級(jí)越來(lái)越高,越來(lái)越吃香
優(yōu)先級(jí)調(diào)度算法
優(yōu)先級(jí)調(diào)度算法每次從后備作業(yè)隊(duì)列中選擇優(yōu)先級(jí)最髙的一個(gè)或幾個(gè)作業(yè),將它們調(diào)入內(nèi)存,分配必要的資源,創(chuàng)建進(jìn)程并放入就緒隊(duì)列。在進(jìn)程調(diào)度中,優(yōu)先級(jí)調(diào)度算法每次從就緒隊(duì)列中選擇優(yōu)先級(jí)最高的進(jìn)程,將處理機(jī)分配給它,使之投入運(yùn)行。
8、什么是死鎖?
死鎖,顧名思義就是導(dǎo)致線程卡死的鎖沖突,例如下面的這種情況
線程 1 已經(jīng)成功拿到了互斥量 1 ,正在申請(qǐng)互斥量 2 ,而同時(shí)在另一個(gè) ?CPU ? 上,線程 2 已經(jīng)拿到了互
斥量 2 ,正在申請(qǐng)互斥量 1 。彼此占有對(duì)方正在申請(qǐng)的互斥量,結(jié)局就是誰(shuí)也沒(méi)辦法拿到想要的互斥
量,于是死鎖就發(fā)生了。
稍微復(fù)雜一點(diǎn)的情況
存在多個(gè)互斥量的情況下,避免死鎖最簡(jiǎn)單的方法就是總是按照一定的先后順序申請(qǐng)這些互斥
量。還是以剛才的例子為例,如果每個(gè)線程都按照先申請(qǐng)互斥量 1 ,再申請(qǐng)互斥量 2 的順序執(zhí)行,死鎖
就不會(huì)發(fā)生。有些互斥量有明顯的層級(jí)關(guān)系,但是也有一些互斥量原本就沒(méi)有特定的層級(jí)關(guān)系,不過(guò)
沒(méi)有關(guān)系,可以人為干預(yù),讓所有的線程必須遵循同樣的順序來(lái)申請(qǐng)互斥量
9、產(chǎn)生死鎖的原因?
由于系統(tǒng)中存在一些不可剝奪資源,而當(dāng)兩個(gè)或兩個(gè)以上進(jìn)程占有自身資源,并請(qǐng)求對(duì)方資源時(shí),會(huì)導(dǎo)致每個(gè)進(jìn)程都無(wú)法向前推進(jìn),這就是死鎖。
競(jìng)爭(zhēng)資源
例如:系統(tǒng)中只有一臺(tái)打印機(jī),可供進(jìn)程 A 使用,假定 A 已占用了打印機(jī),若 B 繼續(xù)要求打印機(jī)打印將被阻塞。
系統(tǒng)中的資源可以分為兩類:
可剝奪資源:是指某進(jìn)程在獲得這類資源后,該資源可以再被其他進(jìn)程或系統(tǒng)剝奪, CPU ? 和主存均屬于可剝奪性資源;
不可剝奪資源,當(dāng)系統(tǒng)把這類資源分配給某進(jìn)程后,再不能強(qiáng)行收回,只能在進(jìn)程用完后自行釋放,如磁帶機(jī)、打印機(jī)等。
進(jìn)程推進(jìn)順序不當(dāng)
例如:進(jìn)程 A 和 進(jìn)程 B 互相等待對(duì)方的數(shù)據(jù)。
10、死鎖產(chǎn)生的必要條件?
互斥
要求各個(gè)資源互斥,如果這些資源都是可以共享的,那么多個(gè)進(jìn)程直接共享即可,不會(huì)存在等待的尷尬場(chǎng)景
非搶占
要求進(jìn)程所占有的資源使用完后主動(dòng)釋放即可,其他的進(jìn)程休想搶占這些資源。原因很簡(jiǎn)單,如果可以搶占,直接拿就好了,不會(huì)進(jìn)入尷尬的等待場(chǎng)景
要求進(jìn)程是在占有(holding)至少一個(gè)資源的前提下,請(qǐng)求(waiting)新的資源的。由于新的資源被其它進(jìn)程占有,此時(shí),發(fā)出請(qǐng)求的進(jìn)程就會(huì)帶著自己占有的資源進(jìn)入阻塞狀態(tài)。假設(shè) P1,P2 分別都需要 R1,R2 資源,如果是下面這種方式:
?P1:??????????P2:
request(R1)??request(R2)
request(R2)??request(R1)?如果 P1 請(qǐng)求到了 R1 資源之后,P2 請(qǐng)求到了 R2 資源,那么此后不管是哪個(gè)進(jìn)程再次請(qǐng)求資源,都是在占有資源的前提下請(qǐng)求的,此時(shí)就會(huì)帶著這個(gè)資源陷入阻塞狀態(tài)。P1 和 P2 需要互相等待,發(fā)生了死鎖。
換一種情況:
?P1:??????????P2:
request(R1)??request(R1)
request(R2)??request(R2)?如果 P1 請(qǐng)求到了 R1 資源,那么 P2 在請(qǐng)求 R1 的時(shí)候雖然也會(huì)阻塞,但是是在不占有資源的情況下阻塞的,不像之前那樣占有 R2。所以,此時(shí) P1 可以正常完成任務(wù)并釋放 R1,P2 拿到 R1 之后再去執(zhí)行任務(wù)。這種情況就不會(huì)發(fā)生死鎖。
循環(huán)等待
要求存在一條進(jìn)程資源的循環(huán)等待鏈,鏈中的每一個(gè)進(jìn)程占有的資源同時(shí)被另一個(gè)進(jìn)程所請(qǐng)求。
發(fā)生死鎖時(shí)一定有循環(huán)等待(因?yàn)槭撬梨i的必要條件),但是發(fā)生循環(huán)等待的時(shí)候不一定會(huì)發(fā)生死鎖。這是因?yàn)?,如果循環(huán)等待鏈中的 P1 和 鏈外的 P6 都占有某個(gè)進(jìn)程 P2 請(qǐng)求的資源,那么 P2 完全可以選擇不等待 P1 釋放該資源,而是等待 P6 釋放資源。這樣就不會(huì)發(fā)生死鎖了。
11、解決死鎖的基本方法?
如果我們已經(jīng)知道死鎖形成的必要條件,逐一攻破即可。
破壞互斥
通過(guò)與鎖完全不同的同步方式CAS,CAS提供原子性支持,實(shí)現(xiàn)各種無(wú)鎖的數(shù)據(jù)結(jié)構(gòu),不僅可以避免互斥鎖帶來(lái)的開銷也可避免死鎖問(wèn)題。
破壞不搶占
如果一個(gè)線程已經(jīng)獲取到了一些鎖,那么在這個(gè)線程釋放鎖之前這些鎖是不會(huì)被強(qiáng)制搶占的。但是為了防止死鎖的發(fā)生,我們可以選擇讓線程在獲取后續(xù)的鎖失敗時(shí)主動(dòng)放棄自己已經(jīng)持有的鎖并在之后重試整個(gè)任務(wù),這樣其他等待這些鎖的線程就可以繼續(xù)執(zhí)行了。這樣就完美了嗎?當(dāng)然不
這種方式雖然可以在一定程度上避免死鎖,但是如果多個(gè)相互存在競(jìng)爭(zhēng)的線程不斷的放棄重啟放棄循環(huán),就會(huì)出現(xiàn)活鎖的問(wèn)題,此時(shí)線程雖然沒(méi)有因?yàn)殒i沖突被卡死,但是仍然會(huì)因?yàn)樽枞麜r(shí)間太長(zhǎng)處于重試當(dāng)中。怎么辦?
方案1:給任務(wù)重試部分增加隨機(jī)延遲時(shí)間,降低任務(wù)沖突的概率
破壞環(huán)路等待
在實(shí)踐的過(guò)程中,采用破壞環(huán)路等待的方式非常常見(jiàn),這種技術(shù)叫做"鎖排序"。很好理解,我們假設(shè)現(xiàn)在有個(gè)數(shù)組A,采用單向訪問(wèn)的方式(從前往后),依次訪問(wèn)并加鎖,這樣一來(lái),線程只會(huì)向前單向等待鎖釋放,自然也就無(wú)法形成一個(gè)環(huán)路了。
說(shuō)到這里,我想說(shuō)死鎖不僅僅出現(xiàn)在多線程編程領(lǐng)域,在數(shù)據(jù)庫(kù)的訪問(wèn)也是非常的常見(jiàn),比如我們需要更新數(shù)據(jù)庫(kù)的幾行數(shù)據(jù),就得先獲取這些數(shù)據(jù)的鎖,然后通過(guò)排序的方式阻止數(shù)據(jù)層發(fā)生死鎖。
這樣就完美了?當(dāng)然沒(méi)有,那會(huì)出現(xiàn)什么問(wèn)題?
這種方案也存在它的缺點(diǎn),比如在大型系統(tǒng)當(dāng)中,不同模塊直接解耦和隔離得非常徹底,不同模塊開發(fā)人員不清楚其細(xì)節(jié),在這樣的情況下就很難做到整個(gè)系統(tǒng)層面的全局鎖排序了。在這種情況下,我們可以對(duì)方案進(jìn)行擴(kuò)充,例如Linux在內(nèi)存映射代碼就使用了一種鎖分組排序的方式來(lái)解決這個(gè)問(wèn)題。鎖分組排序首先按模塊將鎖分為了不同的組,每個(gè)組之間定義了嚴(yán)格的加鎖順序,然后再在組內(nèi)對(duì)具體的鎖按規(guī)則進(jìn)行排序,這樣就保證了全局的加鎖順序一致。在Linux的對(duì)應(yīng)的源碼頂部,我們可以看到有非常詳盡的注釋定義了明確的鎖排序規(guī)則。
這種解決方案如果規(guī)模過(guò)大的話即使可以實(shí)現(xiàn)也會(huì)非常的脆弱,只要有一個(gè)加鎖操作沒(méi)有遵守鎖排序規(guī)則就有可能會(huì)引發(fā)死鎖。不過(guò)在像微服務(wù)之類解耦比較充分的場(chǎng)景下,只要架構(gòu)拆分合理,任務(wù)模塊盡可能小且不會(huì)將加鎖范圍擴(kuò)大到模塊之外,那么鎖排序?qū)⑹且环N非常實(shí)用和便捷的死鎖阻止技術(shù)
12、怎么預(yù)防死鎖?
破壞請(qǐng)求條件:一次性分配所有資源,這樣就不會(huì)再有請(qǐng)求了;
破壞請(qǐng)保持條件:只要有一個(gè)資源得不到分配,也不給這個(gè)進(jìn)程分配其他的資源:
破壞不可剝奪條件:當(dāng)某進(jìn)程獲得了部分資源,但得不到其它資源,則釋放已占有的資源;
破壞環(huán)路等待條件:系統(tǒng)給每類資源賦予一個(gè)編號(hào),每一個(gè)進(jìn)程按編號(hào)遞增的順序請(qǐng)求資源,釋放則相反。
13、怎么避免死鎖?
銀行家算法
當(dāng)進(jìn)程首次申請(qǐng)資源時(shí),要測(cè)試該進(jìn)程對(duì)資源的最大需求量,如果系統(tǒng)現(xiàn)存的資源可以滿足它的最大需求量則按當(dāng)前的申請(qǐng)量分配資源,否則就推遲分配。
當(dāng)進(jìn)程在執(zhí)行中繼續(xù)申請(qǐng)資源時(shí),先測(cè)試該進(jìn)程已占用的資源數(shù)與本次申請(qǐng)資源數(shù)之和是否超過(guò)了該進(jìn)程對(duì)資源的最大需求量。若超過(guò)則拒絕分配資源。若沒(méi)超過(guò)則再測(cè)試系統(tǒng)現(xiàn)存的資源能否滿足該進(jìn)程尚需的最大資源量,若滿足則按當(dāng)前的申請(qǐng)量分配資源,否則也要推遲分配。
安全序列
是指系統(tǒng)能按某種進(jìn)程推進(jìn)順序(P1, P2, P3, …, Pn),為每個(gè)進(jìn)程 Pi 分配其所需要的資源,直至滿足每個(gè)進(jìn)程對(duì)資源的最大需求,使每個(gè)進(jìn)程都可以順序地完成。這種推進(jìn)順序就叫安全序列【銀行家算法的核心就是找到一個(gè)安全序列】。
系統(tǒng)安全狀態(tài)
如果系統(tǒng)能找到一個(gè)安全序列,就稱系統(tǒng)處于安全狀態(tài),否則,就稱系統(tǒng)處于不安全狀態(tài)。
14、怎么解除死鎖?
資源剝奪:掛起某些死鎖進(jìn)程,并搶占它的資源,將這些資源分配給其他死鎖進(jìn)程(但應(yīng)該防止被掛起的進(jìn)程長(zhǎng)時(shí)間得不到資源);
撤銷進(jìn)程:強(qiáng)制撤銷部分、甚至全部死鎖進(jìn)程并剝奪這些進(jìn)程的資源(撤銷的原則可以按進(jìn)程優(yōu)先級(jí)和撤銷進(jìn)程代價(jià)的高低進(jìn)行);
進(jìn)程回退:讓一個(gè)或多個(gè)進(jìn)程回退到足以避免死鎖的地步。進(jìn)程回退時(shí)自愿釋放資源而不是被剝奪。要求系統(tǒng)保持進(jìn)程的歷史信息,設(shè)置還原點(diǎn)。
15、什么是緩沖區(qū)溢出?有什么危害?
官話
緩沖區(qū)溢出是指當(dāng)計(jì)算機(jī)向緩沖區(qū)內(nèi)填充數(shù)據(jù)時(shí)超過(guò)了緩沖區(qū)本身的容量,溢出的數(shù)據(jù)覆蓋在合法數(shù)據(jù)上
舉個(gè)例子
一個(gè)兩升的杯子,你如果想導(dǎo)入三升,怎么做?其他一生只好流出去,不是打濕了電腦就是那啥。
16、物理地址、邏輯地址、線性地址
物理地址:它是地址轉(zhuǎn)換的最終地址,是內(nèi)存單元真正的地址。如果采用了分頁(yè)機(jī)制,那么線性地址會(huì)通過(guò)頁(yè)目錄和頁(yè)表得方式轉(zhuǎn)換為物理地址。如果沒(méi)有啟用則線性地址即為物理地址
邏輯地址:在編寫c語(yǔ)言的時(shí)候,通過(guò)&操作符可以讀取指針變量本省得值,這個(gè)值就是邏輯地址。實(shí)際上是當(dāng)前進(jìn)程得數(shù)據(jù)段得地址,和真實(shí)的物理地址沒(méi)有關(guān)系。只有當(dāng)在Intel實(shí)模式下,邏輯地址==物理地址。我們平時(shí)的應(yīng)用程序都是通過(guò)和邏輯地址打交道,至于分頁(yè),分段機(jī)制對(duì)他們而言是透明得。邏輯地址也稱作虛擬地址
線性地址:線性地址是邏輯地址到物理地址的中間層。我們編寫的代碼會(huì)存在一個(gè)邏輯地址或者是段中得偏移地址,通過(guò)相應(yīng)的計(jì)算(加上基地址)生成線性地址。此時(shí)如果采用了分頁(yè)機(jī)制,那么吸納行地址再經(jīng)過(guò)變換即產(chǎn)生物理地址。在Intelk 80386中地址空間容量為4G,各個(gè)進(jìn)程地址空間隔離,意味著每個(gè)進(jìn)程獨(dú)享4G線性空間。多個(gè)進(jìn)程難免出現(xiàn)進(jìn)程之間的切換,線性空間隨之切換?;诜猪?yè)機(jī)制,對(duì)于4GB的線性地址一部分會(huì)被映射到物理內(nèi)存,一部分映射到磁盤作為交換文件,一部分沒(méi)有映射,通過(guò)下面加深一下印象
17、分頁(yè)與分段的區(qū)別?
我們知道計(jì)算機(jī)的五大組成部分分別為運(yùn)算器,存儲(chǔ)器,存儲(chǔ)器 ,輸入和輸出設(shè)備。我們的數(shù)據(jù)或者指定都需要存放內(nèi)存然后給 CPU ?大哥拿去執(zhí)行。我們平時(shí)寫的代碼不是直接操作的物理地址,我們所看到的地址實(shí)際上叫做虛擬地址,通過(guò)相應(yīng)的轉(zhuǎn)換規(guī)則將虛擬地址轉(zhuǎn)換為物理地址。
那么虛擬地址是怎么轉(zhuǎn)換為物理地址的呢?
第一種方式,采用一個(gè)映射表代表虛擬地址到物理地址的映射,在計(jì)算機(jī)中我們叫做頁(yè)表。頁(yè)表將內(nèi)存地址分為頁(yè)號(hào)和偏移量,舉個(gè)例子
我們將高位部分稱為內(nèi)存地址的頁(yè)號(hào),后面的低位叫做內(nèi)存地址的偏移量。我們只需要保存虛擬地址內(nèi)存的頁(yè)號(hào)和物理內(nèi)存頁(yè)號(hào)之間的映射關(guān)系即可。
這樣說(shuō)了,也就是三部曲
虛擬地址-----> 頁(yè)號(hào)+偏移量
通過(guò)頁(yè)表查詢出虛擬頁(yè)號(hào),對(duì)應(yīng)的物理頁(yè)號(hào)
物理頁(yè)號(hào)+偏移量-----> 物理內(nèi)存地址
這樣的方法,在32位的內(nèi)存地址,頁(yè)表需要多大的空間?
在一個(gè)32位的內(nèi)存地址空間,頁(yè)表需要記錄2^20個(gè)物理頁(yè)面的映射關(guān)系,可以想象為要給數(shù)組。那么一個(gè)頁(yè)號(hào)是完整的4字節(jié)。這樣一個(gè)頁(yè)表就是4MB。
再來(lái),我們知道進(jìn)程有各自的虛擬內(nèi)存空間,也就是說(shuō)每個(gè)進(jìn)程都需要一個(gè)這樣的頁(yè)表,不管此進(jìn)程是只有幾KB的程序還是需要GB的內(nèi)存空間都需要這樣的頁(yè)表,用這樣的結(jié)構(gòu)保存頁(yè)面,內(nèi)存的占用將非常的大,那其他方式是怎么樣的呢
多級(jí)頁(yè)表
同樣的虛擬內(nèi)存地址,偏移量部分和上面方式一樣,但是我們將頁(yè)號(hào)部分拆分為四段,從高到低分成4級(jí)到1級(jí)的4個(gè)頁(yè)表索引
這樣一來(lái),每個(gè)進(jìn)程將有4級(jí)頁(yè)表。通過(guò)4級(jí)頁(yè)表的索引找到對(duì)應(yīng)的條目。通過(guò)這個(gè)條目找到3級(jí)頁(yè)表所在位置,4級(jí)的每一個(gè)條目可能有多個(gè)3級(jí)的條目,找到了3級(jí)的條目后找到對(duì)應(yīng)3級(jí)索引的條目,就這樣到達(dá)1級(jí)頁(yè)表。1級(jí)對(duì)應(yīng)的則為物理頁(yè)號(hào)。最終通過(guò)物理頁(yè)號(hào)+偏移量的方式獲取物理內(nèi)存地址。
18 為什么使用多級(jí)頁(yè)表
使用多級(jí)頁(yè)表可以讓頁(yè)表在內(nèi)存中離散存儲(chǔ)。多級(jí)頁(yè)表通過(guò)索引就可以定位到具體的項(xiàng)。舉個(gè)例子,假設(shè)當(dāng)前虛擬地址空間為4G,每個(gè)頁(yè)的大小為4k,如果是一級(jí)頁(yè)表的話,共有2……20個(gè)頁(yè)表項(xiàng),假設(shè)每個(gè)頁(yè)表項(xiàng)需要4B,那么存放所有的頁(yè)表項(xiàng)需要4M,那么為了隨機(jī)訪問(wèn),我們就需要連續(xù)的4M內(nèi)存空間存放所有的頁(yè)表項(xiàng)。這樣一來(lái),隨著虛擬地址空間的增大,需要存放頁(yè)表所需的連續(xù)空間也就越來(lái)多大。如果使用多級(jí)頁(yè)表,我們只需要一頁(yè)存放目錄項(xiàng),頁(yè)表存放在內(nèi)存其他位置即可,下面有例子進(jìn)一步講解
使用多級(jí)頁(yè)表更加節(jié)省頁(yè)表內(nèi)存。理論上,使用一級(jí)頁(yè)表,需要連續(xù)存儲(chǔ)空間存放所有項(xiàng)。使用多級(jí)頁(yè)表只需要給實(shí)際使用的的那些虛擬地址內(nèi)存的請(qǐng)求分配內(nèi)存
舉個(gè)例子
假設(shè)虛擬地址空間為4G,A進(jìn)程只是用 4M 的內(nèi)存空間。對(duì)于一級(jí)頁(yè)表,我們需要 4M 空間存放這4GB 虛擬地址對(duì)應(yīng)的頁(yè)表,然后找到進(jìn)程真正的 4M 內(nèi)存空間。這樣的話,A進(jìn)程本來(lái)只使用 4MB 內(nèi)存空間,但是為了訪問(wèn)它,我們需要為所有的虛擬地址空間建立頁(yè)表,豈不是很浪費(fèi)。對(duì)于二級(jí)頁(yè)表而言,使用一個(gè)頁(yè)目錄就可定位 4M 的內(nèi)存,存放一個(gè)頁(yè)目錄項(xiàng)需要 4k,還需要一頁(yè)存放進(jìn)程使用的 4M,4M=1024*4k,也就相當(dāng)于 1024 個(gè)頁(yè)表項(xiàng)就可以映射4M的內(nèi)存空間,那么總共就只需要4k(頁(yè)表)+4k(頁(yè)目錄)=8k來(lái)存放進(jìn)程需要的 4M 內(nèi)存空間對(duì)應(yīng)頁(yè)表和頁(yè)目錄項(xiàng)。這樣看來(lái)確實(shí)剩下不少的內(nèi)存。
那使用多級(jí)頁(yè)表有啥缺點(diǎn)?
還是有的,咋們使用一級(jí)頁(yè)表的時(shí)候,只需要訪問(wèn)兩次內(nèi)存,一次是訪問(wèn)頁(yè)表項(xiàng),一次是訪問(wèn)需要讀取的一頁(yè)數(shù)據(jù)。如果是二級(jí)頁(yè)表,就需要訪問(wèn)三次,第一次訪問(wèn)頁(yè)目錄,第二次訪問(wèn)頁(yè)表項(xiàng),第三次訪問(wèn)讀取的數(shù)據(jù)。訪問(wèn)次數(shù)的增加以為訪問(wèn)數(shù)據(jù)所花費(fèi)的總時(shí)間也增加
19、頁(yè)面置換算法有哪些?
請(qǐng)求調(diào)頁(yè),也稱按需調(diào)頁(yè),即對(duì)不在內(nèi)存中的“頁(yè)”,當(dāng)進(jìn)程執(zhí)行時(shí)要用時(shí)才調(diào)入,否則有可能到程序結(jié)束時(shí)也不會(huì)調(diào)入。而內(nèi)存中給頁(yè)面留的位置是有限的,在內(nèi)存中以幀為單位放置頁(yè)面。為了防止請(qǐng)求調(diào)頁(yè)的過(guò)程出現(xiàn)過(guò)多的內(nèi)存頁(yè)面錯(cuò)誤(即需要的頁(yè)面當(dāng)前不在內(nèi)存中,需要從硬盤中讀數(shù)據(jù),也即需要做頁(yè)面的替換)而使得程序執(zhí)行效率下降,我們需要設(shè)計(jì)一些頁(yè)面置換算法,頁(yè)面按照這些算法進(jìn)行相互替換時(shí),可以盡量達(dá)到較低的錯(cuò)誤率。常用的頁(yè)面置換算法如下:
先進(jìn)先出置換算法(FIFO)
先進(jìn)先出,即淘汰最早調(diào)入的頁(yè)面。
最佳置換算法(OPT)
選未來(lái)最遠(yuǎn)將使用的頁(yè)淘汰,是一種最優(yōu)的方案,可以證明缺頁(yè)數(shù)最小。
最近最久未使用(LRU)算法
即選擇最近最久未使用的頁(yè)面予以淘汰
時(shí)鐘(Clock)置換算法
時(shí)鐘置換算法也叫最近未用算法 NRU(Not RecentlyUsed)。該算法為每個(gè)頁(yè)面設(shè)置一位訪問(wèn)位,將內(nèi)存中的所有頁(yè)面都通過(guò)鏈接指針鏈成一個(gè)循環(huán)隊(duì)列。。
20 書籍/視頻學(xué)習(xí)推薦
書籍
Linux內(nèi)核設(shè)計(jì)與實(shí)現(xiàn)
操作系統(tǒng)導(dǎo)論
現(xiàn)代操作系統(tǒng)
深入理解操作系統(tǒng)
視頻
B站 ----哈工大李志軍老師講解
https://www.bilibili.com/video/av17036347/
往期推薦
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!