訂閱
糾錯
加入自媒體

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

本期是操作系統(tǒng)開胃菜,既然是開胃菜那字數(shù)少一點好了哈哈,就先來21 道題,16213 個字好了。

還有本來想等到《逆襲進大廠》系列出完之后再放出 PDF 的,可很多人讓我先出個第一版先看著…..

一開始我是拒絕的,可真的太多人跟我說了,考慮到最近金三銀四以及春招找工作期間很多人都需要這份資料,emm…

沒辦法,先出第一期好了。

以下是本期目錄,看看你會多少。

閑話少敘,開車開車!

1、進程、線程和協(xié)程的區(qū)別和聯(lián)系

1、進程是資源調(diào)度的基本單位,運行一個可執(zhí)行程序會創(chuàng)建一個或多個進程,進程就是運行起來的可執(zhí)行程序

2、線程是程序執(zhí)行的基本單位,是輕量級的進程。每個進程中都有唯一的主線程,且只能有一個,主線程和進程是相互依存的關(guān)系,主線程結(jié)束進程也會結(jié)束。多提一句:協(xié)程是用戶態(tài)的輕量級線程,線程內(nèi)部調(diào)度的基本單位

2、線程與進程的比較

1、線程啟動速度快,輕量級

2、線程的系統(tǒng)開銷小

3、線程使用有一定難度,需要處理數(shù)據(jù)一致性問題

4、同一線程共享的有堆、全局變量、靜態(tài)變量、指針,引用、文件等,而獨自占有棧

3、一個進程可以創(chuàng)建多少線程,和什么有關(guān)?

理論上,一個進程可用虛擬空間是2G,默認情況下,線程的棧的大小是1MB,所以理論上最多只能創(chuàng)建2048個線程。如果要創(chuàng)建多于2048的話,必須修改編譯器的設(shè)置。

因此,一個進程可以創(chuàng)建的線程數(shù)由可用虛擬空間和線程的棧的大小共同決定,只要虛擬空間足夠,那么新線程的建立就會成功。如果需要創(chuàng)建超過2K以上的線程,減小你線程棧的大小就可以實現(xiàn)了,雖然在一般情況下,你不需要那么多的線程。過多的線程將會導致大量的時間浪費在線程切換上,給程序運行效率帶來負面影響。

4、外中斷和異常有什么區(qū)別?

外中斷是指由 CPU 執(zhí)行指令以外的事件引起,如 I/O 完成中斷,表示設(shè)備輸入/輸出處理已經(jīng)完成,處理器能夠發(fā)送下一個輸入/輸出請求。此外還有時鐘中斷、控制臺中斷等。

而異常時由 CPU 執(zhí)行指令的內(nèi)部事件引起,如非法操作碼、地址越界、算術(shù)溢出等。

5、進程線程模型你知道多少?

對于進程和線程的理解和把握可以說基本奠定了對系統(tǒng)的認知和把控能力。其核心意義絕不僅僅是“線程是調(diào)度的基本單位,進程是資源分配的基本單位”這么簡單。

多線程

我們這里討論的是用戶態(tài)的多線程模型,同一個進程內(nèi)部有多個線程,所有的線程共享同一個進程的內(nèi)存空間,進程中定義的全局變量會被所有的線程共享,比如有全局變量int i = 10,這一進程中所有并發(fā)運行的線程都可以讀取和修改這個i的值,而多個線程被CPU調(diào)度的順序又是不可控的,所以對臨界資源的訪問尤其需要注意安全。

我們必須知道,做一次簡單的i = i + 1在計算機中并不是原子操作,涉及內(nèi)存取數(shù),計算和寫入內(nèi)存幾個環(huán)節(jié),而線程的切換有可能發(fā)生在上述任何一個環(huán)節(jié)中間,所以不同的操作順序很有可能帶來意想不到的結(jié)果。

但是,雖然線程在安全性方面會引入許多新挑戰(zhàn),但是線程帶來的好處也是有目共睹的。首先,原先順序執(zhí)行的程序(暫時不考慮多進程)可以被拆分成幾個獨立的邏輯流,這些邏輯流可以獨立完成一些任務(wù)(最好這些任務(wù)是不相關(guān)的)。

比如 QQ 可以一個線程處理聊天一個線程處理上傳文件,兩個線程互不干涉,在用戶看來是同步在執(zhí)行兩個任務(wù),試想如果線性完成這個任務(wù)的話,在數(shù)據(jù)傳輸完成之前用戶聊天被一直阻塞會是多么尷尬的情況。

對于線程,我認為弄清以下兩點非常重要:

線程之間有無先后訪問順序(線程依賴關(guān)系)

多個線程共享訪問同一變量(同步互斥問題)

另外,我們通常只會去說同一進程的多個線程共享進程的資源,但是每個線程特有的部分卻很少提及,除了標識線程的tid,每個線程還有自己獨立的?臻g,線程彼此之間是無法訪問其他線程棧上內(nèi)容的。

而作為處理機調(diào)度的最小單位,線程調(diào)度只需要保存線程棧、寄存器數(shù)據(jù)和PC即可,相比進程切換開銷要小很多。

線程相關(guān)接口不少,主要需要了解各個參數(shù)意義和返回值意義。

線程創(chuàng)建和結(jié)束

背景知識:

在一個文件內(nèi)的多個函數(shù)通常都是按照main函數(shù)中出現(xiàn)的順序來執(zhí)行,但是在分時系統(tǒng)下,我們可以讓每個函數(shù)都作為一個邏輯流并發(fā)執(zhí)行,最簡單的方式就是采用多線程策略。在main函數(shù)中調(diào)用多線程接口創(chuàng)建線程,每個線程對應(yīng)特定的函數(shù)(操作),這樣就可以不按照main函數(shù)中各個函數(shù)出現(xiàn)的順序來執(zhí)行,避免了忙等的情況。線程基本操作的接口如下。

相關(guān)接口:

創(chuàng)建線程:int pthread_create(pthread_t *pthread, const pthread_attr_t *attr, void (start_routine)(void *), void *agr);

創(chuàng)建一個新線程,pthread和start_routine不可或缺,分別用于標識線程和執(zhí)行體入口,其他可以填NULL。

pthread:用來返回線程的tid,*pthread值即為tid,類型pthread_t == unsigned long int。

attr:指向線程屬性結(jié)構(gòu)體的指針,用于改變所創(chuàng)線程的屬性,填NULL使用默認值。

start_routine:線程執(zhí)行函數(shù)的首地址,傳入函數(shù)指針。

arg:通過地址傳遞來傳遞函數(shù)參數(shù),這里是無符號類型指針,可以傳任意類型變量的地址,在被傳入函數(shù)中先強制類型轉(zhuǎn)換成所需類型即可。

獲得線程ID:pthread_t pthread_self();

調(diào)用時,會打印線程ID。

等待線程結(jié)束:int pthread_join(pthread_t tid, void** retval);

主線程調(diào)用,等待子線程退出并回收其資源,類似于進程中wait/waitpid回收僵尸進程,調(diào)用pthread_join的線程會被阻塞。

tid:創(chuàng)建線程時通過指針得到tid值。

retval:指向返回值的指針。

結(jié)束線程:pthread_exit(void *retval);

子線程執(zhí)行,用來結(jié)束當前線程并通過retval傳遞返回值,該返回值可通過pthread_join獲得。

retval:同上。

分離線程:int pthread_detach(pthread_t tid);

主線程、子線程均可調(diào)用。主線程中pthread_detach(tid),子線程中pthread_detach(pthread_self()),調(diào)用后和主線程分離,子線程結(jié)束時自己立即回收資源。

tid:同上。

線程屬性值修改

背景知識:

線程屬性對象類型為pthread_attr_t,結(jié)構(gòu)體定義如下:

typedef struct{
 int etachstate;    // 線程分離的狀態(tài)
 int schedpolicy;    // 線程調(diào)度策略
 struct sched_param schedparam;    // 線程的調(diào)度參數(shù)
 int inheritsched;    // 線程的繼承性
 int scope;    // 線程的作用域
 // 以下為線程棧的設(shè)置
 size_t guardsize;    // 線程棧末尾警戒緩沖大小
 int stackaddr_set;    // 線程的棧設(shè)置
 void *    stackaddr;    // 線程棧的位置
 size_t stacksize;    // 線程棧大小
}pthread_arrt_t;

相關(guān)接口:

對上述結(jié)構(gòu)體中各參數(shù)大多有:pthread_attr_get()和pthread_attr_set()系統(tǒng)調(diào)用函數(shù)來設(shè)置和獲取。這里不一一羅列。

多進程

每一個進程是資源分配的基本單位。

進程結(jié)構(gòu)由以下幾個部分組成:代碼段、堆棧段、數(shù)據(jù)段。代碼段是靜態(tài)的二進制代碼,多個程序可以共享。

實際上在父進程創(chuàng)建子進程之后,父、子進程除了pid外,幾乎所有的部分幾乎一樣。

父、子進程共享全部數(shù)據(jù),但并不是說他們就是對同一塊數(shù)據(jù)進行操作,子進程在讀寫數(shù)據(jù)時會通過寫時復制機制將公共的數(shù)據(jù)重新拷貝一份,之后在拷貝出的數(shù)據(jù)上進行操作。

如果子進程想要運行自己的代碼段,還可以通過調(diào)用execv()函數(shù)重新加載新的代碼段,之后就和父進程獨立開了。

我們在shell中執(zhí)行程序就是通過shell進程先fork()一個子進程再通過execv()重新加載新的代碼段的過程。

進程創(chuàng)建與結(jié)束

背景知識:

進程有兩種創(chuàng)建方式,一種是操作系統(tǒng)創(chuàng)建的一種是父進程創(chuàng)建的。從計算機啟動到終端執(zhí)行程序的過程為:0號進程 -> 1號內(nèi)核進程 -> 1號用戶進程(init進程) -> getty進程 -> shell進程 -> 命令行執(zhí)行進程。所以我們在命令行中通過 ./program執(zhí)行可執(zhí)行文件時,所有創(chuàng)建的進程都是shell進程的子進程,這也就是為什么shell一關(guān)閉,在shell中執(zhí)行的進程都自動被關(guān)閉的原因。從shell進程到創(chuàng)建其他子進程需要通過以下接口。

相關(guān)接口:

創(chuàng)建進程:pid_t fork(void);

返回值:出錯返回-1;父進程中返回pid > 0;子進程中pid == 0

結(jié)束進程:void exit(int status);

status是退出狀態(tài),保存在全局變量中S?,通常0表示正常退出。

獲得PID:pid_t getpid(void);

返回調(diào)用者pid。

獲得父進程PID:pid_t getppid(void);

返回父進程pid。

其他補充:

正常退出方式:exit()、_exit()、return(在main中)。

exit()和_exit()區(qū)別:exit()是對__exit()的封裝,都會終止進程并做相關(guān)收尾工作,最主要的區(qū)別是_exit()函數(shù)關(guān)閉全部描述符和清理函數(shù)后不會刷新流,但是exit()會在調(diào)用_exit()函數(shù)前刷新數(shù)據(jù)流。

return和exit()區(qū)別:exit()是函數(shù),但有參數(shù),執(zhí)行完之后控制權(quán)交給系統(tǒng)。return若是在調(diào)用函數(shù)中,執(zhí)行完之后控制權(quán)交給調(diào)用進程,若是在main函數(shù)中,控制權(quán)交給系統(tǒng)。

異常退出方式:abort()、終止信號。

Linux進程控制

進程地址空間(地址空間)

虛擬存儲器為每個進程提供了獨占系統(tǒng)地址空間的假象。

盡管每個進程地址空間內(nèi)容不盡相同,但是他們的都有相似的結(jié)構(gòu)。X86 Linux進程的地址空間底部是保留給用戶程序的,包括文本、數(shù)據(jù)、堆、棧等,其中文本區(qū)和數(shù)據(jù)區(qū)是通過存儲器映射方式將磁盤中可執(zhí)行文件的相應(yīng)段映射至虛擬存儲器地址空間中。

有一些"敏感"的地址需要注意下,對于32位進程來說,代碼段從0x08048000開始。從0xC0000000開始到0xFFFFFFFF是內(nèi)核地址空間,通常情況下代碼運行在用戶態(tài)(使用0x00000000 ~ 0xC00000000的用戶地址空間),當發(fā)生系統(tǒng)調(diào)用、進程切換等操作時CPU控制寄存器設(shè)置模式位,進入內(nèi)和模式,在該狀態(tài)(超級用戶模式)下進程可以訪問全部存儲器位置和執(zhí)行全部指令。

也就說32位進程的地址空間都是4G,但用戶態(tài)下只能訪問低3G的地址空間,若要訪問3G ~ 4G的地址空間則只有進入內(nèi)核態(tài)才行。

進程控制塊(處理機)

進程的調(diào)度實際就是內(nèi)核選擇相應(yīng)的進程控制塊,被選擇的進程控制塊中包含了一個進程基本的信息。

上下文切換

內(nèi)核管理所有進程控制塊,而進程控制塊記錄了進程全部狀態(tài)信息。每一次進程調(diào)度就是一次上下文切換,所謂的上下文本質(zhì)上就是當前運行狀態(tài),主要包括通用寄存器、浮點寄存器、狀態(tài)寄存器、程序計數(shù)器、用戶棧和內(nèi)核數(shù)據(jù)結(jié)構(gòu)(頁表、進程表、文件表)等。

進程執(zhí)行時刻,內(nèi)核可以決定搶占當前進程并開始新的進程,這個過程由內(nèi)核調(diào)度器完成,當調(diào)度器選擇了某個進程時稱為該進程被調(diào)度,該過程通過上下文切換來改變當前狀態(tài)。

一次完整的上下文切換通常是進程原先運行于用戶態(tài),之后因系統(tǒng)調(diào)用或時間片到切換到內(nèi)核態(tài)執(zhí)行內(nèi)核指令,完成上下文切換后回到用戶態(tài),此時已經(jīng)切換到進程B。

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

發(fā)表評論

0條評論,0人參與

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

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

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

暫無評論

暫無評論

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

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