1. 進程和線程的區別?
- 調度:進程是資源管理的基本單位,線程是程序執行的基本單位。
- 切換:線程上下文切換比進程上下文切換要快得多。
- 擁有資源: 進程是擁有資源的一個獨立單位,線程不擁有系統資源,但是可以訪問隸屬于進程的資源。
- 系統開銷: 創建或撤銷進程時,系統都要為之分配或回收系統資源,如內存空間,I/O設備等,OS所付出的開銷顯著大于在創建或撤銷線程時的開銷,進程切換的開銷也遠大于線程切換的開銷。
2. 協程與線程的區別?
- 線程和進程都是同步機制,而協程是異步機制。
- 線程是搶占式,而協程是非搶占式的。需要用戶釋放使用權切換到其他協程,因此同一時間其實只有一個協程擁有運行權,相當于單線程的能力。
- 一個線程可以有多個協程,一個進程也可以有多個協程。
- 協程不被操作系統內核管理,而完全是由程序控制。線程是被分割的CPU資源,協程是組織好的代碼流程,線程是協程的資源。但協程不會直接使用線程,協程直接利用的是執行器關聯任意線程或線程池。
- 協程能保留上一次調用時的狀態。
3. 并發和并行有什么區別?
并發就是在一段時間內,多個任務都會被處理;但在某一時刻,只有一個任務在執行。單核處理器可以做到并發。比如有兩個進程A
和B
,A
運行一個時間片之后,切換到B
,B
運行一個時間片之后又切換到A
。因為切換速度足夠快,所以宏觀上表現為在一段時間內能同時運行多個程序。
并行就是在同一時刻,有多個任務在執行。這個需要多核處理器才能完成,在微觀上就能同時執行多條指令,不同的程序被放到不同的處理器上運行,這個是物理上的多個進程同時進行。
4. 進程與線程的切換流程?
進程切換分兩步:
1、切換頁表以使用新的地址空間,一旦去切換上下文,處理器中所有已經緩存的內存地址一瞬間都作廢了。
2、切換內核棧和硬件上下文。
對于linux來說,線程和進程的最大區別就在于地址空間,對于線程切換,第1步是不需要做的,第2步是進程和線程切換都要做的。
因為每個進程都有自己的虛擬地址空間,而線程是共享所在進程的虛擬地址空間的,因此同一個進程中的線程進行線程切換時不涉及虛擬地址空間的轉換。
5. 為什么虛擬地址空間切換會比較耗時?
進程都有自己的虛擬地址空間,把虛擬地址轉換為物理地址需要查找頁表,頁表查找是一個很慢的過程,因此通常使用Cache來緩存常用的地址映射,這樣可以加速頁表查找,這個Cache就是TLB(translation Lookaside Buffer,TLB本質上就是一個Cache,是用來加速頁表查找的)。
由于每個進程都有自己的虛擬地址空間,那么顯然每個進程都有自己的頁表,那么當進程切換后頁表也要進行切換,頁表切換后TLB就失效了,Cache失效導致命中率降低,那么虛擬地址轉換為物理地址就會變慢,表現出來的就是程序運行會變慢,而線程切換則不會導致TLB失效,因為線程無需切換地址空間,因此我們通常說線程切換要比較進程切換塊,原因就在這里。
6. 進程間通信方式有哪些?
-
管道:管道這種通訊方式有兩種限制,一是半雙工的通信,數據只能單向流動,二是只能在具有親緣關系的進程間使用。進程的親緣關系通常是指父子進程關系。
管道可以分為兩類:匿名管道和命名管道。匿名管道是單向的,只能在有親緣關系的進程間通信;命名管道以磁盤文件的方式存在,可以實現本機任意兩個進程通信。
-
信號 : 信號是一種比較復雜的通信方式,信號可以在任何時候發給某一進程,而無需知道該進程的狀態。
Linux系統中常用信號:
(1)SIGHUP:用戶從終端注銷,所有已啟動進程都將收到該進程。系統缺省狀態下對該信號的處理是終止進程。(2)SIGINT:程序終止信號。程序運行過程中,按
Ctrl+C
鍵將產生該信號。(3)SIGQUIT:程序退出信號。程序運行過程中,按
Ctrl+\\
鍵將產生該信號。(4)SIGBUS和SIGSEGV:進程訪問非法地址。
(5)SIGFPE:運算中出現致命錯誤,如除零操作、數據溢出等。
(6)SIGKILL:用戶終止進程執行信號。shell下執行
kill -9
發送該信號。(7)SIGTERM:結束進程信號。shell下執行
kill 進程pid
發送該信號。(8)SIGALRM:定時器信號。
(9)SIGCLD:子進程退出信號。如果其父進程沒有忽略該信號也沒有處理該信號,則子進程退出后將形成僵尸進程。
-
信號量:信號量是一個計數器,可以用來控制多個進程對共享資源的訪問。它常作為一種鎖機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。因此,主要作為進程間以及同一進程內不同線程之間的同步手段。
-
消息隊列:消息隊列是消息的鏈接表,包括Posix消息隊列和System V消息隊列。有足夠權限的進程可以向隊列中添加消息,被賦予讀權限的進程則可以讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及緩沖區大小受限等缺點。
-
共享內存:共享內存就是映射一段能被其他進程所訪問的內存,這段共享內存由一個進程創建,但多個進程都可以訪問。共享內存是最快的 IPC 方式,它是針對其他進程間通信方式運行效率低而專門設計的。它往往與其他通信機制,如信號量,配合使用,來實現進程間的同步和通信。
-
Socket:與其他通信機制不同的是,它可用于不同機器間的進程通信。
優缺點:
-
管道:速度慢,容量有限;
-
Socket:任何進程間都能通訊,但速度慢;
-
消息隊列:容量受到系統限制,且要注意第一次讀的時候,要考慮上一次沒有讀完數據的問題;
-
信號量:不能傳遞復雜消息,只能用來同步;
-
共享內存區:能夠很容易控制容量,速度快,但要保持同步,比如一個進程在寫的時候,另一個進程要注意讀寫的問題,相當于線程中的線程安全,當然,共享內存區同樣可以用作線程間通訊,不過沒這個必要,線程間本來就已經共享了同一進程內的一塊內存。
7. 進程間同步的方式有哪些?
1、臨界區:通過對多線程的串行化來訪問公共資源或一段代碼,速度快,適合控制數據訪問。
優點:保證在某一時刻只有一個線程能訪問數據的簡便辦法。
缺點:雖然臨界區同步速度很快,但卻只能用來同步本進程內的線程,而不可用來同步多個進程中的線程。
2、互斥量:為協調共同對一個共享資源的單獨訪問而設計的。互斥量跟臨界區很相似,比臨界區復雜,互斥對象只有一個,只有擁有互斥對象的線程才具有訪問資源的權限。
優點:使用互斥不僅僅能夠在同一應用程序不同線程中實現資源的安全共享,而且可以在不同應用程序的線程之間實現對資源的安全共享。
缺點:
-
互斥量是可以命名的,也就是說它可以跨越進程使用,所以創建互斥量需要的資源更多,所以如果只為了在進程內部是用的話使用臨界區會帶來速度上的優勢并能夠減少資源占用量。
-
通過互斥量可以指定資源被獨占的方式使用,但如果有下面一種情況通過互斥量就無法處理,比如現在一位用戶購買了一份三個并發訪問許可的數據庫系統,可以根據用戶購買的訪問許可數量來決定有多少個線程/進程能同時進行數據庫操作,這時候如果利用互斥量就沒有辦法完成這個要求,信號量對象可以說是一種資源計數器。
3、信號量:為控制一個具有有限數量用戶資源而設計。它允許多個線程在同一時刻訪問同一資源,但是需要限制在同一時刻訪問此資源的最大線程數目。互斥量是信號量的一種特殊情況,當信號量的最大資源數=1就是互斥量了。
優點:適用于對Socket(套接字)程序中線程的同步。
缺點:
-
信號量機制必須有公共內存,不能用于分布式操作系統,這是它最大的弱點;
-
信號量機制功能強大,但使用時對信號量的操作分散, 而且難以控制,讀寫和維護都很困難,加重了程序員的編碼負擔;
-
核心操作P-V分散在各用戶程序的代碼中,不易控制和管理,一旦錯誤,后果嚴重,且不易發現和糾正。
4、事件: 用來通知線程有一些事件已發生,從而啟動后繼任務的開始。
優點:事件對象通過通知操作的方式來保持線程的同步,并且可以實現不同進程中的線程同步操作。
8. 線程同步的方式有哪些?
1、臨界區:當多個線程訪問一個獨占性共享資源時,可以使用臨界區對象。擁有臨界區的線程可以訪問被保護起來的資源或代碼段,其他線程若想訪問,則被掛起,直到擁有臨界區的線程放棄臨界區為止,以此達到用原子方式操 作共享資源的目的。
2、事件:事件機制,則允許一個線程在處理完一個任務后,主動喚醒另外一個線程執行任務。
3、互斥量:互斥對象和臨界區對象非常相似,只是其允許在進程間使用,而臨界區只限制與同一進程的各個線程之間使用,但是更節省資源,更有效率。
4、信號量:當需要一個計數器來限制可以使用某共享資源的線程數目時,可以使用“信號量”對象。
區別:
-
互斥量與臨界區的作用非常相似,但互斥量是可以命名的,也就是說互斥量可以跨越進程使用,但創建互斥量需要的資源更多,所以如果只為了在進程內部是用的話使用臨界區會帶來速度上的優勢并能夠減少資源占用量 。因為互斥量是跨進程的互斥量一旦被創建,就可以通過名字打開它。
-
互斥量,信號量,事件都可以被跨越進程使用來進行同步數據操作。
9. 線程的分類?
從線程的運行空間來說,分為用戶級線程(user-level thread, ULT)和內核級線程(kernel-level, KLT)
內核級線程:這類線程依賴于內核,又稱為內核支持的線程或輕量級進程。無論是在用戶程序中的線程還是系統進程中的線程,它們的創建、撤銷和切換都由內核實現。比如英特爾i5-8250U是4核8線程,這里的線程就是內核級線程
用戶級線程:它僅存在于用戶級中,這種線程是不依賴于操作系統核心的。應用進程利用線程庫來完成其創建和管理,速度比較快,操作系統內核無法感知用戶級線程的存在。
10. 什么是臨界區,如何解決沖突?
每個進程中訪問臨界資源的那段程序稱為臨界區,一次僅允許一個進程使用的資源稱為臨界資源。
解決沖突的辦法:
- 如果有若干進程要求進入空閑的臨界區,一次僅允許一個進程進入,如已有進程進入自己的臨界區,則其它所有試圖進入臨界區的進程必須等待;
- 進入臨界區的進程要在有限時間內退出。
- 如果進程不能進入自己的臨界區,則應讓出CPU,避免進程出現“忙等”現象。
11. 什么是死鎖?死鎖產生的條件?
什么是死鎖:
在兩個或者多個并發進程中,如果每個進程持有某種資源而又等待其它進程釋放它或它們現在保持著的資源,在未改變這種狀態之前都不能向前推進,稱這一組進程產生了死鎖。通俗的講就是兩個或多個進程無限期的阻塞、相互等待的一種狀態。
死鎖產生的四個必要條件:(有一個條件不成立,則不會產生死鎖)
- 互斥條件:一個資源一次只能被一個進程使用
- 請求與保持條件:一個進程因請求資源而阻塞時,對已獲得資源保持不放
- 不剝奪條件:進程獲得的資源,在未完全使用完之前,不能強行剝奪
- 循環等待條件:若干進程之間形成一種頭尾相接的環形等待資源關系
如何處理死鎖問題
常用的處理死鎖的方法有:死鎖預防、死鎖避免、死鎖檢測、死鎖解除、鴕鳥策略。
**(1)死鎖的預防:**基本思想就是確保死鎖發生的四個必要條件中至少有一個不成立:
- ① 破除資源互斥條件
- ② 破除“請求與保持”條件:實行資源預分配策略,進程在運行之前,必須一次性獲取所有的資源。缺點:在很多情況下,無法預知進程執行前所需的全部資源,因為進程是動態執行的,同時也會降低資源利用率,導致降低了進程的并發性。
- ③ 破除“不可剝奪”條件:允許進程強行從占有者那里奪取某些資源。當一個已經保持了某些不可被搶占資源的進程,提出新的資源請求而不能得到滿足時,它必須釋放已經保持的所有資源,待以后需要時再重新申請。這意味著進程已經占有的資源會被暫時被釋放,或者說被搶占了。
- ④ 破除“循環等待”條件:實行資源有序分配策略,對所有資源排序編號,按照順序獲取資源,將緊缺的,稀少的采用較大的編號,在申請資源時必須按照編號的順序進行,一個進程只有獲得較小編號的進程才能申請較大編號的進程。
(2)死鎖避免:
死鎖預防通過約束資源請求,防止4個必要條件中至少一個的發生,可以通過直接或間接預防方法,但是都會導致低效的資源使用和低效的進程執行。而死鎖避免則允許前三個必要條件,但是通過動態地檢測資源分配狀態,以確保循環等待條件不成立,從而確保系統處于安全狀態。所謂安全狀態是指:如果系統能按某個順序為每個進程分配資源(不超過其最大值),那么系統狀態是安全的,換句話說就是,如果存在一個安全序列,那么系統處于安全狀態。銀行家算法是經典的死鎖避免的算法。
(3)死鎖檢測:
死鎖預防策略是非常保守的,他們通過限制訪問資源和在進程上強加約束來解決死鎖的問題。死鎖檢測則是完全相反,它不限制資源訪問或約束進程行為,只要有可能,被請求的資源就被授權給進程。但是操作系統會周期性地執行一個算法檢測前面的循環等待的條件。死鎖檢測算法是通過資源分配圖來檢測是否存在環來實現,從一個節點出發進行深度優先搜索,對訪問過的節點進行標記,如果訪問了已經標記的節點,就表示有存在環,也就是檢測到死鎖的發生。
- (1)如果進程-資源分配圖中無環路,此時系統沒有死鎖。
- (2)如果進程-資源分配圖中有環路,且每個資源類中只有一個資源,則系統發生死鎖。
- (3)如果進程-資源分配圖中有環路,且所涉及的資源類有多個資源,則不一定會發生死鎖。
(4)死鎖解除:
死鎖解除的常用方法就是終止進程和資源搶占,回滾。所謂進程終止就是簡單地終止一個或多個進程以打破循環等待,包括兩種方式:終止所有死鎖進程和一次只終止一個進程直到取消死鎖循環為止;所謂資源搶占就是從一個或者多個死鎖進程那里搶占一個或多個資源。
(5)鴕鳥策略:
把頭埋在沙子里,假裝根本沒發生問題。因為解決死鎖問題的代價很高,因此鴕鳥策略這種不采取任何措施的方案會獲得更高的性能。當發生死鎖時不會對用戶造成多大影響,或發生死鎖的概率很低,可以采用鴕鳥策略。大多數操作系統,包括 Unix,Linux 和 Windows,處理死鎖問題的辦法僅僅是忽略它。
12. 進程調度策略有哪幾種?
-
先來先服務:非搶占式的調度算法,按照請求的順序進行調度。有利于長作業,但不利于短作業,因為短作業必須一直等待前面的長作業執行完畢才能執行,而長作業又需要執行很長時間,造成了短作業等待時間過長。另外,對
I/O
密集型進程也不利,因為這種進程每次進行I/O
操作之后又得重新排隊。 -
短作業優先:非搶占式的調度算法,按估計運行時間最短的順序進行調度。長作業有可能會餓死,處于一直等待短作業執行完畢的狀態。因為如果一直有短作業到來,那么長作業永遠得不到調度。
-
最短剩余時間優先:最短作業優先的搶占式版本,按剩余運行時間的順序進行調度。 當一個新的作業到達時,其整個運行時間與當前進程的剩余時間作比較。如果新的進程需要的時間更少,則掛起當前進程,運行新的進程。否則新的進程等待。
-
時間片輪轉:將所有就緒進程按
FCFS
的原則排成一個隊列,每次調度時,把CPU
時間分配給隊首進程,該進程可以執行一個時間片。當時間片用完時,由計時器發出時鐘中斷,調度程序便停止該進程的執行,并將它送往就緒隊列的末尾,同時繼續把CPU
時間分配給隊首的進程。時間片輪轉算法的效率和時間片的大小有很大關系:因為進程切換都要保存進程的信息并且載入新進程的信息,如果時間片太小,會導致進程切換得太頻繁,在進程切換上就會花過多時間。 而如果時間片過長,那么實時性就不能得到保證。
-
優先級調度:為每個進程分配一個優先級,按優先級進行調度。為了防止低優先級的進程永遠等不到調度,可以隨著時間的推移增加等待進程的優先級。
13. 進程有哪些狀態?
進程一共有5
種狀態,分別是創建、就緒、運行(執行)、終止、阻塞。
- 運行狀態就是進程正在
CPU
上運行。在單處理機環境下,每一時刻最多只有一個進程處于運行狀態。 - 就緒狀態就是說進程已處于準備運行的狀態,即進程獲得了除
CPU
之外的一切所需資源,一旦得到CPU
即可運行。 - 阻塞狀態就是進程正在等待某一事件而暫停運行,比如等待某資源為可用或等待
I/O
完成。即使CPU
空閑,該進程也不能運行。
運行態→阻塞態:往往是由于等待外設,等待主存等資源分配或等待人工干預而引起的。
阻塞態→就緒態:則是等待的條件已滿足,只需分配到處理器后就能運行。
運行態→就緒態:不是由于自身原因,而是由外界原因使運行狀態的進程讓出處理器,這時候就變成就緒態。例如時間片用完,或有更高優先級的進程來搶占處理器等。
就緒態→運行態:系統按某種策略選中就緒隊列中的一個進程占用處理器,此時就變成了運行態。
14. 什么是分頁?
把內存空間劃分為大小相等且固定的塊,作為主存的基本單位。因為程序數據存儲在不同的頁面中,而頁面又離散的分布在內存中,因此需要一個頁表來記錄映射關系,以實現從頁號到物理塊號的映射。
訪問分頁系統中內存數據需要兩次的內存訪問 (一次是從內存中訪問頁表,從中找到指定的物理塊號,加上頁內偏移得到實際物理地址;第二次就是根據第一次得到的物理地址訪問內存取出數據)。
15. 什么是分段?
分頁是為了提高內存利用率,而分段是為了滿足程序員在編寫代碼的時候的一些邏輯需求(比如數據共享,數據保護,動態鏈接等)。
分段內存管理當中,地址是二維的,一維是段號,二維是段內地址;其中每個段的長度是不一樣的,而且每個段內部都是從0開始編址的。由于分段管理中,每個段內部是連續內存分配,但是段和段之間是離散分配的,因此也存在一個邏輯地址到物理地址的映射關系,相應的就是段表機制。
16. 分頁和分段有什區別?
- 分頁對程序員是透明的,但是分段需要程序員顯式劃分每個段。
- 分頁的地址空間是一維地址空間,分段是二維的。
- 頁的大小不可變,段的大小可以動態改變。
- 分頁主要用于實現虛擬內存,從而獲得更大的地址空間;分段主要是為了使程序和數據可以被劃分為邏輯上獨立的地址空間并且有助于共享和保護。
17. 什么是交換空間?
操作系統把物理內存(physical RAM)分成一塊一塊的小內存,每一塊內存被稱為頁(page)。當內存資源不足時,Linux把某些頁的內容轉移至硬盤上的一塊空間上,以釋放內存空間。硬盤上的那塊空間叫做交換空間(swap space),而這一過程被稱為交換(swapping)。物理內存和交換空間的總容量就是虛擬內存的可用容量。
用途:
- 物理內存不足時一些不常用的頁可以被交換出去,騰給系統。
- 程序啟動時很多內存頁被用來初始化,之后便不再需要,可以交換出去。
18. 物理地址、邏輯地址、有效地址、線性地址、虛擬地址的區別?
物理地址就是內存中真正的地址,它就相當于是你家的門牌號,你家就肯定有這個門牌號,具有唯一性。不管哪種地址,最終都會映射為物理地址。
在實模式
下,段基址 + 段內偏移經過地址加法器的處理,經過地址總線傳輸,最終也會轉換為物理地址
。
但是在保護模式
下,段基址 + 段內偏移被稱為線性地址
,不過此時的段基址不能稱為真正的地址,而是會被稱作為一個選擇子
的東西,選擇子就是個索引,相當于數組的下標,通過這個索引能夠在 GDT 中找到相應的段描述符,段描述符記錄了段的起始、段的大小等信息,這樣便得到了基地址。如果此時沒有開啟內存分頁功能,那么這個線性地址可以直接當做物理地址來使用,直接訪問內存。如果開啟了分頁功能,那么這個線性地址又多了一個名字,這個名字就是虛擬地址
。
不論在實模式還是保護模式下,段內偏移地址都叫做有效地址
。有效抵制也是邏輯地址。
線性地址可以看作是虛擬地址
,虛擬地址不是真正的物理地址,但是虛擬地址會最終被映射為物理地址。下面是虛擬地址 -> 物理地址的映射。
19. 頁面替換算法有哪些?
在程序運行過程中,如果要訪問的頁面不在內存中,就發生缺頁中斷從而將該頁調入內存中。此時如果內存已無空閑空間,系統必須從內存中調出一個頁面到磁盤對換區中來騰出空間。
最優算法
在當前頁面中置換最后要訪問的頁面。不幸的是,沒有辦法來判定哪個頁面是最后一個要訪問的,因此實際上該算法不能使用
。然而,它可以作為衡量其他算法的標準。NRU
算法根據 R 位和 M 位的狀態將頁面分為四類。從編號最小的類別中隨機選擇一個頁面。NRU 算法易于實現,但是性能不是很好。存在更好的算法。FIFO
會跟蹤頁面加載進入內存中的順序,并把頁面放入一個鏈表中。有可能刪除存在時間最長但是還在使用的頁面,因此這個算法也不是一個很好的選擇。第二次機會
算法是對 FIFO 的一個修改,它會在刪除頁面之前檢查這個頁面是否仍在使用。如果頁面正在使用,就會進行保留。這個改進大大提高了性能。時鐘
算法是第二次機會算法的另外一種實現形式,時鐘算法和第二次算法的性能差不多,但是會花費更少的時間來執行算法。LRU
算法是一個非常優秀的算法,但是沒有特殊的硬件(TLB)
很難實現。如果沒有硬件,就不能使用 LRU 算法。NFU
算法是一種近似于 LRU 的算法,它的性能不是非常好。老化
算法是一種更接近 LRU 算法的實現,并且可以更好的實現,因此是一個很好的選擇- 最后兩種算法都使用了工作集算法。工作集算法提供了合理的性能開銷,但是它的實現比較復雜。
WSClock
是另外一種變體,它不僅能夠提供良好的性能,而且可以高效地實現。
最好的算法是老化算法和WSClock算法。他們分別是基于 LRU 和工作集算法。他們都具有良好的性能并且能夠被有效的實現。還存在其他一些好的算法,但實際上這兩個可能是最重要的。
20. 什么是緩沖區溢出?有什么危害?
緩沖區溢出是指當計算機向緩沖區填充數據時超出了緩沖區本身的容量,溢出的數據覆蓋在合法數據上。
危害有以下兩點:
- 程序崩潰,導致拒絕額服務
- 跳轉并且執行一段惡意代碼
造成緩沖區溢出的主要原因是程序中沒有仔細檢查用戶輸入。
21. 什么是虛擬內存?
虛擬內存就是說,讓物理內存擴充成更大的邏輯內存,從而讓程序獲得更多的可用內存。虛擬內存使用部分加載的技術,讓一個進程或者資源的某些頁面加載進內存,從而能夠加載更多的進程,甚至能加載比內存大的進程,這樣看起來好像內存變大了,這部分內存其實包含了磁盤或者硬盤,并且就叫做虛擬內存。
22. 虛擬內存的實現方式有哪些?
虛擬內存中,允許將一個作業分多次調入內存。釆用連續分配方式時,會使相當一部分內存空間都處于暫時或永久
的空閑狀態,造成內存資源的嚴重浪費,而且也無法從邏輯上擴大內存容量。因此,虛擬內存的實需要建立在離散分配的內存管理方式的基礎上。虛擬內存的實現有以下三種方式:
- 請求分頁存儲管理。
- 請求分段存儲管理。
- 請求段頁式存儲管理。
23. 講一講IO多路復用?
IO多路復用是指內核一旦發現進程指定的一個或者多個IO條件準備讀取,它就通知該進程。IO多路復用適用如下場合:
- 當客戶處理多個描述字時(一般是交互式輸入和網絡套接口),必須使用I/O復用。
- 當一個客戶同時處理多個套接口時,而這種情況是可能的,但很少出現。
- 如果一個TCP服務器既要處理監聽套接口,又要處理已連接套接口,一般也要用到I/O復用。
- 如果一個服務器即要處理TCP,又要處理UDP,一般要使用I/O復用。
- 如果一個服務器要處理多個服務或多個協議,一般要使用I/O復用。
- 與多進程和多線程技術相比,I/O多路復用技術的最大優勢是系統開銷小,系統不必創建進程/線程,也不必維護這些進程/線程,從而大大減小了系統的開銷。
24. 硬鏈接和軟鏈接有什么區別?
- 硬鏈接就是在目錄下創建一個條目,記錄著文件名與
inode
編號,這個inode
就是源文件的inode
。刪除任意一個條目,文件還是存在,只要引用數量不為0
。但是硬鏈接有限制,它不能跨越文件系統,也不能對目錄進行鏈接。 - 符號鏈接文件保存著源文件所在的絕對路徑,在讀取時會定位到源文件上,可以理解為
Windows
的快捷方式。當源文件被刪除了,鏈接文件就打不開了。因為記錄的是路徑,所以可以為目錄建立符號鏈接。
25. 中斷的處理過程?
- 保護現場:將當前執行程序的相關數據保存在寄存器中,然后入棧。
- 開中斷:以便執行中斷時能響應較高級別的中斷請求。
- 中斷處理
- 關中斷:保證恢復現場時不被新中斷打擾
- 恢復現場:從堆棧中按序取出程序數據,恢復中斷前的執行狀態。
26. 中斷和輪詢有什么區別?
- 輪詢:CPU對特定設備輪流詢問。中斷:通過特定事件提醒CPU。
- 輪詢:效率低等待時間長,CPU利用率不高。中斷:容易遺漏問題,CPU利用率不高。
27. 什么是用戶態和內核態?
用戶態和系統態是操作系統的兩種運行狀態:
- 內核態:內核態運行的程序可以訪問計算機的任何數據和資源,不受限制,包括外圍設備,比如網卡、硬盤等。處于內核態的 CPU 可以從一個程序切換到另外一個程序,并且占用 CPU 不會發生搶占情況。
- 用戶態:用戶態運行的程序只能受限地訪問內存,只能直接讀取用戶程序的數據,并且不允許訪問外圍設備,用戶態下的 CPU 不允許獨占,也就是說 CPU 能夠被其他程序獲取。
將操作系統的運行狀態分為用戶態和內核態,主要是為了對訪問能力進行限制,防止隨意進行一些比較危險的操作導致系統的崩潰,比如設置時鐘、內存清理,這些都需要在內核態下完成 。
28. 用戶態和內核態是如何切換的?
所有的用戶進程都是運行在用戶態的,但是我們上面也說了,用戶程序的訪問能力有限,一些比較重要的比如從硬盤讀取數據,從鍵盤獲取數據的操作則是內核態才能做的事情,而這些數據卻又對用戶程序來說非常重要。所以就涉及到兩種模式下的轉換,即用戶態 -> 內核態 -> 用戶態,而唯一能夠做這些操作的只有 系統調用
,而能夠執行系統調用的就只有 操作系統
。
一般用戶態 -> 內核態的轉換我們都稱之為 trap 進內核,也被稱之為 陷阱指令(trap instruction)
。
他們的工作流程如下:
- 首先用戶程序會調用
glibc
庫,glibc 是一個標準庫,同時也是一套核心庫,庫中定義了很多關鍵 API。 - glibc 庫知道針對不同體系結構調用
系統調用
的正確方法,它會根據體系結構應用程序的二進制接口設置用戶進程傳遞的參數,來準備系統調用。 - 然后,glibc 庫調用
軟件中斷指令(SWI)
,這個指令通過更新CPSR
寄存器將模式改為超級用戶模式,然后跳轉到地址0x08
處。 - 到目前為止,整個過程仍處于用戶態下,在執行 SWI 指令后,允許進程執行內核代碼,MMU 現在允許內核虛擬內存訪問
- 從地址 0x08 開始,進程執行加載并跳轉到中斷處理程序,這個程序就是 ARM 中的
vector_swi()
。 - 在 vector_swi() 處,從 SWI 指令中提取系統調用號 SCNO,然后使用 SCNO 作為系統調用表
sys_call_table
的索引,調轉到系統調用函數。 - 執行系統調用完成后,將還原用戶模式寄存器,然后再以用戶模式執行。
29. Unix 常見的IO模型:
對于一次IO訪問(以read舉例),數據會先被拷貝到操作系統內核的緩沖區中,然后才會從操作系統內核的緩沖區拷貝到應用程序的地址空間。所以說,當一個read操作發生時,它會經歷兩個階段:
- 等待數據準備就緒 (Waiting for the data to be ready)
- 將數據從內核拷貝到進程中 (Copying the data from the kernel to the process)
正式因為這兩個階段,linux系統產生了下面五種網絡模式的方案:
- 阻塞式IO模型(blocking IO model)
- 非阻塞式IO模型(noblocking IO model)
- IO復用式IO模型(IO multiplexing model)
- 信號驅動式IO模型(signal-driven IO model)
- 異步IO式IO模型(asynchronous IO model)
對于這幾種 IO 模型的詳細說明,可以參考這篇文章:https://juejin.cn/post/6942686874301857800#heading-13
其中,IO多路復用模型指的是:使用單個進程同時處理多個網絡連接IO,他的原理就是select、poll、epoll 不斷輪詢所負責的所有 socket,當某個socket有數據到達了,就通知用戶進程。該模型的優勢并不是對于單個連接能處理得更快,而是在于能處理更多的連接。
30. select、poll 和 epoll 之間的區別?
(1)select:時間復雜度 O(n)
select 僅僅知道有 I/O 事件發生,但并不知道是哪幾個流,所以只能無差別輪詢所有流,找出能讀出數據或者寫入數據的流,并對其進行操作。所以 select 具有 O(n) 的無差別輪詢復雜度,同時處理的流越多,無差別輪詢時間就越長。
(2)poll:時間復雜度 O(n)
poll 本質上和 select 沒有區別,它將用戶傳入的數組拷貝到內核空間,然后查詢每個 fd 對應的設備狀態, 但是它沒有最大連接數的限制,原因是它是基于鏈表來存儲的。
(3)epoll:時間復雜度 O(1)
epoll 可以理解為 event poll,不同于忙輪詢和無差別輪詢,epoll 會把哪個流發生了怎樣的 I/O 事件通知我們。所以說 epoll 實際上是事件驅動(每個事件關聯上 fd)的。
select,poll,epoll 都是 IO 多路復用的機制。I/O 多路復用就是通過一種機制監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),就通知程序進行相應的讀寫操作。但 select,poll,epoll 本質上都是同步 I/O,因為他們都需要在讀寫事件就緒后自己負責進行讀寫,也就是說這個讀寫過程是阻塞的,而異步 I/O 則無需自己負責進行讀寫,異步 I/O 的實現會負責把數據從內核拷貝到用戶空間。