
前言
前兩篇文章我們分别介紹了在面試中作業系統有關線程和程序和系統中的一些問題常見的面試題。這篇文章我們繼續給大家介紹常見的問題。這篇文章将給大家介紹作業系統中系統相關的問題。
面試題及參考答案
1、請你說一說作業系統中的結構體對齊,位元組對齊- 1、原因: 1)平台原因(移植原因): 不是所有的硬體平台都能通路任意位址上的任意資料的;某些硬體平台隻能在某些位址處取某些特定類型的資料,否則抛出硬體異常。 2)性能原因: 資料結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在于,為了通路未對齊的記憶體,處理器需要作兩次記憶體通路;而對齊的記憶體通路僅需要一次通路。
- 2、規則 1)資料成員對齊規則:
結構(struct)(或聯合(union))的資料成員,第一個資料成員放在offset為0的地方,以後每個資料成員的對齊按照#pragma
pack指定的數值和這個資料成員自身長度中,比較小的那個進行。
2)結構(或聯合)的整體對齊規則:在資料成員完成各自對齊之後,結構(或聯合)本身也要進行對齊,對齊将按照#pragma
pack指定的數值和結構(或聯合)最大資料成員長度中,比較小的那個進行。
3)結構體作為成員: 如果一個結構裡有某些結構體成員,則結構體成員要從其内部最大元素大小的整數倍位址開始存儲。 - 3、定義結構體對齊 可以通過預編譯指令#pragma pack(n),n=1,2,4,8,16來改變這一系數,其中的n就是指定的“對齊系數”。
- 1、互斥鎖和讀寫鎖差別:
互斥鎖:mutex,用于保證在任何時刻,都隻能有一個線程通路該對象。當擷取鎖操作失敗時,線程會進入睡眠,等待鎖釋放時被喚醒。
讀寫鎖:rwlock,分為讀鎖和寫鎖。處于讀操作時,可以允許多個線程同時獲得讀操作。但是同一時刻隻能有一個線程可以獲得寫鎖。其它擷取寫鎖失敗的線程都會進入睡眠狀态,直到寫鎖釋放時被喚醒。 注意:寫鎖會阻塞其它讀寫鎖。當有一個線程獲得寫鎖在寫時,讀鎖也不能被其它線程擷取;寫者優先于讀者(一旦有寫者,則後續讀者必須等待,喚醒時優先考慮寫者)。适用于讀取資料的頻率遠遠大于寫資料的頻率的場合。
互斥鎖和讀寫鎖的差別:1)讀寫鎖區分讀者和寫者,而互斥鎖不區分
2)互斥鎖同一時間隻允許一個線程通路該對象,無論讀寫;讀寫鎖同一時間内隻允許一個寫者,但是允許多個讀者同時讀對象。
- 2、Linux的4種鎖機制:
3、請你回答一下軟連結和硬連結差別互斥鎖:mutex,用于保證在任何時刻,都隻能有一個線程通路該對象。當擷取鎖操作失敗時,線程會進入睡眠,等待鎖釋放時被喚醒
讀寫鎖:rwlock,分為讀鎖和寫鎖。處于讀操作時,可以允許多個線程同時獲得讀操作。但是同一時刻隻能有一個線程可以獲得寫鎖。其它擷取寫鎖失敗的線程都會進入睡眠狀态,直到寫鎖釋放時被喚醒。
注意:寫鎖會阻塞其它讀寫鎖。當有一個線程獲得寫鎖在寫時,讀鎖也不能被其它線程擷取;寫者優先于讀者(一旦有寫者,則後續讀者必須等待,喚醒時優先考慮寫者)。适用于讀取資料的頻率遠遠大于寫資料的頻率的場合。
自旋鎖:spinlock,在任何時刻同樣隻能有一個線程通路對象。但是當擷取鎖操作失敗時,不會進入睡眠,而是會在原地自旋,直到鎖被釋放。這樣節省了線程從睡眠狀态到被喚醒期間的消耗,在加鎖時間短暫的環境下會極大的提高效率。但如果加鎖時間過長,則會非常浪費CPU資源。
RCU:即read-copy-update,在修改資料時,首先需要讀取資料,然後生成一個副本,對副本進行修改。修改完成後,再将老資料update成新的資料。使用RCU時,讀者幾乎不需要同步開銷,既不需要獲得鎖,也不使用原子指令,不會導緻鎖競争,是以就不用考慮死鎖問題了。而對于寫者的同步開銷較大,它需要複制被修改的資料,還必須使用鎖機制同步并行其它寫者的修改操作。在有大量讀操作,少量寫操作的情況下效率非常高。
為了解決檔案共享問題,Linux引入了軟連結和硬連結。除了為Linux解決檔案共享使用,還帶來了隐藏檔案路徑、增權重限安全及節省存儲等好處。若1個inode号對應多個檔案名,則為硬連結,即硬連結就是同一個檔案使用了不同的别名,使用ln建立。若檔案使用者資料塊中存放的内容是另一個檔案的路徑名指向,則該檔案是軟連接配接。軟連接配接是一個普通檔案,有自己獨立的inode,但是其資料塊内容比較特殊。4、請問什麼是大端小端以及如何判斷大端小端
大端是指低位元組存儲在高位址;小端存儲是指低位元組存儲在低位址。我們可以根據聯合體來判斷該系統是大端還是小端。因為聯合體變量總是從低位址存儲。5、 請你說一下源碼到可執行檔案的過程
- 1)預編譯
主要處理源代碼檔案中的以“#”開頭的預編譯指令。處理規則見下: 1、删除所有的#define,展開所有的宏定義。
2、處理所有的條件預編譯指令,如“#if”、“#endif”、“#ifdef”、“#elif”和“#else”。
3、處理“#include”預編譯指令,将檔案内容替換到它的位置,這個過程是遞歸進行的,檔案中包含其他檔案。
4、删除所有的注釋,“//”和“”。 5、保留所有的#pragma 編譯器指令,編譯器需要用到他們,如:#pragma once
是為了防止有檔案被重複引用。 6、添加行号和檔案辨別,便于編譯時編譯器産生調試用的行号資訊,和編譯時産生編譯錯誤或警告是能夠顯示行号。
- 2)編譯
把預編譯之後生成的xxx.i或xxx.ii檔案,進行一系列詞法分析、文法分析、語義分析及優化後,生成相應的彙編代碼檔案。
1、詞法分析:利用類似于“有限狀态機”的算法,将源代碼程式輸入到掃描機中,将其中的字元序列分割成一系列的記号。
2、文法分析:文法分析器對由掃描器産生的記号,進行文法分析,産生文法樹。由文法分析器輸出的文法樹是一種以表達式為節點的樹。
3、語義分析:文法分析器隻是完成了對表達式文法層面的分析,語義分析器則對表達式是否有意義進行判斷,其分析的語義是靜态語義——在編譯期能分期的語義,相對應的動态語義是在運作期才能确定的語義。
4、優化:源代碼級别的一個優化過程。 5、目标代碼生成:由代碼生成器将中間代碼轉換成目标機器代碼,生成一系列的代碼序列——彙編語言表示。
6、目标代碼優化:目标代碼優化器對上述的目标機器代碼進行優化:尋找合适的尋址方式、使用位移來替代乘法運算、删除多餘的指令等。
- 3)彙編 将彙編代碼轉變成機器可以執行的指令(機器碼檔案)。 彙編器的彙編過程相對于編譯器來說更簡單,沒有複雜的文法,也沒有語義,更不需要做指令優化,隻是根據彙編指令和機器指令的對照表一一翻譯過來,彙編過程有彙編器as完成。經彙編之後,産生目标檔案(與可執行檔案格式幾乎一樣)xxx.o(Windows下)、xxx.obj(Linux下)。
- 4)連結 将不同的源檔案産生的目标檔案進行連結,進而形成一個可以執行的程式。連結分為靜态連結和動态連結: 1、靜态連結:
函數和資料被編譯進一個二進制檔案。在使用靜态庫的情況下,在編譯連結可執行檔案時,連結器從庫中複制這些函數和資料并把它們和應用程式的其它子產品組合起來建立最終的可執行檔案。
空間浪費:因為每個可執行程式中對所有需要的目标檔案都要有一份副本,是以如果多個程式對同一個目标檔案都有依賴,會出現同一個目标檔案都在記憶體存在多個副本;
更新困難:每當庫函數的代碼修改了,這個時候就需要重新進行編譯連結形成可執行程式。
運作速度快:但是靜态連結的優點就是,在可執行程式中已經具備了所有執行程式所需要的任何東西,在執行的時候運作速度快。
2、動态連結:動态連結的基本思想是把程式按照子產品拆分成各個相對獨立部分,在程式運作時才将它們連結在一起形成一個完整的程式,而不是像靜态連結一樣把所有程式子產品都連結成一個單獨的可執行檔案。
共享庫:就是即使需要每個程式都依賴同一個庫,但是該庫不會像靜态連結那樣在記憶體中存在多分,副本,而是這多個程式在執行時共享同一份副本;
更新友善:更新時隻需要替換原來的目标檔案,而無需将所有的程式再重新連結一遍。當程式下一次運作時,新版本的目标檔案會被自動加載到記憶體并且連結起來,程式就完成了更新的目标。
性能損耗:因為把連結推遲到了程式運作時,是以每次執行程式都需要進行連結,是以性能會有一定損失。
1、GDB調試7、請你說一說異步程式設計的事件循環GDB 是自由軟體基金會(Free Software Foundation)的軟體工具之一。它的作用是協助程式員找到代碼中的錯誤。如果沒有GDB的幫助,程式員要想跟蹤代碼的執行流程,唯一的辦法就是添加大量的語句來産生特定的輸出。但這一手段本身就可能會引入新的錯誤,進而也就無法對那些導緻程式崩潰的錯誤代碼進行分析。
GDB的出現減輕了開發人員的負擔,他們可以在程式運作的時候單步跟蹤自己的代碼,或者通過斷點暫時中止程式的執行。此外,他們還能夠随時察看變量和記憶體的目前狀态,并監視關鍵的資料結構是如何影響代碼運作的。
2、條件斷點 條件斷點是當滿足條件就中斷程式運作,指令:break line-or-function if expr。
事件循環就是不停循環等待時間的發生,然後将這個事件的所有處理器,以及他們訂閱這個事件的時間順序依次依次執行。當這個事件的所有處理器都被執行完畢之後,事件循環就會開始繼續等待下一個事件的觸發,不斷往複。當同時并發地處理多個請求時,以上的概念也是正确的,可以這樣了解:在單個的線程中,事件處理器是一個一個按順序執行的。即如果某個事件綁定了兩個處理器,那麼第二個處理器會在第一個處理器執行完畢後,才開始執行。在這個事件的所有處理器都執行完畢之前,事件循環不會去檢查是否有新的事件觸發。在單個線程中,一切都是有順序地一個一個地執行的!8、請你回答一下為什麼要有page cache,作業系統怎麼設計的page cache
加快從磁盤讀取檔案的速率。page cache中有一部分磁盤檔案的緩存,因為從磁盤中讀取檔案比較慢,是以讀取檔案先去page cache中去查找,如果命中,則不需要去磁盤中讀取,大大加快讀取速度。在 Linux 核心中,檔案的每個資料塊最多隻能對應一個 Page Cache 項,它通過兩個資料結構來管理這些 Cache項,一個是radix tree,另一個是雙向連結清單。Radix tree 是一種搜尋樹,Linux核心利用這個資料結構來通過檔案内偏移快速定位Cache 項。9、什麼是頁式存儲?
主存被等分成大小相等的片,稱為主存塊,又稱為實頁。 當一個使用者程式裝入記憶體時,以頁面為機關進行配置設定。頁面的大小是為2n,通常為1KB、2KB、2n KB等10、 Python鎖
Python中的各種鎖:
- 一、全局解釋器鎖(GIL) 1、什麼是全局解釋器鎖 每個CPU在同一時間隻能執行一個線程,那麼其他的線程就必須等待該線程的全局解釋器,使用權消失後才能使用全局解釋器,即使多個線程直接不會互相影響在同一個程序下也隻有一個線程使用cpu,這樣的機制稱為全局解釋器鎖(GIL)。GIL的設計簡化了CPython的實作,使的對象模型包括關鍵的内建類型,如:字典等,都是隐含的,可以并發通路的,鎖住全局解釋器使得比較容易的實作對多線程的支援,但也損失了多處理器主機的并行計算能力。 2、全局解釋器鎖的好處
1)、避免了大量的加鎖解鎖的好處
2)、使資料更加安全,解決多線程間的資料完整性和狀态同步
3、全局解釋器的缺點 多核處理器退化成單核處理器,隻能并發不能并行。 4、GIL的作用: 多線程情況下必須存在資源的競争,GIL是為了保證在解釋器級别的線程唯一使用共享資源(cpu)。 - 二、同步鎖 1、什麼是同步鎖? 同一時刻的一個程序下的一個線程隻能使用一個cpu,要確定這個線程下的程式在一段時間内被cpu執,那麼就要用到同步鎖。 2、為什麼用同步鎖? 因為有可能當一個線程在使用cpu時,該線程下的程式可能會遇到io操作,那麼cpu就會切到别的線程上去,這樣就有可能會影響到該程 序結果的完整性。 3、怎麼使用同步鎖? 隻需要在對公共資料的操作前後加上上鎖和釋放鎖的操作即可。 4、同步鎖的所用: 為了保證解釋器級别下的自己編寫的程式唯一使用共享資源産生了同步鎖。
- 三、死鎖 1、什麼是死鎖? 指兩個或兩個以上的線程或程序在執行程式的過程中,因争奪資源或者程式推進順序不當而互相等待的一個現象。 2、死鎖産生的必要條件? 互斥條件、請求和保持條件、不剝奪條件、環路等待條件 3、處理死鎖的基本方法? 預防死鎖、避免死鎖(銀行家算法)、檢測死鎖(資源配置設定)、解除死鎖:剝奪資源、撤銷程序
- 四、什麼是遞歸鎖? 在Python中為了支援同一個線程中多次請求同一資源,Python提供了可重入鎖。這個RLock内部維護着一個Lock和一個counter變量,counter記錄了acquire的次數,進而使得資源可以被多次require。直到一個線程所有的acquire都被release,其他的線程才能獲得資源。遞歸鎖分為可遞歸鎖與非遞歸鎖。
- 五、什麼是樂觀鎖? 假設不會發生并發沖突,隻在送出操作時檢查是否違反資料完整性。
- 六、什麼是悲觀鎖? 假定會發生并發沖突,屏蔽一切可能違反資料完整性的操作。
- 七、python常用的加鎖方式? 互斥鎖、可重入鎖、疊代死鎖、互相調用死鎖、自旋鎖。
總結
由于作業系統面試的内容較多,是以前面兩篇文章和本篇文章對面試中常見的作業系統問題進行了簡單的總結,分别從作業系統中的線程、程序和系統的相關問題以及本篇文章是關于其他的相關問題。本人之是以花三篇文章的篇幅來介紹作業系統相關問題,一方面是為了友善自己以後面試的複習,另外也是給大家再次面試相關崗位的時候提供複習方向以及思路解答。這裡就需要我們對作業系統有一個較為深層次的了解。于是,我們在準備的時候,首先就應該夯實基礎,隻有這樣才能在衆多的面試者中脫穎而出。另外,作為在計算機行業工作的從事者,掌握一些基礎的作業系統的知識是很有必要的,也是我們的基本素養。最後希望大家不斷進步,都能盡早拿到自己比較滿意的offer!!!!繼續加油,未來可期!!!!