訂閱
糾錯
加入自媒體

動態(tài)分區(qū)分配算法有哪幾種?

2021-04-26 16:04
拓跋阿秀
關注

14、一個程序從開始運行到結(jié)束的完整過程,你能說出來多少?

四個過程:

(1)預編譯

主要處理源代碼文件中的以“!遍_頭的預編譯指令。處理規(guī)則見下

1、刪除所有的#define,展開所有的宏定義。

2、處理所有的條件預編譯指令,如“#if”、“#endif”、“#ifdef”、“#elif”和“#else”。

3、處理“#include”預編譯指令,將文件內(nèi)容替換到它的位置,這個過程是遞歸進行的,文件中包含其他文件。

4、刪除所有的注釋,“//”和“”。

5、保留所有的#pragma 編譯器指令,編譯器需要用到他們,如:#pragma once 是為了防止有文件被重復引用。

6、添加行號和文件標識,便于編譯時編譯器產(chǎn)生調(diào)試用的行號信息,和編譯時產(chǎn)生編譯錯誤或警告是能夠顯示行號。

(2)編譯

把預編譯之后生成的xxx.i或xxx.ii文件,進行一系列詞法分析、語法分析、語義分析及優(yōu)化后,生成相應的匯編代碼文件。

1、詞法分析:利用類似于“有限狀態(tài)機”的算法,將源代碼程序輸入到掃描機中,將其中的字符序列分割成一系列的記號。

2、語法分析:語法分析器對由掃描器產(chǎn)生的記號,進行語法分析,產(chǎn)生語法樹。由語法分析器輸出的語法樹是一種以表達式為節(jié)點的樹。

3、語義分析:語法分析器只是完成了對表達式語法層面的分析,語義分析器則對表達式是否有意義進行判斷,其分析的語義是靜態(tài)語義——在編譯期能分期的語義,相對應的動態(tài)語義是在運行期才能確定的語義。

4、優(yōu)化:源代碼級別的一個優(yōu)化過程。

5、目標代碼生成:由代碼生成器將中間代碼轉(zhuǎn)換成目標機器代碼,生成一系列的代碼序列——匯編語言表示。

6、目標代碼優(yōu)化:目標代碼優(yōu)化器對上述的目標機器代碼進行優(yōu)化:尋找合適的尋址方式、使用位移來替代乘法運算、刪除多余的指令等。

(3)匯編

將匯編代碼轉(zhuǎn)變成機器可以執(zhí)行的指令(機器碼文件)。匯編器的匯編過程相對于編譯器來說更簡單,沒有復雜的語法,也沒有語義,更不需要做指令優(yōu)化,只是根據(jù)匯編指令和機器指令的對照表一一翻譯過來,匯編過程有匯編器as完成。經(jīng)匯編之后,產(chǎn)生目標文件(與可執(zhí)行文件格式幾乎一樣)xxx.o(Linux下)、xxx.obj(Windows下)。

(4)鏈接

將不同的源文件產(chǎn)生的目標文件進行鏈接,從而形成一個可以執(zhí)行的程序。鏈接分為靜態(tài)鏈接和動態(tài)鏈接:

1、靜態(tài)鏈接:

函數(shù)和數(shù)據(jù)被編譯進一個二進制文件。在使用靜態(tài)庫的情況下,在編譯鏈接可執(zhí)行文件時,鏈接器從庫中復制這些函數(shù)和數(shù)據(jù)并把它們和應用程序的其它模塊組合起來創(chuàng)建最終的可執(zhí)行文件。

空間浪費:因為每個可執(zhí)行程序中對所有需要的目標文件都要有一份副本,所以如果多個程序?qū)ν粋目標文件都有依賴,會出現(xiàn)同一個目標文件都在內(nèi)存存在多個副本;更新困難:每當庫函數(shù)的代碼修改了,這個時候就需要重新進行編譯鏈接形成可執(zhí)行程序。

運行速度快:但是靜態(tài)鏈接的優(yōu)點就是,在可執(zhí)行程序中已經(jīng)具備了所有執(zhí)行程序所需要的任何東西,在執(zhí)行的時候運行速度快。

2、動態(tài)鏈接:

動態(tài)鏈接的基本思想是把程序按照模塊拆分成各個相對獨立部分,在程序運行時才將它們鏈接在一起形成一個完整的程序,而不是像靜態(tài)鏈接一樣把所有程序模塊都鏈接成一個單獨的可執(zhí)行文件。

共享庫:就是即使需要每個程序都依賴同一個庫,但是該庫不會像靜態(tài)鏈接那樣在內(nèi)存中存在多份副本,而是這多個程序在執(zhí)行時共享同一份副本;更新方便:更新時只需要替換原來的目標文件,而無需將所有的程序再重新鏈接一遍。當程序下一次運行時,新版本的目標文件會被自動加載到內(nèi)存并且鏈接起來,程序就完成了升級的目標。性能損耗:因為把鏈接推遲到了程序運行時,所以每次執(zhí)行程序都需要進行鏈接,所以性能會有一定損失。

15、通過例子講解邏輯地址轉(zhuǎn)換為物理地址的基本過程

可以借助進程的頁表將邏輯地址轉(zhuǎn)換為物理地址。

通常會在系統(tǒng)中設置一個頁表寄存器(PTR),存放頁表在內(nèi)存中的起始地址F和頁表長度M。進程未執(zhí)行時,頁表的始址和頁表長度放在進程控制塊(PCB) 中,當進程被調(diào)度時,操作系統(tǒng)內(nèi)核會把它們放到頁表寄存器中。

注意:頁面大小是2的整數(shù)冪

設頁面大小為L,邏輯地址A到物理地址E的變換過程如下:

例:若頁面大小L為1K字節(jié),頁號2對應的內(nèi)存塊號b=8,將邏輯地址A=2500轉(zhuǎn)換為物理地址E。
等價描述:某系統(tǒng)按字節(jié)尋址,邏輯地址結(jié)構(gòu)中,頁內(nèi)偏移量占10位(說明一個頁面的大小為2^10B = 1KB),頁號2對應的內(nèi)存塊號 b=8,將邏輯地址A=2500轉(zhuǎn)換為物理地址E。

①計算頁號、頁內(nèi)偏移量
頁號P=A/L = 2500/1024 = 2; 頁內(nèi)偏移量W= A%L = 2500%1024 = 452

②根據(jù)題中條件可知,頁號2沒有越界,其存放的內(nèi)存塊號b=8

③物理地址E=b*L+W=8 * 1024+ 425 = 8644

在分頁存儲管理(頁式管理)的系統(tǒng)中,只要確定了每個頁面的大小,邏輯地址結(jié)構(gòu)就確定了。因此,頁式管理中地址是-維的。即,只要給出一個邏輯地址,系統(tǒng)就可以自動地算出頁號、頁內(nèi)偏移量兩個部分,并不需要顯式地告訴系統(tǒng)這個邏輯地址中,頁內(nèi)偏移量占多少位。

16、進程同步的四種方法?

1. 臨界區(qū)

對臨界資源進行訪問的那段代碼稱為臨界區(qū)。

為了互斥訪問臨界資源,每個進程在進入臨界區(qū)之前,需要先進行檢查。

// entry section
// critical section;
// exit section

2. 同步與互斥

同步:多個進程因為合作產(chǎn)生的直接制約關系,使得進程有一定的先后執(zhí)行關系。

互斥:多個進程在同一時刻只有一個進程能進入臨界區(qū)。

3. 信號量

信號量(Semaphore)是一個整型變量,可以對其執(zhí)行 down 和 up 操作,也就是常見的 P 和 V 操作。

down: 如果信號量大于 0 ,執(zhí)行 -1 操作;如果信號量等于 0,進程睡眠,等待信號量大于 0;

up:對信號量執(zhí)行 +1 操作,喚醒睡眠的進程讓其完成 down 操作。

down和up操作需要被設計成原語,不可分割,通常的做法是在執(zhí)行這些操作的時候屏蔽中斷。

如果信號量的取值只能為0或者1,那么就成為了互斥量(Mutex),0 表示臨界區(qū)已經(jīng)加鎖,1 表示臨界區(qū)解鎖。

typedef int semaphore;
semaphore mutex = 1;
void P1() {
   down(&mutex);
   // 臨界區(qū)
   up(&mutex);

void P2() {
   down(&mutex);
   // 臨界區(qū)
   up(&mutex);

使用信號量實現(xiàn)生產(chǎn)者-消費者問題    

問題描述:使用一個緩沖區(qū)來保存物品,只有緩沖區(qū)沒有滿,生產(chǎn)者才可以放入物品;只有緩沖區(qū)不為空,消費者才可以拿走物品。

因為緩沖區(qū)屬于臨界資源,因此需要使用一個互斥量 mutex 來控制對緩沖區(qū)的互斥訪問。

為了同步生產(chǎn)者和消費者的行為,需要記錄緩沖區(qū)中物品的數(shù)量。數(shù)量可以使用信號量來進行統(tǒng)計,這里需要使用兩個信號量:empty 記錄空緩沖區(qū)的數(shù)量,full 記錄滿緩沖區(qū)的數(shù)量。

其中,empty 信號量是在生產(chǎn)者進程中使用,當 empty 不為 0 時,生產(chǎn)者才可以放入物品;full 信號量是在消費者進程中使用,當 full 信號量不為 0 時,消費者才可以取走物品。

注意,不能先對緩沖區(qū)進行加鎖,再測試信號量。也就是說,不能先執(zhí)行 down(mutex) 再執(zhí)行 down(empty)。如果這么做了,那么可能會出現(xiàn)這種情況:生產(chǎn)者對緩沖區(qū)加鎖后,執(zhí)行 down(empty) 操作,發(fā)現(xiàn) empty = 0,此時生產(chǎn)者睡眠。

消費者不能進入臨界區(qū),因為生產(chǎn)者對緩沖區(qū)加鎖了,消費者就無法執(zhí)行 up(empty) 操作,empty 永遠都為 0,導致生產(chǎn)者永遠等待下,不會釋放鎖,消費者因此也會永遠等待下去。

#define N 100
typedef int semaphore;
semaphore mutex = 1;
semaphore empty = N;
semaphore full = 0;
void producer() {
   while(TRUE) {
       int item = produce_item();
       down(&empty);
       down(&mutex);
       insert_item(item);
       up(&mutex);
       up(&full);
   }

void consumer() {
   while(TRUE) {
       down(&full);
       down(&mutex);
       int item = remove_item();
       consume_item(item);
       up(&mutex);
       up(&empty);
   }

4. 管程

使用信號量機制實現(xiàn)的生產(chǎn)者消費者問題需要客戶端代碼做很多控制,而管程把控制的代碼獨立出來,不僅不容易出錯,也使得客戶端代碼調(diào)用更容易。

c 語言不支持管程,下面的示例代碼使用了類 Pascal 語言來描述管程。示例代碼的管程提供了 insert() 和 remove() 方法,客戶端代碼通過調(diào)用這兩個方法來解決生產(chǎn)者-消費者問題。

monitor ProducerConsumer
   integer i;
   condition c;
   procedure insert();
   begin
       // ...
   end;
   procedure remove();
   begin
       // ...
   end;
end monitor;

管程有一個重要特性:在一個時刻只能有一個進程使用管程。進程在無法繼續(xù)執(zhí)行的時候不能一直占用管程,否則其它進程永遠不能使用管程。

管程引入了條件變量以及相關的操作:wait() 和 signal() 來實現(xiàn)同步操作。對條件變量執(zhí)行 wait() 操作會導致調(diào)用進程阻塞,把管程讓出來給另一個進程持有。signal() 操作用于喚醒被阻塞的進程。

使用管程實現(xiàn)生產(chǎn)者-消費者問題  

// 管程
monitor ProducerConsumer
   condition full, empty;
   integer count := 0;
   condition c;
   procedure insert(item: integer);
   begin
       if count = N then wait(full);
       insert_item(item);
       count := count + 1;
       if count = 1 then signal(empty);
   end;
   function remove: integer;
   begin
       if count = 0 then wait(empty);
       remove = remove_item;
       count := count - 1;
       if count = N -1 then signal(full);
   end;
end monitor;
// 生產(chǎn)者客戶端
procedure producer
begin
   while true do
   begin
       item = produce_item;
       ProducerConsumer.insert(item);
   end
end;
// 消費者客戶端
procedure consumer
begin
   while true do
   begin
       item = ProducerConsumer.remove;
       consume_item(item);
   end
end;

17、操作系統(tǒng)在對內(nèi)存進行管理的時候需要做些什么?

操作系統(tǒng)負責內(nèi)存空間的分配與回收。

操作系統(tǒng)需要提供某種技術從邏輯上對內(nèi)存空間進行擴充。

操作系統(tǒng)需要提供地址轉(zhuǎn)換功能,負責程序的邏輯地址與物理地址的轉(zhuǎn)換。

操作系統(tǒng)需要提供內(nèi)存保護功能。保證各進程在各自存儲空間內(nèi)運行,互不干擾

18、進程通信方法(Linux和windows下),線程通信方法(Linux和windows下)

進程通信方法

線程通信方法

19、程間通信有哪幾種方式?把你知道的都說出來

Linux幾乎支持全部UNIX進程間通信方法,包括管道(有名管道和無名管道)、消息隊列、共享內(nèi)存、信號量和套接字。其中前四個屬于同一臺機器下進程間的通信,套接字則是用于網(wǎng)絡通信。

管道

無名管道

無名管道特點:

無名管道是一種特殊的文件,這種文件只存在于內(nèi)存中。

無名管道只能用于父子進程或兄弟進程之間,必須用于具有親緣關系的進程間的通信。

無名管道只能由一端向另一端發(fā)送數(shù)據(jù),是半雙工方式,如果雙方需要同時收發(fā)數(shù)據(jù)需要兩個管道。

相關接口:

int pipe(int fd[2]);

fd[2]:管道兩端用fd[0]和fd[1]來描述,讀的一端用fd[0]表示,寫的一端用fd[1]表示。通信雙方的進程中寫數(shù)據(jù)的一方需要把fd[0]先close掉,讀的一方需要先把fd[1]給close掉。

有名管道:

有名管道特點:

有名管道是FIFO文件,存在于文件系統(tǒng)中,可以通過文件路徑名來指出。

有名管道可以在不具有親緣關系的進程間進行通信。

相關接口:

int mkfifo(const char *pathname, mode_t mode);

pathname:即將創(chuàng)建的FIFO文件路徑,如果文件存在需要先刪除。

mode:和open()中的參數(shù)相同。

消息隊列

相比于 FIFO,消息隊列具有以下優(yōu)點:

消息隊列可以獨立于讀寫進程存在,從而避免了 FIFO 中同步管道的打開和關閉時可能產(chǎn)生的困難;

避免了 FIFO 的同步阻塞問題,不需要進程自己提供同步方法;

讀進程可以根據(jù)消息類型有選擇地接收消息,而不像 FIFO 那樣只能默認地接收。

共享內(nèi)存

進程可以將同一段共享內(nèi)存連接到它們自己的地址空間,所有進程都可以訪問共享內(nèi)存中的地址,如果某個進程向共享內(nèi)存內(nèi)寫入數(shù)據(jù),所做的改動將立即影響到可以訪問該共享內(nèi)存的其他所有進程。

相關接口

創(chuàng)建共享內(nèi)存:int shmget(key_t key, int size, int flag);

成功時返回一個和key相關的共享內(nèi)存標識符,失敗范湖范圍-1。

key:為共享內(nèi)存段命名,多個共享同一片內(nèi)存的進程使用同一個key。

size:共享內(nèi)存容量。

flag:權限標志位,和open的mode參數(shù)一樣。

連接到共享內(nèi)存地址空間:void *shmat(int shmid, void *addr, int flag);

返回值即共享內(nèi)存實際地址。

shmid:shmget()返回的標識。

addr:決定以什么方式連接地址。

flag:訪問模式。

從共享內(nèi)存分離:int shmdt(const void *shmaddr);

調(diào)用成功返回0,失敗返回-1。

shmaddr:是shmat()返回的地址指針。

其他補充

共享內(nèi)存的方式像極了多線程中線程對全局變量的訪問,大家都對等地有權去修改這塊內(nèi)存的值,這就導致在多進程并發(fā)下,最終結(jié)果是不可預期的。所以對這塊臨界區(qū)的訪問需要通過信號量來進行進程同步。

但共享內(nèi)存的優(yōu)勢也很明顯,首先可以通過共享內(nèi)存進行通信的進程不需要像無名管道一樣需要通信的進程間有親緣關系。其次內(nèi)存共享的速度也比較快,不存在讀取文件、消息傳遞等過程,只需要到相應映射到的內(nèi)存地址直接讀寫數(shù)據(jù)即可。

信號量

在提到共享內(nèi)存方式時也提到,進程共享內(nèi)存和多線程共享全局變量非常相似。所以在使用內(nèi)存共享的方式是也需要通過信號量來完成進程間同步。多線程同步的信號量是POSIX信號量,而在進程里使用SYSTEM  V信號量。

相關接口

創(chuàng)建信號量:int semget(key_t key, int nsems, int semflag);

創(chuàng)建成功返回信號量標識符,失敗返回-1。

key:進程pid。

nsems:創(chuàng)建信號量的個數(shù)。

semflag:指定信號量讀寫權限。

改變信號量值:int semop(int semid, struct sembuf *sops, unsigned nsops);

我們所需要做的主要工作就是串講sembuf變量并設置其值,然后調(diào)用semop,把設置好的sembuf變量傳遞進去。

struct sembuf結(jié)構(gòu)體定義如下:

struct sembuf{
   short sem_num;
   short sem_op;
   short sem_flg;
};

成功返回信號量標識符,失敗返回-1。

semid:信號量集標識符,由semget()函數(shù)返回。

sops:指向struct sembuf結(jié)構(gòu)的指針,先設置好sembuf值再通過指針傳遞。

nsops:進行操作信號量的個數(shù),即sops結(jié)構(gòu)變量的個數(shù),需大于或等于1。最常見設置此值等于1,只完成對一個信號量的操作。

直接控制信號量信息:int semctl(int semid, int semnum, int cmd, union semun arg);

semid:信號量集標識符。

semnum:信號量集數(shù)組上的下標,表示某一個信號量。

arg:union semun類型。

輔助命令

ipcs命令用于報告共享內(nèi)存、信號量和消息隊列信息。

ipcs -a:列出共享內(nèi)存、信號量和消息隊列信息。

ipcs -l:列出系統(tǒng)限額。

ipcs -u:列出當前使用情況。

套接字

與其它通信機制不同的是,它可用于不同機器間的進程通信。

20、虛擬內(nèi)存的目的是什么?

虛擬內(nèi)存的目的是為了讓物理內(nèi)存擴充成更大的邏輯內(nèi)存,從而讓程序獲得更多的可用內(nèi)存。

為了更好的管理內(nèi)存,操作系統(tǒng)將內(nèi)存抽象成地址空間。每個程序擁有自己的地址空間,這個地址空間被分割成多個塊,每一塊稱為一頁。

這些頁被映射到物理內(nèi)存,但不需要映射到連續(xù)的物理內(nèi)存,也不需要所有頁都必須在物理內(nèi)存中。當程序引用到不在物理內(nèi)存中的頁時,由硬件執(zhí)行必要的映射,將缺失的部分裝入物理內(nèi)存并重新執(zhí)行失敗的指令。

從上面的描述中可以看出,虛擬內(nèi)存允許程序不用將地址空間中的每一頁都映射到物理內(nèi)存,也就是說一個程序不需要全部調(diào)入內(nèi)存就可以運行,這使得有限的內(nèi)存運行大程序成為可能。

例如有一臺計算機可以產(chǎn)生 16 位地址,那么一個程序的地址空間范圍是 0~64K。該計算機只有 32KB 的物理內(nèi)存,虛擬內(nèi)存技術允許該計算機運行一個 64K 大小的程序。

21、說一下你理解中的內(nèi)存?他有什么作用呢?

結(jié)語

完了,白了個白!


<上一頁  1  2  3  
聲明: 本文由入駐維科號的作者撰寫,觀點僅代表作者本人,不代表OFweek立場。如有侵權或其他問題,請聯(lián)系舉報。

發(fā)表評論

0條評論,0人參與

請輸入評論內(nèi)容...

請輸入評論/評論長度6~500個字

您提交的評論過于頻繁,請輸入驗證碼繼續(xù)

暫無評論

暫無評論

人工智能 獵頭職位 更多
掃碼關注公眾號
OFweek人工智能網(wǎng)
獲取更多精彩內(nèi)容
文章糾錯
x
*文字標題:
*糾錯內(nèi)容:
聯(lián)系郵箱:
*驗 證 碼:

粵公網(wǎng)安備 44030502002758號