版權(quán)聲明:本文為 cdeveloper 原創(chuàng)文章,可以隨意轉(zhuǎn)載,但必須在明確位置注明出處忌傻!
進程基本概念
這篇文章帶你了解 Linux 進程的基本概念沉唠,不解釋了疆虚,快點上車...
程序和進程
先看看程序和進程的區(qū)別梭冠,程序是一個磁盤上的文件蓝丙,而進程通常被定義為一個正在運行的程序?qū)嵗?/strong>,進程由兩部分組成:
- 操作系統(tǒng)用來管理進程的內(nèi)核對象:內(nèi)核對象用來存放進程的統(tǒng)計信息
- 獨立的內(nèi)存地址空間:包含可執(zhí)行模塊的代碼和數(shù)據(jù)俘种,還包含分配的內(nèi)存空間嘀韧,例如堆篇亭,棧
進程運行過程
運行進程可以簡單的理解為:計算機將磁盤的二進制程序加載到內(nèi)存空間中,并且指引 CPU 在內(nèi)存中尋址锄贷,然后計算的過程:
- 將磁盤程序裝載到內(nèi)存译蒂,所謂實例化
- 讀取并執(zhí)行內(nèi)存中的程序段內(nèi)容,這個過程會涉及變量分配和內(nèi)存尋址等操作
- 結(jié)束或者繼續(xù)執(zhí)行
進程運行特性
進程的運行特性主要可以包含 3 個方面:
- 多任務:內(nèi)存中可以存在多個進程
- 并發(fā):多個進程可以「并發(fā)」執(zhí)行
- 程序獨立:進程之間互不影響
例如 Linux 系統(tǒng)是多任務谊却,分時系統(tǒng)柔昼,每個進程都有獨立的地址空間,操作系統(tǒng)給每個進程都分配一定的 CPU 執(zhí)行時間片炎辨,然后通過進程調(diào)度(時間片輪轉(zhuǎn)調(diào)度算法)來同時調(diào)度運行多個程序岳锁,因為調(diào)度時間被設(shè)置的非常短,模擬了好像每個程序都獨占地使用 CPU 的狀態(tài)蹦魔。如下圖:
A激率,B,C勿决,D 每個進程都有固定的運行時間片乒躺。比如當進程 B 的時間片用完了,操作系統(tǒng)就保存 B 進程的狀態(tài)低缩,并將 CPU 切換到另外一個進程 A 去運行嘉冒,之后再次輪到 B 進程運行時,操作系統(tǒng)就恢復之前保存的狀態(tài)繼續(xù)執(zhí)行咆繁。這些切換的時間非常短讳推,以至于在單 CPU 上,可以模擬出「并行執(zhí)行」的效果玩般,但是實際上還是順序執(zhí)行银觅。但在多核 CPU 上,每個 CPU 都可以單獨運行一個程序坏为,那樣才是真正的并行執(zhí)行究驴。
進程地址空間 & 虛擬內(nèi)存
因為系統(tǒng)中同時存在多個進程镊绪,為了避免它們相互干擾,需要某種保護機制洒忧。通常每個進程有一些可以使用的內(nèi)存地址集合蝴韭,如果多個進程需要的總內(nèi)存比物理內(nèi)存小,那么可以在內(nèi)存中存放多個進程熙侍,如果有進程需要使用比物理內(nèi)存還要大的內(nèi)存榄鉴,那么在早期這個進程就不能運行了。
但是現(xiàn)在有一種稱為虛擬內(nèi)存的技術(shù)可以解決這個難題:操作系統(tǒng)創(chuàng)建一個地址空間的抽象蛉抓,作為進程可以引用的地址集合庆尘,例如 32 位上是 2^32 = 4 GB
,這個地址空間被分割成一個個的頁面(page)芝雪,常見 4KB 大小。這些頁面被映射到物理內(nèi)存综苔,但并不是所有的頁面都在內(nèi)存中才可以運行程序惩系,而是只加載需要的部分:
- 當進程引用到的內(nèi)容在物理內(nèi)存中,就由硬件映射到進程地址空間中
- 當進程引用到的內(nèi)存不在物理內(nèi)存中如筛,就由操作系統(tǒng)負責將缺失的部分裝入物理內(nèi)存并重新執(zhí)行失敗的指令堡牡,這個過程也叫做「缺頁中斷」
在 32 bit 系統(tǒng)中,進程地址空間大小為 2 ^ 32 = 4 GB
杨刨,具體分配如下圖:
一個進程地址空間主要包含下面這些:
- 代碼段:只讀
- 數(shù)據(jù)段:初始化過的變量
- BSS 段:未初始化的變量
- 堆空間:低地址向高地址增長晤柄,調(diào)用
malloc
分配堆內(nèi)存 - 棧空間:高地址向低地址增長妖胀,自動增長和釋放芥颈,用于存儲臨時變量和函數(shù)調(diào)用
- 內(nèi)存映射空間:比如用于進程間通信(IPC)的共享內(nèi)存文件
其中每個部分都有很多可以學習的,這里只是介紹基本的的概念赚抡。
進程生命周期
進程主要的生命周期有 3 個爬坑,在不同的系統(tǒng)中都是大同小異:
- 運行
- 掛起
- 消亡
整個生命周期可以用下圖來表示:
例如在 Linux 中:
- 進程通過
fork
系統(tǒng)調(diào)用被創(chuàng)建,進入 TASK_RUNNING 準備運行狀態(tài) - 如果此進程被內(nèi)核
schedule
調(diào)度器調(diào)度運行涂臣,則會進入運行狀態(tài)(TASK_RUNNING) - 如果進程睡眠或者等待 IO 則會處于任務掛起狀態(tài)(TASK_INTERRUPTIBLE)或(TASK_UNINTERRUPTIBLE)盾计,這兩者的區(qū)別是能否被中斷喚醒。
- 如果進程退出赁遗,例如調(diào)用
do_exit
則進入消亡狀態(tài)
進程表
在許多操作系統(tǒng)中署辉,與一個進程有關(guān)的所有信息,除了該進程自身地址空間的內(nèi)容以外岩四,均放在操作系統(tǒng)的一張表中哭尝,稱為進程表(process table),進程表是數(shù)組或者鏈表結(jié)構(gòu)剖煌,當前存在的每個進程都要占用其中的一項刚夺。每個表項描述的進程信息有下面 3 類:
- 進程管理信息:比如使用的寄存器狀態(tài)献丑,PSW,進程優(yōu)先級侠姑,父進程创橄,棧指針等等
- 文件管理信息:比如當前工作目錄,用戶 ID莽红,組 ID 等等
- 存儲管理信息:比如常見的進程的代碼段指針妥畏,數(shù)據(jù)段指針等等
進程樹
在 Linux 中一個進程可以創(chuàng)建子進程,子進程也可以創(chuàng)建子進程安吁,這樣就可形成一棵進程樹醉蚁,像下面這樣:
在這個進程樹中,進程 A 創(chuàng)建了它的 2 個子進程 B 和 C鬼店,進程 B 又創(chuàng)建了它的 3 個子進程 D网棍,E,F(xiàn)妇智,這樣就形成了一顆樹滥玷,要注意的是,進程樹的深度一般是 2 - 3 層巍棱,不會太多惑畴。但是在 Windows 中卻沒有子進程和進程樹這些概念,Windows 上的進程都是平等的航徙。
僵尸進程 & 孤兒進程
有時候父子進程之間會出現(xiàn)點異常如贷,例如下面這兩種狀態(tài):
孤兒進程:父進程退出,而它的一個或多個子進程還在運行到踏,那么那些子進程將成為孤兒進程杠袱。孤兒進程將被 init
進程(進程號為 1) 所收養(yǎng),并由 init
進程對它們完成狀態(tài)收集工作窝稿,這樣的進程稱為孤兒進程霞掺。
僵尸進程:一個進程使用 fork
創(chuàng)建子進程,如果子進程退出讹躯,而父進程并沒有調(diào)用 wait
或 waitpid
獲取子進程的狀態(tài)信息菩彬,那么子進程的進程描述符仍然保存在系統(tǒng)中,這樣的進程稱之為僵尸進程潮梯。
結(jié)語
在學習 Linux 的進程之前骗灶,這些概念是需要了解的,這會幫助我們更好的理解和編寫進程相關(guān)的代碼秉馏。進程管理也是內(nèi)核中的一個非常重要的模塊耙旦,畢竟我們天天玩的都是一個個的進程,了解它的原理太有必要了萝究!
最后免都,感謝你的閱讀锉罐,我們下次再見 :)