??文件和文件系統(tǒng)
? ? ? ?文件是Linux系統(tǒng)中最重要的抽象鄙陡,大多數(shù)情況下你可以把linux系統(tǒng)中的任何東西都理解為文件冕房,很多的交互操作其實(shí)都是通過(guò)文件的讀寫(xiě)來(lái)實(shí)現(xiàn)的。
? 文件描述符
? ? ? ?在Linux內(nèi)核中趁矾,文件是用一個(gè)整數(shù)來(lái)表示的耙册,稱(chēng)為?文件描述符,通俗的來(lái)說(shuō)毫捣,你可以理解它是文件的id(唯一標(biāo)識(shí)符)
??普通文件
??????普通文件就是字節(jié)流組織的數(shù)據(jù)详拙。
??????文件并不是通過(guò)和文件名關(guān)聯(lián)來(lái)實(shí)現(xiàn)的帝际,而是通過(guò)關(guān)聯(lián)索引節(jié)點(diǎn)來(lái)實(shí)現(xiàn)的,文件節(jié)點(diǎn)擁有文件系統(tǒng)為普通文件分配的唯一整數(shù)值(ino)饶辙,并且存放著一些文件的相關(guān)元數(shù)據(jù)蹲诀。
??目錄與鏈接
????? 正常情況下文件是通過(guò)文件名來(lái)打開(kāi)的。
? ????目錄是可讀名稱(chēng)到索引編號(hào)之間的映射弃揽,名稱(chēng)和索引節(jié)點(diǎn)之間的配對(duì)稱(chēng)為鏈接脯爪。
??????可以把目錄看做普通文件,只是它包含著文件名稱(chēng)到索引節(jié)點(diǎn)的映射(鏈接)
??進(jìn)程
? ? ? ?進(jìn)程是僅次于文件的抽象概念蹋宦,簡(jiǎn)單的理解披粟,進(jìn)程就是正在執(zhí)行的目標(biāo)代碼,活動(dòng)的冷冗,正在運(yùn)行的程序守屉。不過(guò)在復(fù)雜情況下,進(jìn)程還會(huì)包含著各種各樣的數(shù)據(jù)蒿辙,資源拇泛,狀態(tài)甚至虛擬計(jì)算機(jī)。
你可以這么理解進(jìn)程:它是競(jìng)爭(zhēng)計(jì)算機(jī)資源的基本單位思灌。
??進(jìn)程俺叭、程序與線(xiàn)程
? ????程序
? ? ? ?程序,簡(jiǎn)單的來(lái)說(shuō)就是存在磁盤(pán)上的二進(jìn)制文件泰偿,是可以?xún)?nèi)核所執(zhí)行的代碼
? ????進(jìn)程
? ? ? ?當(dāng)一個(gè)用戶(hù)啟動(dòng)一個(gè)程序熄守,將會(huì)在內(nèi)存中開(kāi)啟一塊空間,這就創(chuàng)造了一個(gè)進(jìn)程耗跛,一個(gè)進(jìn)程包含一個(gè)獨(dú)一無(wú)二的PID裕照,和執(zhí)行者的權(quán)限屬性參數(shù),以及程序所需代碼與相關(guān)的資料调塌。
? ? ? ?進(jìn)程是系統(tǒng)分配資源的基本單位晋南。
? ? ? ?一個(gè)進(jìn)程可以衍生出其他的子進(jìn)程,子進(jìn)程的相關(guān)權(quán)限將會(huì)沿用父進(jìn)程的相關(guān)權(quán)限羔砾。
? ????線(xiàn)程
? ? ? ?每個(gè)進(jìn)程包含一個(gè)或多個(gè)線(xiàn)程负间,線(xiàn)程是進(jìn)程內(nèi)的活動(dòng)單元,是負(fù)責(zé)執(zhí)行代碼和管理進(jìn)程運(yùn)行狀態(tài)的抽象姜凄。
? ? ? ?線(xiàn)程是獨(dú)立運(yùn)行和調(diào)度的基本單位政溃。
??進(jìn)程的層次結(jié)構(gòu)(父進(jìn)程與子進(jìn)程)
? ? ? ?在進(jìn)程執(zhí)行的過(guò)程中可能會(huì)衍生出其他的進(jìn)程,稱(chēng)之為子進(jìn)程檀葛,子進(jìn)程擁有一個(gè)指明其父進(jìn)程PID的PPID玩祟。子進(jìn)程可以繼承父進(jìn)程的環(huán)境變量和權(quán)限參數(shù)。
? ? ? ?于是屿聋,linux系統(tǒng)中就誕生了進(jìn)程的層次結(jié)構(gòu)——進(jìn)程樹(shù)空扎。
? ? ? ?進(jìn)程樹(shù)的根是第一個(gè)進(jìn)程(init進(jìn)程)藏鹊。
??????過(guò)程調(diào)用的流程: fork & exec
? ? ? ?一個(gè)進(jìn)程生成子進(jìn)程的過(guò)程是,系統(tǒng)首先復(fù)制(fork)一份父進(jìn)程转锈,生成一個(gè)暫存進(jìn)程盘寡,這個(gè)暫存進(jìn)程和父進(jìn)程的區(qū)別是pid不一樣夷磕,而且擁有一個(gè)ppid扼脐,這時(shí)候系統(tǒng)再去執(zhí)行(exec)這個(gè)暫存進(jìn)程,讓他加載實(shí)際要運(yùn)行的程序软棺,最終成為一個(gè)子進(jìn)程的存在砌溺。
??????進(jìn)程的結(jié)束
? ? ? ?當(dāng)一個(gè)進(jìn)程終止時(shí)影涉,并不會(huì)立即從系統(tǒng)中刪除,內(nèi)核將在內(nèi)存中保存該進(jìn)程的部分內(nèi)容规伐,允許父進(jìn)程查詢(xún)其狀態(tài)(這個(gè)被稱(chēng)為等待終止進(jìn)程)蟹倾。
? ? ? ?當(dāng)父進(jìn)程確定子進(jìn)程已經(jīng)終止,該子進(jìn)程將會(huì)被徹底刪除猖闪。
? ? ? ?但是如果一個(gè)子進(jìn)程已經(jīng)終止鲜棠,但父進(jìn)程卻不知道它的狀態(tài),這個(gè)進(jìn)程將會(huì)成為?僵尸進(jìn)程
??服務(wù)與進(jìn)程
簡(jiǎn)單的說(shuō)服務(wù)(daemon)就是常駐內(nèi)存的進(jìn)程培慌,通常服務(wù)會(huì)在開(kāi)機(jī)時(shí)通過(guò)init.d中的一段腳本被啟動(dòng)豁陆。
??進(jìn)程通信
? ? ? ?進(jìn)程通信的幾種基本方式:管道,信號(hào)量吵护,消息隊(duì)列盒音,共享內(nèi)存,快速用戶(hù)控件互斥馅而。
??程序里逆,進(jìn)程和線(xiàn)程
? ? ? ?現(xiàn)在我們?cè)俅卧敿?xì)的討論這三個(gè)概念
??????程序(program)
? ? ? ?程序是指編譯過(guò)的、可執(zhí)行的二進(jìn)制代碼用爪,保存在儲(chǔ)存介質(zhì)上,不運(yùn)行胁镐。
??????進(jìn)程(process)
? ? ? ?進(jìn)程是指正在運(yùn)行的程序偎血。
? ? ? ?進(jìn)程包括了很多資源,擁有自己獨(dú)立的內(nèi)存空間盯漂。
??????線(xiàn)程
? ? ? ?線(xiàn)程是進(jìn)程內(nèi)的活動(dòng)單元颇玷。
? ? ? ?包括自己的虛擬儲(chǔ)存器,如棧就缆、進(jìn)程狀態(tài)如寄存器帖渠,以及指令指針。
在單線(xiàn)程的進(jìn)程中竭宰,線(xiàn)程即進(jìn)程空郊。而在多線(xiàn)程的進(jìn)程中份招,多個(gè)線(xiàn)程將會(huì)共享同一個(gè)內(nèi)存地址空間
? 運(yùn)行一個(gè)進(jìn)程
? ? ? ?創(chuàng)建一個(gè)進(jìn)程,在unix系統(tǒng)中被分為了兩個(gè)流程狞甚。
????????● 把程序載入內(nèi)存并執(zhí)行程序映像的操作:exec
????????● 創(chuàng)建一個(gè)新進(jìn)程:fork
??exec
? ??? 最簡(jiǎn)單的exec系統(tǒng)調(diào)用函數(shù):execl()
????????● 函數(shù)原型:
int execl(const char * path,const chr * arg,...)
execl()調(diào)用將會(huì)把path所指的路徑的映像載入內(nèi)存锁摔,替換當(dāng)前進(jìn)程的映像。
參數(shù)arg是以第一個(gè)參數(shù)哼审,參數(shù)內(nèi)容是可變的谐腰,但最后必須以NULL結(jié)尾。
? ??????●?舉例:
int ret;
ret = execl("/bin/vi","vi",NULL);
if (ret == -1) {
? ? perror("execl");
}
? ? ? ?上面的代碼將會(huì)通過(guò)/bin/vi替換當(dāng)前運(yùn)行的程序
? ? ? ?注意這里的第一個(gè)參數(shù)vi涩盾,是unix系統(tǒng)的默認(rèn)慣例十气,當(dāng)創(chuàng)建、執(zhí)行進(jìn)程時(shí)春霍,shell會(huì)把路徑中的最后部分放入新進(jìn)程的第一個(gè)參數(shù)砸西,這樣可以使得進(jìn)程解析出二進(jìn)制映像文件的名字。
int ret;
ret = execl("/bin/vi","vi","/home/mark/a.txt",NULL);
if (ret == -1) {
? ? perror("execl");
}
? ? ? ?上面的代碼是一個(gè)非常有代表性的操作终畅,這相當(dāng)于你在終端執(zhí)行以下命令:
vi /home/mark/a.txt
? ??????●?返回值:
? ? ? ?正常情況下其實(shí)execl()不會(huì)返回籍胯,調(diào)用成功后會(huì)跳轉(zhuǎn)到新的程序入口點(diǎn)。
? ? ? ?成功的execl()調(diào)用离福,將改變地址空間和進(jìn)程映像杖狼,還改變了很多進(jìn)程的其他屬性。
? ? ? ?不過(guò)進(jìn)程的PID,PPID,優(yōu)先級(jí)等參數(shù)將會(huì)被保留下來(lái)妖爷,甚至?xí)A粝滤蜷_(kāi)的文件描述符(這就意味著它可以訪(fǎng)問(wèn)所有這些原本進(jìn)程打開(kāi)的文件)蝶涩。
? ? ? ?失敗后將會(huì)返回-1,并更新errno絮识。
????????●?其他exec系函數(shù)
? ? ? ?略绿聘,使用時(shí)查找
??fork
通過(guò)fork()系統(tǒng)調(diào)用,可以創(chuàng)建一個(gè)和當(dāng)前進(jìn)程映像一模一樣的子進(jìn)程次舌。
????●?函數(shù)原型
pid_t fork(void)
調(diào)用成功后熄攘,會(huì)創(chuàng)建一個(gè)新的進(jìn)程(子進(jìn)程),這兩個(gè)進(jìn)程都會(huì)繼續(xù)運(yùn)行彼念。
????●?返回值
如果調(diào)用成功挪圾,
父進(jìn)程中,fork()會(huì)返回子進(jìn)程的pid逐沙,在子進(jìn)程中返回0哲思;
如果失敗,返回-1吩案,并更新errno棚赔,不會(huì)創(chuàng)建子進(jìn)程。
? ??●?舉例
我們看下面這段代碼
#include <unistd.h>
#include <stdio.h>
int main ()
{
? ? pid_t fpid; //fpid表示fork函數(shù)返回的值
? ? int count=0;
? ? printf("this is a process\n");
? ? fpid=fork();
? ? if (fpid < 0)
? ? ? ? printf("error in fork!");
? ? else if (fpid == 0) {
? ? ? ? printf("i am the child process, my process id is %d\n",getpid());
? ? ? ? printf("我是爹的兒子\n");
? ? ? ? count++;
? ? }
? ? else {
? ? ? ? printf("i am the parent process, my process id is %d\n",getpid());
? ? ? ? printf("我是孩子他爹\n");
? ? ? ? count++;
? ? }
? ? printf("統(tǒng)計(jì)結(jié)果是: %d\n",count);
? ? return 0;
}
這段代碼的運(yùn)行結(jié)果比較神奇,是這樣的:
this is a process
i am the parent process, my process id is 21448
我是孩子他爹
統(tǒng)計(jì)結(jié)果是: 1
i am the child process, my process id is 21449
我是爹的兒子
統(tǒng)計(jì)結(jié)果是: 1
? ? ? ?在執(zhí)行了fork()之后靠益,這個(gè)程序就擁有了兩個(gè)進(jìn)程丧肴,父進(jìn)程和子進(jìn)程分別往下繼續(xù)執(zhí)行代碼,進(jìn)入了不同的if分支捆毫。
? ? ? ?如何理解pid在父子進(jìn)程中不同闪湾?
? ? ? ?其實(shí)就相當(dāng)于鏈表,進(jìn)程形成了鏈表绩卤,父進(jìn)程的pid指向了子進(jìn)程的pid途样,因?yàn)樽舆M(jìn)程沒(méi)有子進(jìn)程,所以pid為0濒憋。
? ??????●?寫(xiě)時(shí)復(fù)制
? ? ? ?傳統(tǒng)的fork機(jī)制是何暇,調(diào)用fork時(shí),內(nèi)核會(huì)復(fù)制所有的內(nèi)部數(shù)據(jù)結(jié)構(gòu)凛驮,復(fù)制進(jìn)程的頁(yè)表項(xiàng)裆站,然后把父進(jìn)程的地址空間按頁(yè)復(fù)制給子進(jìn)程(非常耗時(shí))。
? ? ? ?現(xiàn)代的fork機(jī)制采用了一種惰性算法的優(yōu)化策略黔夭。
? ? ? ?為了避免復(fù)制時(shí)系統(tǒng)開(kāi)銷(xiāo)宏胯,就盡可能的減少“復(fù)制”操作,當(dāng)多個(gè)進(jìn)程需要讀取他們自己那部分資源的副本時(shí)本姥,并不復(fù)制多個(gè)副本出來(lái)肩袍,而是為每個(gè)進(jìn)程設(shè)定一個(gè)文件指針,讓它們讀取同一個(gè)實(shí)際文件婚惫。
? ? ? ?顯然這樣的方式會(huì)在寫(xiě)入時(shí)產(chǎn)生沖突(類(lèi)似并發(fā))氛赐,于是當(dāng)某個(gè)進(jìn)程想要修改自己的那個(gè)副本時(shí),再去復(fù)制該資源先舷,(只有寫(xiě)入時(shí)才復(fù)制艰管,所以叫寫(xiě)時(shí)復(fù)制)這樣就減少了復(fù)制的頻率。
??聯(lián)合實(shí)例
? ? ? ?在程序中創(chuàng)建一個(gè)子進(jìn)程蒋川,打開(kāi)另一個(gè)應(yīng)用牲芋。
pid_t pid;
pid = fork();
if (pid == -1)
? ? perror("fork");
//子進(jìn)程
if (!pid) {
? ? const char * args[] = {"windlass",NULL};
? ? int ret;
? ? // 參數(shù)以數(shù)組方式傳入
? ? ret = execv("/bin/windlass",args);
? ? if (ret == -1) {
? ? ? ? perror("execv");
? ? ? ? exit(EXIT_FAILURE);
? ? }
}
? ? ? ?上面的程序創(chuàng)建了一個(gè)子進(jìn)程,并且使子進(jìn)程運(yùn)行了/bin/windlas程序捺球。
??終止進(jìn)程
??exit()
? ??????●?函數(shù)原型
void exit (int status)
? ? ? ?該函數(shù)用于終止當(dāng)前的進(jìn)程街图,參數(shù)status只用于標(biāo)識(shí)進(jìn)程的退出狀態(tài),這個(gè)值將會(huì)被傳送給當(dāng)前進(jìn)程的父進(jìn)程用于判斷懒构。
? ? ? ?還有一些其他的終止調(diào)用函數(shù),在此不贅述耘擂。
如果你也很想學(xué)編程胆剧,可以來(lái)我專(zhuān)欄的C語(yǔ)言/C++編程學(xué)習(xí)基地【點(diǎn)擊進(jìn)入】!
還有(源碼,零基礎(chǔ)教程秩霍,項(xiàng)目實(shí)戰(zhàn)教學(xué)視頻)篙悯!帶你入個(gè)門(mén)還是簡(jiǎn)簡(jiǎn)單單啦~
涉及:游戲開(kāi)發(fā)、課程設(shè)計(jì)铃绒、常用軟件開(kāi)發(fā)鸽照、編程基礎(chǔ)知識(shí)、黑客等等...