linux系統(tǒng)編程-day09-進(jìn)程管理(1)

進(jìn)程是Unix系統(tǒng)中僅次于文件的基本抽象概念琳猫。當(dāng)目標(biāo)代碼執(zhí)行的時候,進(jìn)程不僅僅包括匯編代碼私痹,它由數(shù)據(jù)脐嫂、資源、狀態(tài)和一個虛擬的計算機(jī)組成紊遵。

  • 進(jìn)程ID
    每個進(jìn)程都由一個唯一的標(biāo)識符表示账千,即進(jìn)程ID,簡稱pid暗膜。系統(tǒng)保證在某時刻每個pid是唯一的匀奏。
    空閑進(jìn)程(idle process)——當(dāng)沒有其他進(jìn)程在運行時,內(nèi)核所運行的進(jìn)程——它的pid是0学搜。在啟動后娃善,內(nèi)核運行的第一個進(jìn)程稱為init進(jìn)程,它的pid是1瑞佩。
  • 分配進(jìn)程ID
    缺省情況下聚磺,內(nèi)核將進(jìn)程ID的最大值限制為32768(和老Unix系統(tǒng)兼容,16位來表示進(jìn)程ID)炬丸。
    內(nèi)核分配進(jìn)程ID是以嚴(yán)格的線性函數(shù)的方式進(jìn)行的瘫寝, 一直遞增,直到分配的pid達(dá)到了/proc/sys/kernel/pid_max稠炬,內(nèi)核是不會重用以前已分配過的值焕阿。
    Linux分配pid的方式在短期內(nèi)至少是穩(wěn)定的和并保證了pid值的唯一性。
  • 進(jìn)程體系
    創(chuàng)建新進(jìn)程的那個進(jìn)程叫父進(jìn)程酸纲,而新進(jìn)程被稱為子進(jìn)程捣鲸。每個進(jìn)程都是由其他進(jìn)程創(chuàng)建的(除了init進(jìn)程),因此每個子進(jìn)程都有一個父進(jìn)程闽坡,這個關(guān)系保存在每個進(jìn)程的父進(jìn)程ID號(ppid)中栽惶。
    每個進(jìn)程都被一個用戶和組擁有。每個子進(jìn)程都繼承了父進(jìn)程的用戶和組疾嗅。
    每個進(jìn)程都是某個進(jìn)程組的一部分外厂,它簡單的表明了自己和其他進(jìn)程之間的關(guān)系。子進(jìn)程通常屬于其父進(jìn)程所在的那個進(jìn)程組代承。
  • 獲得進(jìn)程ID和父進(jìn)程的ID
    getpid( )返回調(diào)用進(jìn)程的ID
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);

getppid( )返回調(diào)用進(jìn)程的父進(jìn)程的ID

#include <sys/types.h>
#include <unistd.h>
pid_t getppid(void);

一般把pid_t當(dāng)成int形來printf汁蝶。

運行新進(jìn)程

在unix中,載入內(nèi)存并執(zhí)行程序映像的操作創(chuàng)建一個新進(jìn)程分離的。
運行一個新進(jìn)程:將二進(jìn)制文件的程序映像載入內(nèi)存掖棉,替換原先進(jìn)程的地址空間墓律,并開始運行它,該系統(tǒng)調(diào)用為exec系統(tǒng)調(diào)用(實際上是一系列的系統(tǒng)調(diào)用)幔亥。
創(chuàng)建一個新的進(jìn)程:基本上就是復(fù)制父進(jìn)程耻讽。通常情況下新的進(jìn)程會立刻執(zhí)行一個新的程序。完成創(chuàng)建新進(jìn)程的這種行為叫做派生(fork)帕棉,完成這個功能的系統(tǒng)調(diào)用就是fork( )针肥。

exec系列系統(tǒng)調(diào)用

沒有單一的exec系統(tǒng)調(diào)用,它們由基于單個系統(tǒng)調(diào)用的一組exec函數(shù)構(gòu)成香伴。

  • execl( ):
#include <unistd.h>
int execl(const char *path, const char *arg, ...);

對execl( )的調(diào)用會將path所指路徑的映像載入內(nèi)存慰枕,替換當(dāng)前進(jìn)程的映像。它的參數(shù)列表是可變長度的即纲,但參數(shù)列表必須是以NULL結(jié)尾的具帮。
例如,下面的代碼會用/bin/vi替換當(dāng)前運行的程序:

int ret;
ret = execl("/bin/vi", "vi", NULL);
if (ret == -1)
    perror("execl");

當(dāng)fork/exec進(jìn)程時崇裁,shell會把path的后一個成分匕坯,即本例中的"vi",放入新進(jìn)程的第一個參數(shù)argv[0]拔稳。這樣一個程序就可以檢測argv[0]葛峻,從而得知二進(jìn)制映像文件的名字。
很多情況下术奖,用戶會看到一些系統(tǒng)工具有不同的名字,實際上這些名字都是指向同一個程序的硬連接轻绞。所以程序需要第一個參數(shù)來決定它的具體行為采记。
如果你想要編輯/home/kidd/hooks.txt,執(zhí)行以下代碼:

int ret;
ret = execl("/bin/vi", "vi", "/home/kidd/hooks.txt", NULL);
if (ret == -1)
  perror("execl");

execl( )成功調(diào)用的話不會返回政勃,而是以跳到新的程序的入口點為結(jié)束唧龄,而剛剛才被運行的代碼是不會存在于進(jìn)程的地址空間中的。
execl( )成功的調(diào)用不僅僅改變了地址空間和進(jìn)程的映像奸远,還改變了進(jìn)程的一些屬性:

  • 任何掛起的信號都會丟失
  • 捕捉的任何信號會還原為缺省的處理方式既棺,因為信號處理函數(shù)已經(jīng)不存在于地址空間中了
  • 任何內(nèi)存的鎖定會丟失
  • 多數(shù)線程的屬性會還原到缺省值
  • 多數(shù)關(guān)于進(jìn)程的統(tǒng)計信息會復(fù)位
  • 與進(jìn)程內(nèi)存相關(guān)的任何數(shù)據(jù)都會丟失,包括映射的文件
  • 包括C語言庫的一些特征(例如atexit( ))等獨立存在于用戶空間的數(shù)據(jù)都會丟失

然而也有很多進(jìn)程的屬性沒有改變懒叛,例如pid丸冕、ppid、優(yōu)先級薛窥、所屬的用戶和組胖烛。

  • 其他exec系列系統(tǒng)調(diào)用
    除了execl( )外眼姐,還有其他五個系統(tǒng)調(diào)用:
#include <unistd.h>
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[ ]);
int execv(const char *path, char *const argv[ ]);
int evecvp(const char *file, char *const argv[ ]);
int execve(const char *filename, char *const argv[ ], char *const envp[ ]);
  • l:列表方式 v:數(shù)組(向量)方式提供參數(shù)
  • p:在用戶的PATH環(huán)境變量中尋找可執(zhí)行文件,只要出現(xiàn)在用戶的路徑中佩番,帶p的exec函數(shù)可以簡單的只提供文件名众旗。帶p的exec函數(shù)主要用于shell的,因為在shell所執(zhí)行的進(jìn)程通常會從shell繼承環(huán)境變量趟畏。
  • e:表示會提供給新進(jìn)程以新的環(huán)境變量逝钥。
  • 除了需要構(gòu)造一個數(shù)組并用它代替列表作為參數(shù)傳遞以外,使用數(shù)組作為參數(shù)的exec函數(shù)基本上沒什么區(qū)別拱镐。使用數(shù)組可以在運行時動態(tài)的構(gòu)造參數(shù),且數(shù)組也必須以NULL結(jié)尾持际。

用execvp( )來執(zhí)行vi的例子:

const char *args[ ] =  {"vi", "/home/kidd/hooks.txt", NULL};
int ret;
/* 這里假設(shè)/bin在用戶的路徑中 */
ret = execvp("vi", args);
if (ret == -1)
  perror ("evecvp");

fork( )系統(tǒng)調(diào)用

創(chuàng)建一個和當(dāng)前進(jìn)程映像一樣的進(jìn)程可以通過fork( )系統(tǒng)調(diào)用:

#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);

成功調(diào)用fork( )會創(chuàng)建一個新的進(jìn)程沃琅,它幾乎與調(diào)用fork( )的進(jìn)程一模一樣。這兩個進(jìn)程都會繼續(xù)運行蜘欲。
父進(jìn)程和子進(jìn)程在每個方面都非常相近:

  • 子進(jìn)程的pid是新分配的益眉,它是與父進(jìn)程不同的
  • 子進(jìn)程的ppid會設(shè)置為父進(jìn)程的pid
  • 子進(jìn)程中的資源統(tǒng)計信息會清零
  • 任何掛起的信號都會清除,也不會被子進(jìn)程繼承
  • 任何文件鎖都不會被子進(jìn)程所繼承

fork( )的用法如下:

pid_t pid;
pid = fork( );
if (pid > 0)
  /* 在父進(jìn)程中fork( )返回子進(jìn)程的pid */
  printf("I am the parent of pid = %d \n", pid);
else if (!pid)
  /* 成功調(diào)用時會返回0 */
  printf("I am the baby! \n");
else if (pid == -1)
  /* 錯誤時返回-1 */
  perror("fork");

最常見的fork( )用法是創(chuàng)建一個新的進(jìn)程姥份,然后載入二進(jìn)制映像郭脂,這種派生加執(zhí)行的方式是很常見的。下面的例子創(chuàng)建了一個新的進(jìn)程來運行/bin/windlass:

pid_t pid;
pid = fork( );
if (pid == -1)
  perror("fork");
/* the child ... */
if (!pid) {
  const char *args[] = {"windlass", NULL};
  int ret;
  ret = execv("/bin/windlass", args);
  if (ret == -1) {
    perror("execv");
    exit(EXIT_FAILURE);
  }
}

  • 寫時復(fù)制
    早期的Unix系統(tǒng)中澈歉,fork時會把所有的內(nèi)部數(shù)據(jù)結(jié)構(gòu)復(fù)制一份展鸡,復(fù)制進(jìn)程的頁表項,然后把父進(jìn)程的地址空間中的內(nèi)容逐頁的復(fù)制到子進(jìn)程的地址空間中埃难。這樣是十分耗時且不必要的。
    現(xiàn)代Unix系統(tǒng)采用寫時復(fù)制COW的方法。
    如果多個進(jìn)程要讀取它們自己的那部分資源的副本年缎,那么每個進(jìn)程只需要保存一個指向這個資源的指針就可以了歼争。如果一個進(jìn)程要“修改”自己的那份資源“副本”,那么就會復(fù)制那份資源考抄,并把復(fù)制的那份提供給進(jìn)程细疚。這就是寫入時復(fù)制。
    子進(jìn)程們共享父進(jìn)程的原始頁川梅,接下來這些頁又可以被其他的父進(jìn)程或者子進(jìn)程共享疯兼。
    寫時復(fù)制在內(nèi)核中的實現(xiàn)很簡單,要修改時挑势,產(chǎn)生缺頁中斷镇防,內(nèi)核處理缺頁中斷的方式就是對該頁進(jìn)行一次透明復(fù)制,這時會清楚頁面的COW屬性潮饱,表示著它不再被共享来氧。
    現(xiàn)在的計算機(jī)結(jié)構(gòu)體系中都在內(nèi)存管理單元MMU提供了硬件級別的COW支持,所以實現(xiàn)是很容易的。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末啦扬,一起剝皮案震驚了整個濱河市中狂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌扑毡,老刑警劉巖胃榕,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瞄摊,居然都是意外死亡勋又,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門换帜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來楔壤,“玉大人,你說我怎么就攤上這事惯驼《紫” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵祟牲,是天一觀的道長隙畜。 經(jīng)常有香客問我,道長说贝,這世上最難降的妖魔是什么议惰? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮乡恕,結(jié)果婚禮上换淆,老公的妹妹穿的比我還像新娘。我一直安慰自己几颜,他們只是感情好倍试,可當(dāng)我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蛋哭,像睡著了一般县习。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谆趾,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天躁愿,我揣著相機(jī)與錄音,去河邊找鬼沪蓬。 笑死彤钟,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的跷叉。 我是一名探鬼主播逸雹,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼营搅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了梆砸?” 一聲冷哼從身側(cè)響起转质,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎帖世,沒想到半個月后休蟹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡日矫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年赂弓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哪轿。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡拣展,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出缔逛,到底是詐尸還是另有隱情,我是刑警寧澤姓惑,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布褐奴,位于F島的核電站,受9級特大地震影響于毙,放射性物質(zhì)發(fā)生泄漏敦冬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一唯沮、第九天 我趴在偏房一處隱蔽的房頂上張望脖旱。 院中可真熱鬧,春花似錦介蛉、人聲如沸萌庆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽践险。三九已至,卻和暖如春吹菱,著一層夾襖步出監(jiān)牢的瞬間巍虫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工鳍刷, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留占遥,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓输瓜,卻偏偏與公主長得像瓦胎,于是被迫代替她去往敵國和親芬萍。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容