1. 程式人生 > >【程式語言】誠邁試題二

【程式語言】誠邁試題二

1. linux核心記憶體分配方式

Linux核心地址空間劃分0~3G為使用者空間,3~4G為核心空間

2. new/deletemalloc/free的區別

(1)操作物件有所不同。 

malloc與free是C++/C語言的標準庫函式,new/delete是C++的運算子。對於非內部資料類的物件而言,光用maloc/free無法滿足動態物件的要求。物件在建立的同時要自動執行建構函式,物件消亡之前要自動執行解構函式。由於malloc/free是庫函式而不是運算子,不在編譯器控制權限之內,不能夠把執行建構函式和解構函式的任務強加malloc/free。

(2)在用法上也有所不同。函式malloc的原型如下:void * malloc(size_t size); 

用malloc申請一塊長度為length的整數型別的記憶體,程式如下:int *p = (int *) malloc(sizeof(int) * length); 

malloc返回值的型別是void *,所以在呼叫malloc 時要顯式地進行型別轉換,將void * 轉換成所需要的指標型別。 

malloc 函式本身並不識別要申請的記憶體是什麼型別,它只關心記憶體的總位元組數。 

函式free 的原型如下: 

void free( void * memblock ); 

為什麼free 函式不象malloc 函式那樣複雜呢?這是因為指標p 的型別以及它所指的記憶體的容量事先都是知道的,語句free(p)能正確地釋放記憶體。如果p 是NULL 指標,那麼free  

對p 無論操作多少次都不會出問題。如果p 不是NULL 指標,那麼free 對p連續操作兩次就會導致程式執行錯誤。 

new/delete 的使用要點 

運算子new 使用起來要比函式malloc 簡單得多

3. 驅動中斷

    在Linux裝置驅動程式設計中的中斷服務程式執行並不存在程序的上下文,因此在請求中斷服務程式的時間儘可能的短,因此,Linux在中斷處理中引入了頂半部和底半部分離的機制。

   所謂的中斷就是指CPU在執行程式的過程中,出現了某些突發事件待急處理,CPU必須暫停當前執行的程式,轉去處理突發事件,處理完畢後CPU又會返回原程式被中斷打斷的位置並繼續執行。根據中斷源,中斷可以分為內部中斷和外部中斷,內部中斷的中斷源來自CPU內部(軟中斷指令,溢位,除法錯誤等),外部中斷來自CPU處理器的外部,由於在arm處理器中可以有硬體可控制中斷,所以提供了外部中斷請求。

  在Linux核心中為了處理中斷的時間竟可能短和中斷處理完成大量工作之間找到一個平衡點,Linux將中斷處理分為頂半部和底半部,在頂半部完成儘可能少的緊急功能,它往往只是簡單的讀取暫存器中中斷狀態並清除中斷標誌後就進行登記中斷的工作,登記中斷的意味將底半部處理程式掛到裝置的底半部執行佇列中去,這樣頂半部可以儘可能快的服務更多的中斷請求,而底半部則要處理完中斷的所有事情。

在Linux核心中對於中斷的請求和釋放都要使用到核心的request_irq()和free_irq()函式,而在底半部機制中主要有tasklet,工作佇列和軟中斷

4. 緊湊模式和非緊湊性模式

Struct

{

Char d;

char b;

int a;

double c;

}A

Struct

{

Char d;

int a;

char b;

double c;

}B

緊湊模式:sizeof(A) =1+1+2+4+8=16

非緊湊模式:sizeof(B) =4+4+4+8=20

5. 堆和棧的區別

1程式的記憶體分配:

棧區(stack)— 由編譯器自動分配釋放 ,存放函式的引數值,區域性變數的值等

堆區(heap) — 一般由程式設計師分配釋放, 若程式設計師不釋放,程式結束時可能由OS回收

2申請方式:

stack:由系統自動配

heap:需要程式設計師自己申請,並指明大小

3申請效率的比較:

棧由系統自動分配,速度較快。但程式設計師是無法控制的。堆是由new分配的記憶體,一般速度比較慢,而且容易產生記憶體碎片,不過用起來最方便.

4申請後的系統響應及大小限制:

棧:只要棧的剩餘空間大於所申請空間,系統將為程式提供記憶體,否則將報異常提示棧溢出。堆:首先應該知道作業系統有一個記錄空閒記憶體地址的連結串列,當系統收到程式的申請時,會遍歷該連結串列,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閒結點連結串列中刪除,並將該結點的空間分配給程式,另外,對於大多數系統,會在這塊記憶體空間中的首地址處記錄本次分配的大小,這樣,程式碼中的delete語句才能正確的釋放本記憶體空間。另外,由於找到的堆結點的大小不一定正好等於申請的大小,系統會自動的將多餘的那部分重新放入空閒連結串列中。

5堆疊的儲存內容 :

棧: 在函式呼叫時,第一個進棧的是主函式中後的下一條指令(函式呼叫語句的下一條可執行語句)的地址,然後是函式的各個引數引數是由右往左入棧的,然後是函式中的區域性變數。注意靜態變數是不入棧的

當本次函式呼叫結束後,區域性變數先出棧,然後是引數,最後棧頂指標指向最開始存的地址,也就是主函式中的下一條指令,程式由該點繼續執行

堆:一般是在堆的頭部用一個位元組存放堆的大小。堆中的具體內容由程式設計師安排。

6. 呼叫函式時的壓棧順序

在函式呼叫時,第一個進棧的是主函式中後的下一條指令(函式呼叫語句的下一條可執行語句)的地址,然後是函式的各個引數引數是由右往左入棧的,然後是函式中的區域性變數。注意靜態變數是不入棧的

7. 區域性變數和全域性變數

1)全域性變數具有全域性作用域。全域性變數只需在一個原始檔中定義,就可以作用於所有的原始檔。當然,其他不包括全域性變數定義的原始檔需要用extern關鍵字再次宣告這個全域性變數

2)區域性變數只有區域性作用域,是自動物件,他在程式執行期間不是一直存在,而是隻在函式執行期間存在,函式的一次呼叫結束後,變數就被撤銷,其所佔用的記憶體也被收回。

8. bootloader啟動過程

第一步:當系統在上電或者復位的後,CPU的PC指標會指向實體地址的0x00000000處,這裡的實體地址表示片內的IROM儲存器,它固化了系統啟動的BL0引導程式碼,這段程式碼會選擇以什麼方式啟動,就會找到bootloader的BL1存放的地址,由於BL1不支援片上執行,所以會把IROM這段程式碼拷貝到IRAM中去,但是在拷貝之前,IROM會先把IRAM的起始4個地址分別寫上一定的內容(不過通常只寫0x00地址為BL1所佔空間的大小(一般是8k),而其他三個地址全寫為0),把BL1這段程式碼拷貝到IRAM後,IROM的使命就快要完成了,IROM的最後一個任務就是把PC指標指向放BL1的那塊地址的起始位置,然後就按照這個地址的指令開始執行。

第二步:這個時候開始執行BL1的程式碼。這段程式碼的功能是初始化硬體,比如串列埠,記憶體,顯示器,按鍵等,還有電源、時鐘初始化,堆疊空間,以及各種必要的初始化,並且會提供一個命令列,可以進行互動。在這之後會有一個設定核心引數的過程,這些引數在記憶體中的存在方式也是以結構體儲存,以連結串列進行關聯的,而這個 連結串列有一個固定的起始地址0x30000100;每一個結構體代表一個資訊,並首尾相連,核心在需要這些引數時,就可以再對應的地址上取資料。

第三部:第二步執行完畢後,就要把kernel的程式碼拷貝到SDRAM中的一個指定地址,Linux中一般拷貝到實體地址的0x20008000處,並且會把這個地址強制轉換成一個函式指標,並且向這個函式中傳遞一些引數,最終會到核心中執行核心程式碼。這個時候,核心就會被引導到執行狀態。

9. 巨集定義、行內函數、和普通函式的區別

1)行內函數的執行過程與帶引數巨集定義很相似,但引數的處理不同。帶引數的巨集定義並不對引數進行運算,而是直接替換;行內函數首先是函式,這就意味著函式的很多性質都適用於行內函數,即行內函數先把引數表示式進行運算求值,然後把表示式的值傳遞給形式引數。

2)行內函數與帶引數巨集定義的另一個區別是,行內函數的引數型別和返回值型別在宣告中都有明確的指定;而帶引數巨集定義的引數沒有型別的概念,只有在巨集展開以後,才由編譯器檢查語法,這就存在很多的安全隱患

3)巨集做的是簡單的字串替換(是字串的替換,不是其他型別引數的替換),而函式的引數的傳遞,引數是有資料型別的,可以是各種各樣的型別.

巨集的引數替換是不經計算而直接處理的,而函式呼叫是將實參的值傳遞給形參,既然說是值,自然是計算得來的.巨集在編譯之前進行,即先用巨集體替換巨集名,然後再編譯的,而函式顯然是編譯之後,在執行時,才呼叫的.因此,巨集佔用的是編譯的時間,而函式佔用的是執行時的時間.

巨集的引數是不佔記憶體空間的,因為只是做字串的替換,而函式呼叫時的引數傳遞則是具體變數之間的資訊傳遞,形參作為函式的區域性變數,顯然是佔用記憶體的.

10. 程序間通訊方式

傳統的程序間通訊方式:無名管道,有名管道和訊號

SYS V IPC物件:共享記憶體,訊息佇列和訊號燈

BSD通訊方式:套接字

11. Input子系統

輸入子系統由驅動層(Drivers),輸入子系統核心層( Input Core )和事件處理層(Event Handler)三部份組成。一個輸入事件,如滑鼠移動,鍵盤按鍵按下等都是通過 Driver -> InputCore -> Eventhandler -> userspace 的順序到達使用者空間傳給應用程式。下面介紹各部分的功能:

(1)驅動層功能:負責和底層的硬體裝置打交道,將底層硬體裝置對使用者輸入的響應轉換為標準的輸入事件以後再向上傳送給輸入子系統核心層(Input Core)。

(2)Input系統核心層:Input Core即Input Layer,由driver/input/input.c及相關標頭檔案實現,它對下提供了裝置驅動層的介面,對上提供了事件處理層(Event Handler)的程式設計介面。

(3)事件處理層將硬體裝置上報的事件分發到使用者空間和核心。