linux c进程和线程脑图,进程和线程
關(guān)于進程和線程,你需要理解下面這張腦圖中的重點
進程
操作系統(tǒng)中最核心的概念就是?進程,進程是對正在運行中的程序的一個抽象。操作系統(tǒng)的其他所有內(nèi)容都是圍繞著進程展開的。
在多道程序處理的系統(tǒng)中,CPU 會在進程間快速切換,使每個程序運行幾十或者幾百毫秒。然而,嚴(yán)格意義來說,在某一個瞬間,CPU 只能運行一個進程,然而我們?nèi)绻褧r間定位為 1 秒內(nèi)的話,它可能運行多個進程。這樣就會讓我們產(chǎn)生并行的錯覺。因為 CPU 執(zhí)行速度很快,進程間的換進換出也非常迅速,因此我們很難對多個并行進程進行跟蹤。所以,操作系統(tǒng)的設(shè)計者開發(fā)了用于描述并行的一種概念模型(順序進程),使得并行更加容易理解和分析。
進程模型
一個進程就是一個正在執(zhí)行的程序的實例,進程也包括程序計數(shù)器、寄存器和變量的當(dāng)前值。從概念上來說,每個進程都有各自的虛擬 CPU,但是實際情況是 CPU 會在各個進程之間進行來回切換。
如上圖所示,這是一個具有 4 個程序的多道處理程序,在進程不斷切換的過程中,程序計數(shù)器也在不同的變化。
在上圖中,這 4 道程序被抽象為 4 個擁有各自控制流程(即每個自己的程序計數(shù)器)的進程,并且每個程序都獨立的運行。當(dāng)然,實際上只有一個物理程序計數(shù)器,每個程序要運行時,其邏輯程序計數(shù)器會裝載到物理程序計數(shù)器中。當(dāng)程序運行結(jié)束后,其物理程序計數(shù)器就會是真正的程序計數(shù)器,然后再把它放回進程的邏輯計數(shù)器中。
從下圖我們可以看到,在觀察足夠長的一段時間后,所有的進程都運行了,「但在任何一個給定的瞬間僅有一個進程真正運行」。
因此,當(dāng)我們說一個 CPU 只能真正一次運行一個進程的時候,即使有 2 個核(或 CPU),「每一個核也只能一次運行一個線程」。
由于 CPU 會在各個進程之間來回快速切換,所以每個進程在 CPU 中的運行時間是無法確定的。并且當(dāng)同一個進程再次在 CPU 中運行時,其在 CPU 內(nèi)部的運行時間往往也是不固定的。
這里的關(guān)鍵思想是認(rèn)識到一個進程所需的條件,進程是某一類特定活動的總和,它有程序、輸入輸出以及狀態(tài)。
進程的創(chuàng)建
操作系統(tǒng)需要一些方式來創(chuàng)建進程。下面是一些創(chuàng)建進程的方式:系統(tǒng)初始化(init):啟動操作系統(tǒng)時,通常會創(chuàng)建若干個進程。
正在運行的程序執(zhí)行了創(chuàng)建進程的系統(tǒng)調(diào)用(比如 fork)
用戶請求創(chuàng)建一個新進程:在許多交互式系統(tǒng)中,輸入一個命令或者雙擊圖標(biāo)就可以啟動程序,以上任意一種操作都可以選擇開啟一個新的進程,在基本的 UNIX 系統(tǒng)中運行 X,新進程將接管啟動它的窗口。
初始化一個批處理工作
從技術(shù)上講,在所有這些情況下,讓現(xiàn)有流程執(zhí)行流程是通過創(chuàng)建系統(tǒng)調(diào)用來創(chuàng)建新流程的。該進程可能是正在運行的用戶進程,是從鍵盤或鼠標(biāo)調(diào)用的系統(tǒng)進程或批處理程序。這些就是系統(tǒng)調(diào)用創(chuàng)建新進程的過程。該系統(tǒng)調(diào)用告訴操作系統(tǒng)創(chuàng)建一個新進程,并直接或間接指示在其中運行哪個程序。
在 UNIX 中,僅有一個系統(tǒng)調(diào)用來創(chuàng)建一個新的進程,這個系統(tǒng)調(diào)用就是?fork。這個調(diào)用會創(chuàng)建一個與調(diào)用進程相關(guān)的副本。在 fork 后,一個父進程和子進程會有相同的內(nèi)存映像,相同的環(huán)境字符串和相同的打開文件。
在 Windows 中,情況正相反,一個簡單的 Win32 功能調(diào)用?CreateProcess,會處理流程創(chuàng)建并將正確的程序加載到新的進程中。這個調(diào)用會有 10 個參數(shù),包括了需要執(zhí)行的程序、輸入給程序的命令行參數(shù)、各種安全屬性、有關(guān)打開的文件是否繼承控制位、優(yōu)先級信息、進程所需要創(chuàng)建的窗口規(guī)格以及指向一個結(jié)構(gòu)的指針,在該結(jié)構(gòu)中新創(chuàng)建進程的信息被返回給調(diào)用者。「在 Windows 中,從一開始父進程的地址空間和子進程的地址空間就是不同的」。
進程的終止
進程在創(chuàng)建之后,它就開始運行并做完成任務(wù)。然而,沒有什么事兒是永不停歇的,包括進程也一樣。進程早晚會發(fā)生終止,但是通常是由于以下情況觸發(fā)的
正常退出(自愿的)?:多數(shù)進程是由于完成了工作而終止。當(dāng)編譯器完成了所給定程序的編譯之后,編譯器會執(zhí)行一個系統(tǒng)調(diào)用告訴操作系統(tǒng)它完成了工作。這個調(diào)用在 UNIX 中是?exit?,在 Windows 中是?ExitProcess。
錯誤退出(自愿的):比如執(zhí)行一條不存在的命令,于是編譯器就會提醒并退出。
嚴(yán)重錯誤(非自愿的)
被其他進程殺死(非自愿的)?:某個進程執(zhí)行系統(tǒng)調(diào)用告訴操作系統(tǒng)殺死某個進程。在 UNIX 中,這個系統(tǒng)調(diào)用是 kill。在 Win32 中對應(yīng)的函數(shù)是?TerminateProcess(注意不是系統(tǒng)調(diào)用)。
進程的層次結(jié)構(gòu)
在一些系統(tǒng)中,當(dāng)一個進程創(chuàng)建了其他進程后,父進程和子進程就會以某種方式進行關(guān)聯(lián)。子進程它自己就會創(chuàng)建更多進程,從而形成一個進程層次結(jié)構(gòu)。
UNIX 進程體系
在 UNIX 中,進程和它的所有子進程以及子進程的子進程共同組成一個進程組。當(dāng)用戶從鍵盤中發(fā)出一個信號后,該信號被發(fā)送給當(dāng)前與鍵盤相關(guān)的進程組中的所有成員(它們通常是在當(dāng)前窗口創(chuàng)建的所有活動進程)。每個進程可以分別捕獲該信號、忽略該信號或采取默認(rèn)的動作,即被信號 kill 掉。整個操作系統(tǒng)中所有的進程都隸屬于一個單個以 init 為根的進程樹。
Windows 進程體系
相反,Windows 中沒有進程層次的概念,Windows 中所有進程都是平等的,唯一類似于層次結(jié)構(gòu)的是在創(chuàng)建進程的時候,父進程得到一個特別的令牌(稱為句柄),該句柄可以用來控制子進程。然而,這個令牌可能也會移交給別的操作系統(tǒng),這樣就不存在層次結(jié)構(gòu)了。而在 UNIX 中,進程不能剝奪其子進程的?進程權(quán)。(這樣看來,還是 Windows 比較渣)。
進程狀態(tài)
盡管每個進程是一個獨立的實體,有其自己的程序計數(shù)器和內(nèi)部狀態(tài),但是,進程之間仍然需要相互幫助。當(dāng)一個進程開始運行時,它可能會經(jīng)歷下面這幾種狀態(tài)
圖中會涉及三種狀態(tài):運行態(tài),運行態(tài)指的就是進程實際占用 CPU 時間片運行時
就緒態(tài),就緒態(tài)指的是可運行,但因為其他進程正在運行而處于就緒狀態(tài)
阻塞態(tài),除非某種外部事件發(fā)生,否則進程不能運行
進程的實現(xiàn)
操作系統(tǒng)為了執(zhí)行進程間的切換,會維護著一張表,這張表就是?進程表(process table)。每個進程占用一個進程表項。該表項包含了進程狀態(tài)的重要信息,包括程序計數(shù)器、堆棧指針、內(nèi)存分配狀況、所打開文件的狀態(tài)、賬號和調(diào)度信息,以及其他在進程由運行態(tài)轉(zhuǎn)換到就緒態(tài)或阻塞態(tài)時所必須保存的信息。
下面展示了一個典型系統(tǒng)中的關(guān)鍵字段
典型的進程表表項中的一些字段
第一列內(nèi)容與進程管理有關(guān),第二列內(nèi)容與?存儲管理有關(guān),第三列內(nèi)容與文件管理有關(guān)。
現(xiàn)在我們應(yīng)該對進程表有個大致的了解了,就可以在對單個 CPU 上如何運行多個順序進程的錯覺做更多的解釋。與每一 I/O 類相關(guān)聯(lián)的是一個稱作?中斷向量(interrupt vector)?的位置(靠近內(nèi)存底部的固定區(qū)域)。它包含中斷服務(wù)程序的入口地址。假設(shè)當(dāng)一個磁盤中斷發(fā)生時,用戶進程 3 正在運行,則中斷硬件將程序計數(shù)器、程序狀態(tài)字、有時還有一個或多個寄存器壓入堆棧,計算機隨即跳轉(zhuǎn)到中斷向量所指示的地址。這就是硬件所做的事情。然后軟件就隨即接管一切剩余的工作。
當(dāng)中斷結(jié)束后,操作系統(tǒng)會調(diào)用一個 C 程序來處理中斷剩下的工作。在完成剩下的工作后,會使某些進程就緒,接著調(diào)用調(diào)度程序,決定隨后運行哪個進程。然后將控制權(quán)轉(zhuǎn)移給一段匯編語言代碼,為當(dāng)前的進程裝入寄存器值以及內(nèi)存映射并啟動該進程運行,下面顯示了中斷處理和調(diào)度的過程。
硬件壓入堆棧程序計數(shù)器等
硬件從中斷向量裝入新的程序計數(shù)器
匯編語言過程保存寄存器的值
匯編語言過程設(shè)置新的堆棧
C 中斷服務(wù)器運行(典型的讀和緩存寫入)
調(diào)度器決定下面哪個程序先運行
C 過程返回至匯編代碼
匯編語言過程開始運行新的當(dāng)前進程
一個進程在執(zhí)行過程中可能被中斷數(shù)千次,但關(guān)鍵每次中斷后,被中斷的進程都返回到與中斷發(fā)生前完全相同的狀態(tài)。
線程
在傳統(tǒng)的操作系統(tǒng)中,每個進程都有一個地址空間和一個控制線程。事實上,這是大部分進程的定義。不過,在許多情況下,經(jīng)常存在同一地址空間中運行多個控制線程的情形,這些線程就像是分離的進程。下面我們就著重探討一下什么是線程
線程的使用
或許這個疑問也是你的疑問,為什么要在進程的基礎(chǔ)上再創(chuàng)建一個線程的概念,準(zhǔn)確的說,這其實是進程模型和線程模型的討論,回答這個問題,可能需要分三步來回答:
多線程之間會共享同一塊地址空間和所有可用數(shù)據(jù)的能力,這是進程所不具備的
線程要比進程更輕量級,由于線程更輕,所以它比進程更容易創(chuàng)建,也更容易撤銷。在許多系統(tǒng)中,創(chuàng)建一個線程要比創(chuàng)建一個進程快 10 - 100 倍。
第三個原因可能是性能方面的探討,如果多個線程都是 CPU 密集型的,那么并不能獲得性能上的增強,但是如果存在著大量的計算和大量的 I/O 處理,擁有多個線程能在這些活動中彼此重疊進行,從而會加快應(yīng)用程序的執(zhí)行速度
經(jīng)典的線程模型
進程中擁有一個執(zhí)行的線程,通常簡寫為?線程(thread)。線程會有程序計數(shù)器,用來記錄接著要執(zhí)行哪一條指令;線程實際上 CPU 上調(diào)度執(zhí)行的實體。
下圖我們可以看到三個傳統(tǒng)的進程,每個進程有自己的地址空間和單個控制線程。每個線程都在不同的地址空間中運行
下圖中,我們可以看到有一個進程三個線程的情況。每個線程都在相同的地址空間中運行。
線程不像是進程那樣具備較強的獨立性。同一個進程中的所有線程都會有完全一樣的地址空間,這意味著它們也共享同樣的全局變量。由于每個線程都可以訪問進程地址空間內(nèi)每個內(nèi)存地址,「因此一個線程可以讀取、寫入甚至擦除另一個線程的堆棧」。線程之間除了共享同一內(nèi)存空間外,還具有如下不同的內(nèi)容:
上圖左邊的是同一個進程中每個線程共享的內(nèi)容,上圖右邊是每個線程中的內(nèi)容。也就是說左邊的列表是進程的屬性,右邊的列表是線程的屬性。
「線程之間的狀態(tài)轉(zhuǎn)換和進程之間的狀態(tài)轉(zhuǎn)換是一樣的」。
每個線程都會有自己的堆棧,如下圖所示:
線程系統(tǒng)調(diào)用
進程通常會從當(dāng)前的某個單線程開始,然后這個線程通過調(diào)用一個庫函數(shù)(比如?thread_create)創(chuàng)建新的線程。線程創(chuàng)建的函數(shù)會要求指定新創(chuàng)建線程的名稱。創(chuàng)建的線程通常都返回一個線程標(biāo)識符,該標(biāo)識符就是新線程的名字。
當(dāng)一個線程完成工作后,可以通過調(diào)用一個函數(shù)(比如?thread_exit)來退出。緊接著線程消失,狀態(tài)變?yōu)榻K止,不能再進行調(diào)度。在某些線程的運行過程中,可以通過調(diào)用函數(shù)例如?thread_join?,表示一個線程可以等待另一個線程退出。這個過程阻塞調(diào)用線程直到等待特定的線程退出。在這種情況下,線程的創(chuàng)建和終止非常類似于進程的創(chuàng)建和終止。
另一個常見的線程是調(diào)用?thread_yield,它允許線程自動放棄 CPU 從而讓另一個線程運行。這樣一個調(diào)用還是很重要的,因為不同于進程,線程是無法利用時鐘中斷強制讓線程讓出 CPU 的。
POSIX 線程
POSIX 線程 通常稱為 pthreads是一種獨立于語言而存在的執(zhí)行模型,以及并行執(zhí)行模型。
它允許程序控制時間上重疊的多個不同的工作流程。每個工作流程都稱為一個線程,可以通過調(diào)用 POSIX Threads API 來實現(xiàn)對這些流程的創(chuàng)建和控制。可以把它理解為線程的標(biāo)準(zhǔn)。
?
POSIX Threads 的實現(xiàn)在許多類似且符合POSIX的操作系統(tǒng)上可用,例如「FreeBSD、NetBSD、OpenBSD、Linux、macOS、Android、Solaris」,它在現(xiàn)有 Windows API 之上實現(xiàn)了「pthread」。
IEEE 是世界上最大的技術(shù)專業(yè)組織,致力于為人類的利益而發(fā)展技術(shù)。
?
線程調(diào)用描述
pthread_create創(chuàng)建一個新線程
pthread_exit結(jié)束調(diào)用的線程
pthread_join等待一個特定的線程退出
pthread_yield釋放 CPU 來運行另外一個線程
pthread_attr_init創(chuàng)建并初始化一個線程的屬性結(jié)構(gòu)
pthread_attr_destory刪除一個線程的屬性結(jié)構(gòu)
所有的 Pthreads 都有特定的屬性,每一個都含有標(biāo)識符、一組寄存器(包括程序計數(shù)器)和一組存儲在結(jié)構(gòu)中的屬性。這個屬性包括堆棧大小、調(diào)度參數(shù)以及其他線程需要的項目。
線程實現(xiàn)
主要有三種實現(xiàn)方式
在用戶空間中實現(xiàn)線程;
在內(nèi)核空間中實現(xiàn)線程;
在用戶和內(nèi)核空間中混合實現(xiàn)線程。
下面我們分開討論一下
在用戶空間中實現(xiàn)線程
第一種方法是把整個線程包放在用戶空間中,內(nèi)核對線程一無所知,它不知道線程的存在。所有的這類實現(xiàn)都有同樣的通用結(jié)構(gòu)
在用戶空間中實現(xiàn)多線程
線程在運行時系統(tǒng)之上運行,運行時系統(tǒng)是管理線程過程的集合,包括前面提到的四個過程:pthread_create, pthread_exit, pthread_join 和 pthread_yield。
在內(nèi)核中實現(xiàn)線程
當(dāng)某個線程希望創(chuàng)建一個新線程或撤銷一個已有線程時,它會進行一個系統(tǒng)調(diào)用,這個系統(tǒng)調(diào)用通過對線程表的更新來完成線程創(chuàng)建或銷毀工作。
在內(nèi)核中實現(xiàn)多線程
內(nèi)核中的線程表持有每個線程的寄存器、狀態(tài)和其他信息。這些信息和用戶空間中的線程信息相同,但是位置卻被放在了內(nèi)核中而不是用戶空間中。另外,內(nèi)核還維護了一張進程表用來跟蹤系統(tǒng)狀態(tài)。
所有能夠阻塞的調(diào)用都會通過系統(tǒng)調(diào)用的方式來實現(xiàn),當(dāng)一個線程阻塞時,內(nèi)核可以進行選擇,是運行在同一個進程中的另一個線程(如果有就緒線程的話)還是運行一個另一個進程中的線程。但是在用戶實現(xiàn)中,運行時系統(tǒng)始終運行自己的線程,直到內(nèi)核剝奪它的
CPU 時間片(或者沒有可運行的線程存在了)為止。
混合實現(xiàn)
結(jié)合用戶空間和內(nèi)核空間的優(yōu)點,設(shè)計人員采用了一種內(nèi)核級線程的方式,然后將用戶級線程與某些或者全部內(nèi)核線程多路復(fù)用起來
用戶線程與內(nèi)核線程的多路復(fù)用
在這種模型中,編程人員可以自由控制用戶線程和內(nèi)核線程的數(shù)量,具有很大的靈活度。采用這種方法,內(nèi)核只識別內(nèi)核級線程,并對其進行調(diào)度。其中一些內(nèi)核級線程會被多個用戶級線程多路復(fù)用。
總結(jié)
以上是生活随笔為你收集整理的linux c进程和线程脑图,进程和线程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 广告商为何还是不回推特打广告,行业高管称
- 下一篇: linux系统网络对时,Linux系统网