您好,登錄后才能下訂單哦!
(1)我們之前在寫裸機(jī)代碼的時(shí)候,需要有段引導(dǎo)代碼start.S
(2)我們操作系統(tǒng)中的應(yīng)用程序,也是需要一段引導(dǎo)代碼的,在我們編寫好一個(gè)應(yīng)用程序的時(shí)候,我們鏈接這個(gè)應(yīng)用程序的時(shí)候,鏈接器會(huì)從編譯器中將那段引導(dǎo)代碼加上鏈接進(jìn)去和我們的應(yīng)用程序一起生成可執(zhí)行程序,用gcc -v xxx.c編譯一個(gè)程序的時(shí)候我們可以看到這些詳細(xì)的信息,包括預(yù)處理,編譯,鏈接,gcc內(nèi)部有一個(gè)默認(rèn)的鏈接腳本,所以我們可以不用用鏈接腳本進(jìn)行指定。
(3)運(yùn)行時(shí)的加載器,加載器是操作系統(tǒng)的一個(gè)代碼,當(dāng)我們執(zhí)行一個(gè)程序的時(shí)候,加載器會(huì)將這段程序加載到內(nèi)存中去執(zhí)行程序編譯時(shí)用編譯器,程序運(yùn)行時(shí)用加載器,由加載器將程序弄到內(nèi)存中運(yùn)行。
(1)當(dāng)我們執(zhí)行一個(gè)程序的時(shí)候,帶了參數(shù)的話,因?yàn)槲覉?zhí)行這個(gè)程序的時(shí)候,其實(shí)所執(zhí)行的這個(gè)程序也相當(dāng)于一個(gè)參數(shù),所在的環(huán)境其實(shí)是一個(gè)shell的環(huán)境,shell這個(gè)環(huán)境接收到了這個(gè)兩個(gè)參數(shù)后會(huì)給加載器,加載器找到了main函數(shù),然后將這個(gè)兩個(gè)參數(shù)給了main,
(1)程序正常終止:return、exit、_exit,就是程序有目的終止了,知道什么時(shí)候終止
(2)程序非正常終止:自己或他人發(fā)信號(hào)終止進(jìn)程
(1)atexit函數(shù)注冊(cè)的那個(gè)函數(shù),是在程序正常終止的時(shí)候,會(huì)被執(zhí)行的,也就是說(shuō),如果一個(gè)程序正常終止是執(zhí)行到return 0,表示這個(gè)程序正常終止了,如果你使用的atexit函數(shù)的后面還有函數(shù),執(zhí)行順序不是先執(zhí)行atexit函數(shù),在執(zhí)行后面的那個(gè)函數(shù),而是先執(zhí)行后面的函數(shù),在執(zhí)行atexit函數(shù),就是說(shuō),程序正常終止時(shí),這個(gè)atexit注冊(cè)的那個(gè)函數(shù)才會(huì)被執(zhí)行
(2)當(dāng)一個(gè)進(jìn)程中多次用atexit函數(shù)注冊(cè)進(jìn)程終止處理函數(shù)時(shí),在程序正常終止的時(shí)候,先注冊(cè)的會(huì)被后執(zhí)行,后注冊(cè)的會(huì)先執(zhí)行,因?yàn)槟阆茸?cè)了,說(shuō)明你這個(gè)進(jìn)程正常終止的時(shí)候,你這個(gè)先注冊(cè)的會(huì)有優(yōu)先起到作用,跟棧的先進(jìn)后出一樣
(3)return exit _exir 的區(qū)別:return 和 exit 來(lái)讓程序正常終止的時(shí)候,atexit注冊(cè)的進(jìn)程終止處理函數(shù)會(huì)被執(zhí)行,但是_exit終止時(shí)就不會(huì)執(zhí)行進(jìn)程終止處理函數(shù)
(1)我們?cè)诿钚邢拢梢杂胑xport看到所有的環(huán)境變量
(2)進(jìn)程所在的環(huán)境中,也有環(huán)境變量,進(jìn)程環(huán)境表將這些環(huán)境變量構(gòu)成了一個(gè)表,當(dāng)前的進(jìn)程可以使用這個(gè)當(dāng)前進(jìn)程環(huán)境表的環(huán)境變量
(3)進(jìn)程中通過environ全局變量使用當(dāng)前進(jìn)程的環(huán)境變量,這個(gè)全局變量是由庫(kù)提供的,只用聲明,不用定義的。
environ這個(gè)全局變量聲明的方法,extern char environ,因?yàn)楫?dāng)前進(jìn)程可以使用的環(huán)境變量,是在進(jìn)程環(huán)境表中的,是一個(gè)一個(gè)的字符串,所以用了二重指針,是一個(gè)字符串?dāng)?shù)組,指針數(shù)組,數(shù)組中的每個(gè)元素都指向一個(gè)字符串,進(jìn)程環(huán)境表其實(shí)是一個(gè)字符串?dāng)?shù)組,用environ變量指向這個(gè)表來(lái)使用,所以可以使用environ[i]來(lái)遍歷所有的環(huán)境變量
(4)獲取環(huán)境變量,getenv函數(shù)
getenv函數(shù),C庫(kù)函數(shù),傳一個(gè)環(huán)境變量的名字,返回一個(gè)當(dāng)前環(huán)境變量的值,都是字符串格式的,獲取或者改環(huán)境變量,只是獲取和修改的你當(dāng)前進(jìn)程中的環(huán)境變量
**(1)操作系統(tǒng)中的每一個(gè)進(jìn)程都以為自己是操作系統(tǒng)中的唯一的一個(gè)進(jìn)程,所以都認(rèn)為自己享有獨(dú)立的地址空間,但是實(shí)際上不是這樣的,每一個(gè)進(jìn)程相當(dāng)于分時(shí)復(fù)用的。(2)操作系統(tǒng)為每一個(gè)進(jìn)程分配了邏輯空間4GB(32位系統(tǒng)),每個(gè)進(jìn)程都天真的以為,有4G的內(nèi)存,其中有1G是給操作系統(tǒng)的,其余的都是給自己用的,但實(shí)際上可能整個(gè)電腦就只有512MB的物理內(nèi)存,操作系統(tǒng)可能只需要用到10M內(nèi)存,這10M內(nèi)存就真正的映射到了物理內(nèi)存上,剩下的1G-10M的內(nèi)存是空的,相當(dāng)于是虛假的,當(dāng)你用到了的時(shí)候,在將你用到的相應(yīng)的內(nèi)存映射到物理內(nèi)存上,不用的時(shí)候,就又不映射到物理內(nèi)存上。所以雖然你相當(dāng)于有4GB的邏輯虛擬內(nèi)存,但是你的物理內(nèi)存真正并沒有那么多,而是你邏輯虛擬內(nèi)存用一點(diǎn),對(duì)應(yīng)你就去物理內(nèi)存中用一點(diǎn),但你肯定不會(huì)一下子用的內(nèi)存多于物理內(nèi)存,當(dāng)你不用時(shí),你要把內(nèi)存還回去了,所以這樣的情況就相當(dāng)于你的程序可以自己認(rèn)為自己在4G內(nèi)存中跑著的,程序用了多少內(nèi)存都會(huì)去物理內(nèi)存中取一點(diǎn)用,用完后又還回去了,所以這4G內(nèi)存是虛擬出來(lái)的。雖然進(jìn)程認(rèn)為自己有4G的內(nèi)存,但是你的進(jìn)程可能只會(huì)用到4M內(nèi)存,所以就對(duì)應(yīng)物理內(nèi)存中的4M,只是欺騙了進(jìn)程你有4G內(nèi)存。
所以也就是說(shuō)每一個(gè)進(jìn)程是工作在4G的邏輯虛擬內(nèi)存中的,但是你的進(jìn)程并不可能用到這么真正的對(duì)應(yīng)的物理內(nèi)存,你在邏輯虛擬內(nèi)存中用到了1M的內(nèi)存,就給你分配物理內(nèi)存中的1M進(jìn)行對(duì)應(yīng),用10M就給你10M,只是你自己以為你有4G的內(nèi)存。
這樣做的意義:
@1:為了讓進(jìn)程隔離,為了安全,讓進(jìn)程之間看不到對(duì)方。讓每個(gè)進(jìn)程都以為自己獨(dú)立的生活在那4G的內(nèi)存空間上。如果進(jìn)程不隔離的話,比如你在用支付寶,這個(gè)進(jìn)程運(yùn)行著,你又運(yùn)行了QQ,這個(gè)進(jìn)程也運(yùn)行著,但是因?yàn)檫M(jìn)程不隔離,QQ進(jìn)程能看支付寶進(jìn)程,那么QQ進(jìn)程就會(huì)用一些手段能獲取到你的支付寶密碼了。
br/>(2)操作系統(tǒng)為每一個(gè)進(jìn)程分配了邏輯空間4GB(32位系統(tǒng)),每個(gè)進(jìn)程都天真的以為,有4G的內(nèi)存,其中有1G是給操作系統(tǒng)的,其余的都是給自己用的,但實(shí)際上可能整個(gè)電腦就只有512MB的物理內(nèi)存,操作系統(tǒng)可能只需要用到10M內(nèi)存,這10M內(nèi)存就真正的映射到了物理內(nèi)存上,剩下的1G-10M的內(nèi)存是空的,相當(dāng)于是虛假的,當(dāng)你用到了的時(shí)候,在將你用到的相應(yīng)的內(nèi)存映射到物理內(nèi)存上,不用的時(shí)候,就又不映射到物理內(nèi)存上。所以雖然你相當(dāng)于有4GB的邏輯虛擬內(nèi)存,但是你的物理內(nèi)存真正并沒有那么多,而是你邏輯虛擬內(nèi)存用一點(diǎn),對(duì)應(yīng)你就去物理內(nèi)存中用一點(diǎn),但你肯定不會(huì)一下子用的內(nèi)存多于物理內(nèi)存,當(dāng)你不用時(shí),你要把內(nèi)存還回去了,所以這樣的情況就相當(dāng)于你的程序可以自己認(rèn)為自己在4G內(nèi)存中跑著的,程序用了多少內(nèi)存都會(huì)去物理內(nèi)存中取一點(diǎn)用,用完后又還回去了,所以這4G內(nèi)存是虛擬出來(lái)的。雖然進(jìn)程認(rèn)為自己有4G的內(nèi)存,但是你的進(jìn)程可能只會(huì)用到4M內(nèi)存,所以就對(duì)應(yīng)物理內(nèi)存中的4M,只是欺騙了進(jìn)程你有4G內(nèi)存。
所以也就是說(shuō)每一個(gè)進(jìn)程是工作在4G的邏輯虛擬內(nèi)存中的,但是你的進(jìn)程并不可能用到這么真正的對(duì)應(yīng)的物理內(nèi)存,你在邏輯虛擬內(nèi)存中用到了1M的內(nèi)存,就給你分配物理內(nèi)存中的1M進(jìn)行對(duì)應(yīng),用10M就給你10M,只是你自己以為你有4G的內(nèi)存。
這樣做的意義:
@1:為了讓進(jìn)程隔離,為了安全,讓進(jìn)程之間看不到對(duì)方。讓每個(gè)進(jìn)程都以為自己獨(dú)立的生活在那4G的內(nèi)存空間上。如果進(jìn)程不隔離的話,比如你在用支付寶,這個(gè)進(jìn)程運(yùn)行著,你又運(yùn)行了QQ,這個(gè)進(jìn)程也運(yùn)行著,但是因?yàn)檫M(jìn)程不隔離,QQ進(jìn)程能看支付寶進(jìn)程,那么QQ進(jìn)程就會(huì)用一些手段能獲取到你的支付寶密碼了。
(1)進(jìn)程就是程序的一次運(yùn)行過程,一個(gè)程序a.out被運(yùn)行后,在內(nèi)存中運(yùn)行,重運(yùn)行開始到結(jié)束就是一個(gè)進(jìn)程的開始和結(jié)束,a.out運(yùn)行一次就是進(jìn)程,運(yùn)行一次就是一個(gè)進(jìn)程
(1)內(nèi)核中專門用來(lái)管理進(jìn)程的一個(gè)數(shù)據(jù)結(jié)構(gòu),就叫進(jìn)程控制塊。操作系統(tǒng)用一個(gè)進(jìn)程控制塊,來(lái)管理和記錄這個(gè)進(jìn)程的信息
**(1)一個(gè)程序被運(yùn)行了,就是一個(gè)進(jìn)程,每一個(gè)進(jìn)程操作系統(tǒng)都會(huì)其分配一個(gè)ID,就是一個(gè)號(hào)碼,來(lái)唯一的標(biāo)識(shí)這個(gè)進(jìn)程,進(jìn)程ID是進(jìn)程控制塊這個(gè)結(jié)構(gòu)體中的一個(gè)元素。
(2)在Linux的命令行下,可以用ps命令來(lái)打印當(dāng)前的進(jìn)程(當(dāng)前在運(yùn)行的程序),就有當(dāng)前進(jìn)程的ID。 ps -a 可以查看所有的進(jìn)程 ps -aux 查看操作系統(tǒng)所有的進(jìn)程(這可能是包括之前運(yùn)行的進(jìn)程和當(dāng)前運(yùn)行的進(jìn)程)
(3)在編程的過程中,也可以用函數(shù)來(lái)獲得進(jìn)程號(hào)
getpid、獲取當(dāng)前進(jìn)程的自己的PID,進(jìn)程ID號(hào)
getppid、 獲取父進(jìn)程的ID號(hào)
getuid 獲取當(dāng)前進(jìn)程的用戶ID
geteuid、 獲取當(dāng)前進(jìn)程的有效用戶ID
getgid、 獲取當(dāng)前進(jìn)程組的ID
getegid 獲取當(dāng)前進(jìn)程的有效組ID
實(shí)際用戶ID,有效用戶ID,實(shí)際組ID,有效組ID(有需要去百度吧)**
(1)操作系統(tǒng)同時(shí)運(yùn)行多個(gè)進(jìn)程
(2)宏觀上的并行,微觀上的串行
(3)實(shí)際上現(xiàn)代操作系統(tǒng)最小的調(diào)度單元是線程而不是進(jìn)程
(1)每一次程序的運(yùn)行都需要一個(gè)進(jìn)程,操作系統(tǒng)運(yùn)行你的程序的時(shí)候是需要代價(jià)的,代價(jià)就是要讓這個(gè)程序加載到一個(gè)進(jìn)程中去運(yùn)行,這個(gè)進(jìn)程又不是憑空出現(xiàn)的,是需要?jiǎng)?chuàng)建的,所以操作系統(tǒng)運(yùn)行一個(gè)程序的時(shí)候,是要先構(gòu)建一個(gè)進(jìn)程的,就構(gòu)建了一個(gè)進(jìn)程的進(jìn)程控制塊PCB,這是一個(gè)結(jié)構(gòu)體,既然你要構(gòu)建,你就是一個(gè)結(jié)構(gòu)體變量,是需要內(nèi)存的,這個(gè)進(jìn)程控制塊中,就有這個(gè)進(jìn)程的ID號(hào),就需要將你的程序用這個(gè)進(jìn)程控制塊PCB來(lái)進(jìn)行管理和操作。
也就是說(shuō),你的程序想要在操作系統(tǒng)中運(yùn)行的話,就一定要參與進(jìn)程調(diào)度,所以就一定要先構(gòu)建一個(gè)進(jìn)程,所以先構(gòu)建一個(gè)進(jìn)程控制快PCB,將這個(gè)程序用這個(gè)進(jìn)程控制塊來(lái)管理,這個(gè)進(jìn)程控制塊就是一個(gè)結(jié)構(gòu)體變量,里面有好多好多的元素,就相當(dāng)于一個(gè)房子,一個(gè)讓程序住的房子,把這個(gè)程序當(dāng)成了一個(gè)進(jìn)程來(lái)管理,進(jìn)程控制塊中就有這個(gè)進(jìn)程的好多信息,比如PID號(hào)等等。
(2)linux 中制造進(jìn)程的方法就是拿老進(jìn)程來(lái)復(fù)制出來(lái)一個(gè)新進(jìn)程
(3)fork之前的的代碼段只有父進(jìn)程擁有,fork之后的代碼段,父子進(jìn)程都有,數(shù)據(jù)段,棧這些內(nèi)存中的數(shù)據(jù),即使在fork之前父子進(jìn)程也都有,因?yàn)閒ork的時(shí)候是將父進(jìn)程的進(jìn)程控制塊中的代碼段復(fù)制到子進(jìn)程的進(jìn)程控制塊中,而全局變量,棧,數(shù)據(jù)值都是擁有的
(1)進(jìn)程的分裂生長(zhǎng)模式:如果操作系統(tǒng)需要運(yùn)行一個(gè)程序,就需要一個(gè)進(jìn)程,讓這個(gè)程序在這個(gè)進(jìn)程控制塊中。做法是,用一個(gè)現(xiàn)有的老的進(jìn)程復(fù)制出一個(gè)新的進(jìn)程(比如用memcopy直接將一個(gè)老的進(jìn)程控制塊進(jìn)行復(fù)制,因?yàn)槭墙Y(jié)構(gòu)體變量嘛),然后在這個(gè)復(fù)制過來(lái)的基礎(chǔ)上去修改,比如修改這個(gè)進(jìn)程塊中描述當(dāng)前運(yùn)行的那個(gè)程序的內(nèi)容,給改成我們這個(gè)程序,讓我們這個(gè)程序的代碼段到這個(gè)位置。既然是復(fù)制的,所以就有了父進(jìn)程和子進(jìn)程
總結(jié):一個(gè)程序想要在操作系統(tǒng)上運(yùn)行的話,操作系統(tǒng)是需要先構(gòu)建一個(gè)進(jìn)程的,為了能其進(jìn)行調(diào)度。構(gòu)建了一個(gè)進(jìn)程控制塊PCB,這是一個(gè)結(jié)構(gòu)體,用這個(gè)結(jié)構(gòu)體定義了一個(gè)變量,讓這個(gè)變量中進(jìn)行填充內(nèi)容,這就是一個(gè)進(jìn)程的樣子,里面放了這個(gè)要執(zhí)行的程序的代碼段等等,還未這個(gè)程序弄了一個(gè)進(jìn)程ID號(hào),不過,在構(gòu)建一個(gè)進(jìn)程的時(shí)候,不是直接從頭開始構(gòu)建的,而是用老的進(jìn)程復(fù)制了一個(gè)新的進(jìn)程,就是用memcopy將老進(jìn)程也就那個(gè)結(jié)構(gòu)體變量中的內(nèi)存空間復(fù)制了一個(gè)快出來(lái),在向其中進(jìn)行修改,修改ID號(hào),修改程序的代碼段等等,所以就有了父進(jìn)程和子進(jìn)程。
(1)fork函數(shù)調(diào)用一次,會(huì)返回兩次,返回值等于0的就是子進(jìn)程,返回值大于0的就父進(jìn)程。
我們程序 p1 = fork();后,在運(yùn)行到這一句的時(shí)候,操作系統(tǒng)就已經(jīng)將父進(jìn)程就是當(dāng)前的進(jìn)程復(fù)制了一份了,子進(jìn)程就出來(lái)了,這個(gè)時(shí)候在這個(gè)程序中,父進(jìn)程也就是現(xiàn)在的這個(gè)程序還在操作系統(tǒng)的中運(yùn)行這,同時(shí)子進(jìn)程也被操作系統(tǒng)運(yùn)行著了,所以當(dāng)我們后面一次判斷 if(p1 == 0) 的時(shí)候,就是判斷這個(gè)是子進(jìn)程還是父進(jìn)程,0就是子進(jìn)程,if(p1 > 0)就是判斷是否是父進(jìn)程
(2)因?yàn)楫?dāng)我們fork的時(shí)候,這個(gè)進(jìn)程的整個(gè)進(jìn)程控制塊都被復(fù)制了,也就代表著這個(gè)程序的代碼段也被復(fù)制了一份,變成了子進(jìn)程,這個(gè)子進(jìn)程有的也是這個(gè)代碼段,所以p1 = fork()后面的代碼是兩個(gè)進(jìn)程同時(shí)擁有的,兩個(gè)進(jìn)程都要運(yùn)行的,所以有了if (p1 == 0)和if(p1 > 0 )這兩個(gè)if來(lái)讓程序進(jìn)到其中的某一個(gè)進(jìn)程中,因?yàn)楦高M(jìn)程中的p1是大于0的,子進(jìn)程中的p1是等于0的,這樣就可以進(jìn)到其中的某一個(gè)進(jìn)程中。fork后有了兩個(gè)進(jìn)程,都在運(yùn)行的,宏觀上的并行,微觀上的并行,由操作系統(tǒng)來(lái)調(diào)度。
再次總結(jié):
fork函數(shù)運(yùn)行后,就將父進(jìn)程的進(jìn)程控制塊復(fù)制了一個(gè)份成為了子進(jìn)程的進(jìn)程控制塊,這個(gè)時(shí)候,子進(jìn)程的進(jìn)程控制塊中就有了父進(jìn)程控制塊的代碼段,并且這個(gè)時(shí)候已經(jīng)是兩個(gè)進(jìn)程了,所以操作系統(tǒng)就會(huì)對(duì)這兩個(gè)進(jìn)程進(jìn)行調(diào)度運(yùn)行,所以這個(gè)時(shí)候兩個(gè)進(jìn)程都是在運(yùn)行的,所以fork后面的代碼段兩個(gè)進(jìn)程都有,都會(huì)運(yùn)行,這個(gè)時(shí)候我們?nèi)绻胍M(jìn)到這兩個(gè)進(jìn)程中某一個(gè)進(jìn)程中的話,就需要用到if來(lái)進(jìn)行判斷,因?yàn)閒ork會(huì)返回兩次,一次的返回值給了父進(jìn)程,一次的返回值給子進(jìn)程,給父進(jìn)程的返回值是大于0的,給子進(jìn)程的返回值是等于0的,因?yàn)楹竺娴拇a兩個(gè)進(jìn)程都會(huì)運(yùn)行,如果我們想要進(jìn)入到某一個(gè)進(jìn)程中去做事情的話,就可以判斷返回值是父進(jìn)程的還是子進(jìn)程的來(lái)決定到底是哪個(gè)進(jìn)程的,重而可以進(jìn)入到這個(gè)進(jìn)程中去。父進(jìn)程的返回值是大于0的,值就是本次fork創(chuàng)建子進(jìn)程的pid
(1)在父進(jìn)程中open打開一個(gè)文件得到fd,之后fork父進(jìn)程創(chuàng)建子進(jìn)程,之后再父子進(jìn)程中wirte向文件中寫東西,發(fā)現(xiàn)父子進(jìn)程是接續(xù)寫的,原因是因?yàn)楦缸舆M(jìn)程中的fd對(duì)應(yīng)的文件指針彼此是關(guān)聯(lián)的,很像加上了O_APPEDN標(biāo)志。
(2)我們父子進(jìn)程寫的時(shí)候,有的時(shí)候會(huì)發(fā)現(xiàn)類似于分別寫的情況,那是因?yàn)楦缸舆M(jìn)程中的程序太短了,可以加一額sleep(1)來(lái)休眠一段時(shí)間,主要是因?yàn)椋愕母高M(jìn)程或者子進(jìn)程在運(yùn)行完自己的程序的時(shí)候,就會(huì)close關(guān)閉這個(gè)文件了,既然有一個(gè)進(jìn)程把文件關(guān)閉了,那另一個(gè)進(jìn)程既然寫不進(jìn)去了
(1)父進(jìn)程open打開一個(gè)文件然后寫入,子進(jìn)程也open打開這個(gè)文件然后寫入,結(jié)論是分別寫,會(huì)覆蓋,原因是因?yàn)檫@個(gè)時(shí)候父子進(jìn)程已經(jīng)完全分離后才在各自的進(jìn)程中打開文件的,這時(shí)兩個(gè)進(jìn)程的PCB已經(jīng)是完全獨(dú)立的了,所以相當(dāng)于兩個(gè)進(jìn)程打開了同一個(gè)文件,實(shí)現(xiàn)了文件共享。open中加上O_APPEND標(biāo)志后,可以實(shí)現(xiàn)接續(xù)寫,實(shí)現(xiàn)文件指針關(guān)聯(lián)起來(lái)
(1)進(jìn)程0和進(jìn)程1,進(jìn)程0是在內(nèi)核態(tài)的時(shí)候,內(nèi)核自己弄出來(lái)的,是空閑進(jìn)程,進(jìn)程1是內(nèi)核復(fù)制進(jìn)程0弄出來(lái)的,相當(dāng)于內(nèi)核中的fork弄出來(lái)的,然后執(zhí)行了進(jìn)程1中的那個(gè)根文件系統(tǒng)中init程序,進(jìn)而逐步的重內(nèi)核態(tài)到用戶態(tài)。
(2)進(jìn)入到了用戶太的時(shí)候,之后的每一個(gè)進(jìn)程都是父進(jìn)程fork出來(lái)的
(3)vfork,和fork有微小的區(qū)別,需要可以自己去百度
(1)正常終止和異常終止,靜態(tài)的放在那里不動(dòng)的a.out叫做程序,運(yùn)行了就叫做進(jìn)程,進(jìn)程就是運(yùn)行的程序
(2)進(jìn)程在運(yùn)行的時(shí)候是需要耗費(fèi)系統(tǒng)資源的(內(nèi)存、IO),內(nèi)存是指進(jìn)程中向操作系統(tǒng)申請(qǐng)的內(nèi)存的資源,創(chuàng)建進(jìn)程時(shí)也需要內(nèi)存,IO是進(jìn)程對(duì)文件IO和IO硬件設(shè)備,比如串口這個(gè)IO的資源。所以進(jìn)程終止的時(shí)候,應(yīng)當(dāng)把這些資源完全釋放,不然的話,就會(huì)不斷的消耗系統(tǒng)的資源,比如你的進(jìn)程死的時(shí)候,你沒有將向操作系統(tǒng)申請(qǐng)的資源換回去,那么內(nèi)存就會(huì)越用越少,形成吃內(nèi)存的情況
(3)linux系統(tǒng)中,當(dāng)一個(gè)進(jìn)程退出的時(shí)候,操作系統(tǒng)會(huì)自動(dòng)回收這個(gè)進(jìn)程涉及到的所有資源,比如,向我們?cè)谝粋€(gè)進(jìn)程malloc的時(shí)候,但是沒有free,但是這個(gè)進(jìn)程退出的時(shí)候,內(nèi)存也會(huì)被是釋放,比如open了一個(gè)文件,但是進(jìn)程結(jié)束時(shí)我們沒有close,但是進(jìn)程結(jié)束時(shí),操作系統(tǒng)也會(huì)進(jìn)行回收。但是操作系統(tǒng)只是回收了這個(gè)進(jìn)程工作時(shí)消耗的資源,并沒有回收這個(gè)進(jìn)程本身占用的內(nèi)存(一個(gè)進(jìn)程的創(chuàng)建需要內(nèi)存,因?yàn)槭菑?fù)制父進(jìn)程的PCB出來(lái)的,主要是task_struct和棧內(nèi)存,有8KB,task_struct就是PCB,描述這個(gè)進(jìn)程的那個(gè)結(jié)構(gòu)體,棧內(nèi)存是這個(gè)進(jìn)程所獨(dú)有的棧)。
(4)所以進(jìn)程消耗的資源主要是分為兩部分,一部分是這個(gè)進(jìn)程工作時(shí)消耗的資源,另一部分是這個(gè)進(jìn)程本身自己存在就需要的資源,操作系統(tǒng)在一個(gè)進(jìn)程結(jié)束的時(shí)候,回收的只是進(jìn)程工作時(shí)用的資源,并沒有回收進(jìn)程本身自己存在所需要的資源
(5)所以進(jìn)程自己本身需要的8KB內(nèi)存,操作系統(tǒng)沒有回收,是需要?jiǎng)e人輔助回收的,所以需要收尸的人,收進(jìn)程尸體的人,因?yàn)檫M(jìn)程的尸體也需要占用8KB的內(nèi)存,所以需要收尸,這個(gè)人就是這個(gè)進(jìn)程的父進(jìn)程
(1)僵尸進(jìn)程就是子進(jìn)程先與父進(jìn)程結(jié)束的進(jìn)程:子進(jìn)程結(jié)束后,父進(jìn)程并不是馬上立刻就將子進(jìn)程收尸的,在這一段子進(jìn)程結(jié)束了,但是父進(jìn)程尚未幫子進(jìn)程收尸的這一段時(shí)間,這個(gè)子進(jìn)程就是一個(gè)僵尸進(jìn)程
(2)在僵尸進(jìn)程的這一段時(shí)間,子進(jìn)程除了8KB這一段內(nèi)存(task_struct和棧)外,其余的內(nèi)存空間和IO資源都被操作系統(tǒng)回收干凈了
(3)父進(jìn)程可以使用wait或者waitpid以顯式的方式回收子進(jìn)程的待被回收的內(nèi)存資源,并且獲取子進(jìn)程的退出狀態(tài)。真因?yàn)楦高M(jìn)程要調(diào)用wait或者waitpid來(lái)幫子進(jìn)程回收子進(jìn)程結(jié)束時(shí)剩余的內(nèi)存資源,所以父進(jìn)程也是要執(zhí)行函數(shù)的,所以有了子進(jìn)程的這一段僵尸進(jìn)程的時(shí)間,因?yàn)樽舆M(jìn)程死的太快了,父進(jìn)程需要等到調(diào)用了這個(gè)兩個(gè)函數(shù)才可以回收子進(jìn)程,所以子進(jìn)程必然會(huì)存在僵尸進(jìn)程的這一段時(shí)間。
(4)子進(jìn)程結(jié)束的階段到父進(jìn)程調(diào)用wait或waitpid函數(shù)的這一階段,就是子進(jìn)程的僵尸進(jìn)程階段
(5)父進(jìn)程還有一種情況也可以回收子進(jìn)程剩余的內(nèi)存資源,就是父進(jìn)程也死了,這個(gè)時(shí)候,父進(jìn)程結(jié)束時(shí)也會(huì)去回收子進(jìn)程剩余的內(nèi)存資源,這種回收,是在子進(jìn)程先結(jié)束了,父進(jìn)程后結(jié)束的情況下 。(這樣設(shè)計(jì)是為了防止,父進(jìn)程活的時(shí)候忘記使用wait或waitpid來(lái)回收子進(jìn)程從而造成內(nèi)存泄漏)
(1)父進(jìn)程先于子進(jìn)程結(jié)束
(2)Linux中規(guī)定,所有的孤兒進(jìn)程都自動(dòng)成為進(jìn)程1,init進(jìn)程的子進(jìn)程
(1)子進(jìn)程結(jié)束時(shí),系統(tǒng)向其父進(jìn)程發(fā)出SIGCHILD信號(hào)(SIGCHILD是信號(hào)中的一個(gè)編號(hào))
(2)父進(jìn)程調(diào)用wait函數(shù)后阻塞,wait這個(gè)函數(shù)是阻塞式的,父進(jìn)程調(diào)用wait這個(gè)函數(shù)阻塞在這里,等操作系統(tǒng)向我發(fā)SIGCHILD信號(hào)
(3)父進(jìn)程wait后阻塞等到操作系統(tǒng)發(fā)來(lái)的SIGCHILD信號(hào),收到SIGCHILD信號(hào)后,父進(jìn)程被喚醒,去回收僵尸子進(jìn)程
(4)父進(jìn)程如果沒有任何的子進(jìn)程,這個(gè)時(shí)候父進(jìn)程調(diào)用wait函數(shù),wait函數(shù)就會(huì)返回錯(cuò)誤
(1)wait的參數(shù),status。這個(gè)參數(shù)是用來(lái)返回狀態(tài)的,是子進(jìn)程結(jié)束時(shí)的狀態(tài),父進(jìn)程調(diào)用wait通過status這個(gè)參數(shù),可以知道子進(jìn)程結(jié)束時(shí)的狀態(tài)。子進(jìn)程結(jié)束的時(shí)候有兩種狀態(tài),一種是正常的結(jié)束狀態(tài),就是return,exit等造成的結(jié)束,一種是異常狀態(tài),就是由信號(hào)造成的異常終止?fàn)顟B(tài),通過status參數(shù)返回的狀態(tài)可以知道這個(gè)僵尸子進(jìn)程是怎么死的
(2)wait的返回值,pid_t,就是本次wait回收的僵尸子進(jìn)程的PID號(hào)。因?yàn)楫?dāng)前進(jìn)程可能有多個(gè)子進(jìn)程,wait阻塞后,你也不知道將來(lái)哪一個(gè)子進(jìn)程會(huì)結(jié)束先讓你回收,所以需要用wait的返回值來(lái)表示本次回收的僵尸子進(jìn)程的PID號(hào),來(lái)知道是哪個(gè)進(jìn)程本次被回收了。
所以:wait函數(shù)就是用來(lái)回收僵尸子進(jìn)程的,父進(jìn)程wait后阻塞等到操作系統(tǒng)發(fā)來(lái)的SIGCHILD信號(hào),收到SIGCHILD信號(hào)后,父進(jìn)程被喚醒,去回收僵尸子進(jìn)程,并且還會(huì)通過返回值pid_t類型的值得到這個(gè)僵尸子進(jìn)程的PID,還會(huì)通過status參數(shù)得到這個(gè)子進(jìn)程的結(jié)束的狀態(tài),
(3)WIFEXITED、WIFSIGNALED、WEXITSTATUS,這幾個(gè)宏是來(lái)判斷wait函數(shù)中status參數(shù)返回回來(lái)的值代表子進(jìn)程結(jié)束是哪種狀態(tài)的
WIFEXITED:可以用WIFEXITED宏和status參數(shù)值來(lái)判斷回收的那個(gè)子進(jìn)程是否是正常終止的(return exit _exit退出的),測(cè)試status的值是正常退出,這個(gè)宏就返回1,不正常就是0。
WIFSGNALED: 用來(lái)判斷子進(jìn)程是否非正常終止,是否是不正常終止(被信號(hào)所終止),是非正常的就返回1,不是就0
WEXITSTATUS:這個(gè)宏可以得到子進(jìn)程正常結(jié)束狀態(tài)下的返回值的(就是return exit _exit 帶的值)
*(1)wait和waitpid的功能幾乎是一樣的,都是用來(lái)回收僵尸子進(jìn)程的。不同的是waitpid可以指定PID號(hào),來(lái)回收指定的僵尸子進(jìn)程,waitpid可以有阻塞和非阻塞兩種工作方式
pid_t waitpid(pid_t pid, int status, int options);
返回值是被回收的子進(jìn)程的PID號(hào),第一個(gè)參數(shù)是指定要回收子進(jìn)程的PID號(hào),如果第一個(gè)參數(shù)給了-1,就表示不管哪一個(gè)子進(jìn)程要被回收都可以,就跟wait一樣了,第二個(gè)參數(shù)會(huì)得到一個(gè)子進(jìn)程的結(jié)束狀態(tài),第三個(gè)參數(shù)是一個(gè)可選功能,可以有讓這個(gè)函數(shù)是阻塞還是非阻塞的,第三個(gè)參數(shù)如果是0的話,就表示沒有特別的要求,表示是阻塞式的
WNOHANG,options是這個(gè)就表示是非阻塞式的,如果第一個(gè)參數(shù)給的子進(jìn)程號(hào),這個(gè)子進(jìn)程沒有執(zhí)行完,沒有可回收的就立馬返回 ,返回值是0,如果個(gè)的子進(jìn)程號(hào)是不對(duì)的,就返回-1,如果給的子進(jìn)程號(hào)被成功回收了,就返回這個(gè)被回收的子進(jìn)程的PID號(hào)**
(1)競(jìng)態(tài)的全稱就是競(jìng)爭(zhēng)狀態(tài),在多進(jìn)程的環(huán)境下,多個(gè)進(jìn)程會(huì)搶占系統(tǒng)的資源,內(nèi)存,文件IO,cpu。
(2)競(jìng)爭(zhēng)狀態(tài)是有害的,我們應(yīng)該去消滅這種競(jìng)態(tài),操作系統(tǒng)為我們提供了很多機(jī)制來(lái)去消滅這種競(jìng)態(tài),利用好sleep就可以讓父子進(jìn)程哪個(gè)先執(zhí)行,哪個(gè)后結(jié)束,當(dāng)然還有其他的方法,就是讓進(jìn)程同步
(1)execl和execv:這兩個(gè)函數(shù)主要是第二個(gè)參數(shù)的格式的問題,第一個(gè)函數(shù)中的執(zhí)行的那個(gè)可執(zhí)行程序所帶的參數(shù),是一字符串,一個(gè)字符串代表一個(gè)參數(shù),多個(gè)參數(shù),是多個(gè)字符串,像列表一樣,一個(gè)參數(shù)一個(gè)參數(shù)的,參數(shù)的結(jié)尾必須是NULL表示傳參結(jié)束了。execl中的l其實(shí)就是list的縮寫,列表。
而execv是將參數(shù)放到一個(gè)字符串?dāng)?shù)組中,
這個(gè)兩個(gè)函數(shù),如果找到了那個(gè)pathname的可執(zhí)行程序,就會(huì)執(zhí)行,找不到就會(huì)報(bào)錯(cuò),可執(zhí)行程序是我們指定的。注意是全路徑
(2)execlp和execlp:這連個(gè)函數(shù)的區(qū)別就是多了個(gè)p,這個(gè)函數(shù)的第一個(gè)參數(shù)是file,是一個(gè)文件名,當(dāng)然也可以是全路徑加文件名,這兩個(gè)函數(shù)因?yàn)閰?shù)是文件名,所以會(huì)首先找file這個(gè)名的可執(zhí)行程序,找到后就執(zhí)行,如果找不到就會(huì)去PATH環(huán)境變量指定的目錄下去尋找,也就是說(shuō)這兩個(gè)函數(shù)執(zhí)行的那個(gè)可執(zhí)行程序應(yīng)該是唯一的,如果你確定只有這一個(gè)程序,就應(yīng)該使用這個(gè),方便多些路徑的麻煩
(3)execle和execpe:這兩個(gè)函數(shù)就是多了一個(gè)e,多了一個(gè)環(huán)境變量的字符串?dāng)?shù)組,可以給那個(gè)可執(zhí)行程序多傳遞一個(gè)環(huán)境變量,讓那個(gè)可執(zhí)行程序在運(yùn)行的時(shí)候,可以用這個(gè)傳過去的環(huán)境變量
extern char **environ;這個(gè)是進(jìn)程中的那個(gè)進(jìn)程環(huán)境表,當(dāng)前進(jìn)程的所有環(huán)境變量都維護(hù)在這個(gè)表中,這個(gè)environ指針指向那個(gè)環(huán)境表。自己是就是一個(gè)字符串?dāng)?shù)組指針,就是一個(gè)指針數(shù)組
@1:int execl(const char path, const char arg, ...);
(1)第一個(gè)參數(shù)是要執(zhí)行的那個(gè)可執(zhí)行程序的全路徑,就是pathname,第二參數(shù)是要執(zhí)行的那個(gè)可執(zhí)行程序的名字,第三個(gè)是變參,說(shuō)明那個(gè)可執(zhí)行程序可以帶的參數(shù),是由那個(gè)可執(zhí)行程序決定,他能接受幾個(gè),你就可以傳幾個(gè),參數(shù)是以NULL結(jié)尾的,告訴傳參沒了,可以在命令行下,用which xxx來(lái)查這個(gè)xxx命令(也是程序)的全路徑,一定要是全路徑第一個(gè)參數(shù)
用法是:execl("/bin/ls", "ls", "-l", "-a", NULL);
@2:int execlp(const char file, const char arg, ...);
用法只是第一個(gè)參數(shù)不同,第一個(gè)參數(shù)是要執(zhí)行可執(zhí)行程序的名字,可以是全路徑,也可以是單純的名字,先去PATH環(huán)境變量下的路徑中去尋找,如果沒有找到這個(gè)程序就在指定的目錄下或者當(dāng)前目錄下找
@3:int execle(const char path, const char arg, ..., char * const envp[]);
@4:int execv(const char path, char const argv[]);
(1)第一個(gè)參數(shù)和execl一樣,第二參數(shù)說(shuō)明,參數(shù)是放在argv這個(gè)指針數(shù)組中的,字符串?dāng)?shù)組,數(shù)組中的指針指向的東西是可以變的。用的時(shí)候是這樣用的,先定義一個(gè)
char * const arg[] = {"ls", "-l", "-a", NULL};
然后在 execv("/bin/ls", arg);
@5:int execvp(const char file, char const argv[]);
@6:int execvpe(const char file, char const argv[], char *const envp[]);
(3)execle和execpe @1:真正的main函數(shù)的原型是這樣的
br/>@1:真正的main函數(shù)的原型是這樣的
argv, char env)
env就是給main函數(shù)額外傳遞的環(huán)境變量字符串?dāng)?shù)組,正常我們直接寫一個(gè)程序直接編譯運(yùn)行的時(shí)候里面的環(huán)境變量值是繼承父進(jìn)程的,最早是來(lái)源于操作系統(tǒng)中的環(huán)境變量
@2:execle 的用法,
在子進(jìn)程中先定義了一個(gè) char * const envp["AA=XX", "BB=DDD", NULL];
在使用execle("/bin/ls", "ls", "-l", "-a", NULL, env);給ls這個(gè)可執(zhí)行程序傳遞了環(huán)境變量envp中的,這個(gè)時(shí)候這個(gè)執(zhí)行的程序中的env[0]就等于了AA=XX env[1]就等于了BB=DDD,NULL表示后面沒有要傳的環(huán)境變量,用來(lái)當(dāng)結(jié)束。如果你不給這個(gè)可執(zhí)行程序傳遞環(huán)境變量的話,這個(gè)可執(zhí)行程序繼承的默認(rèn)就是父進(jìn)程中的那一份,如果你傳了,就是你傳遞的那一份環(huán)境變量
1、進(jìn)程的幾個(gè)重要的需要明白的狀態(tài)
操作系統(tǒng)是怎么調(diào)度進(jìn)程的,操作系統(tǒng)中有一個(gè)就緒鏈表,每一個(gè)節(jié)點(diǎn)就是一個(gè)進(jìn)程,將所有符合條件可以運(yùn)行的進(jìn)程加到了這個(gè)就緒鏈表中。
操作系統(tǒng)中還有一個(gè)鏈表,是所有進(jìn)程的鏈表,就是所有的進(jìn)程都在這個(gè)鏈表中,這個(gè)鏈表中的進(jìn)程如果有符合就緒態(tài)可以運(yùn)行的了,就會(huì)被復(fù)制加載到就緒態(tài)鏈表中
(1)就緒態(tài):這個(gè)進(jìn)程當(dāng)前的所有運(yùn)行條件具備,只要得到CPU的時(shí)間,就可以運(yùn)行了,只差被OS調(diào)度得到CPU的時(shí)間了
(2)運(yùn)行態(tài):進(jìn)程從就緒態(tài)得到了CPU,就開始運(yùn)行了,當(dāng)前進(jìn)程就是在運(yùn)行態(tài)
(3)僵尸態(tài):進(jìn)程運(yùn)行結(jié)束了,操作系統(tǒng)回收了一部分資源(進(jìn)程工作時(shí)用的內(nèi)存和IO資源),但是沒有被徹底回收,父進(jìn)程還沒將剩余的8KB內(nèi)存空間(進(jìn)程自身占用的內(nèi)存)回收的這段時(shí)間。
(4)等待態(tài)(淺度睡眠&深度睡眠):進(jìn)程等待滿足某種條件達(dá)到就緒態(tài)的這段時(shí)間,等待態(tài)下就算給CPU時(shí)間也沒法運(yùn)行。淺度睡眠時(shí),進(jìn)程可以被(信號(hào),別人打電話告訴你不要等那個(gè)衛(wèi)生紙了,你就不等了)喚醒達(dá)到就緒態(tài),得到CPU時(shí)間,就可以達(dá)到運(yùn)行態(tài)了。深度睡眠時(shí),進(jìn)程不可以被喚醒了(別人打電話告訴你不要等那個(gè)衛(wèi)生紙了,你就非要等到那個(gè)衛(wèi)生紙才行),不能再喚醒達(dá)到就緒態(tài)了,只有等待到條件滿足了,才能結(jié)束睡眠狀態(tài)
(5)暫停態(tài):暫停態(tài)不是說(shuō)進(jìn)程終止了,進(jìn)程終止了就成為僵尸態(tài)了才對(duì),暫停態(tài)只是說(shuō)進(jìn)程被信號(hào)暫停了,還可以恢復(fù)(發(fā)信號(hào))
一個(gè)進(jìn)程在一個(gè)生命周期內(nèi),是在這5種狀態(tài)間切換的
(1)system函數(shù) = fork + exec
system函數(shù)是原子操作的,而fork+exec操作是非原子操作的
(2)原子操作的意思是,一旦開始,就會(huì)不被打斷的執(zhí)行完。fork + exec是非原子的,意思是說(shuō)你fork后得到一個(gè)子進(jìn)程的時(shí)候,CPU的時(shí)間有可能是還沒執(zhí)行exec呢,就被調(diào)度執(zhí)行別的進(jìn)程去了,而system函數(shù)是,你創(chuàng)建了子進(jìn)程讓子進(jìn)程執(zhí)行那個(gè)程序,這之間是立刻的事情,并且執(zhí)行完
(3)原子操作的好處就是會(huì)盡量的避免競(jìng)爭(zhēng)狀態(tài),壞處就是自己連續(xù)占用CPU的時(shí)間太長(zhǎng)了
(4)int system(const char *command);
system函數(shù)先調(diào)用fork創(chuàng)建一個(gè)子進(jìn)程,再讓這個(gè)子進(jìn)程去執(zhí)行command參數(shù)的命令程序,成功運(yùn)行結(jié)束后返回到調(diào)用sysytem的進(jìn)程。詳情使用的話,去百度看就行。
(1)無(wú)關(guān)系,沒有以下三種關(guān)系234的情況下,可以認(rèn)為無(wú)關(guān)系。無(wú)關(guān)系就是說(shuō),進(jìn)程間是獨(dú)立的,進(jìn)程和進(jìn)程之間不可以隨便的訪問被的進(jìn)程的地址空間,不能隨便訪問別進(jìn)程中的東西
(2)父子進(jìn)程關(guān)系
(3)進(jìn)程組(group):由很多個(gè)進(jìn)程構(gòu)成了一個(gè)進(jìn)程組,為了讓這些進(jìn)程間的關(guān)系更加密切,組外的不可以隨便的訪問
(4)會(huì)話(session):會(huì)話就由很多個(gè)進(jìn)程組構(gòu)成的一個(gè)組,為了讓一個(gè)會(huì)話做的事情,會(huì)話外的不能隨便訪問
(1)單獨(dú)ps只能看到當(dāng)前終端的進(jìn)程,當(dāng)前是哪個(gè)終端,哪個(gè)進(jìn)程,對(duì)應(yīng)的時(shí)間和進(jìn)程程序是什么
(2)常用的ps帶的參數(shù):
ps -ajx:偏向于顯示操作系統(tǒng)各種進(jìn)程的ID號(hào)
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
父進(jìn)程ID 子進(jìn)程 進(jìn)程組 會(huì)話ID 終端 用戶ID 時(shí)間 對(duì)應(yīng)的程序名
ps -aux:偏向于顯示進(jìn)程所占系統(tǒng)的資源
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
當(dāng)前用戶 進(jìn)程ID CPU 內(nèi)存 虛擬內(nèi)存大小
(1)kill -信號(hào)編號(hào) 進(jìn)程ID //向一個(gè)進(jìn)程發(fā)送信號(hào)
(2)kill -9 xxx //這個(gè)是比較常用的,向xxx進(jìn)程發(fā)送編號(hào)為9的信號(hào),意思是關(guān)閉當(dāng)前這個(gè)進(jìn)程
(1)daemon:守護(hù)進(jìn)程的意思,簡(jiǎn)稱d,進(jìn)程名后面帶d的基本表示是一個(gè)守護(hù)進(jìn)程
(2)長(zhǎng)期運(yùn)行(一般開機(jī)運(yùn)行直到關(guān)機(jī)時(shí)關(guān)閉),而普通進(jìn)程如ls,運(yùn)行完一遍進(jìn)程就結(jié)束了
(3)與控制臺(tái)脫離,也就是說(shuō),終端關(guān)閉了,守護(hù)進(jìn)程還在運(yùn)行呢,而其他普通進(jìn)程,終端關(guān)了,進(jìn)程就關(guān)了,普通進(jìn)程和運(yùn)行該進(jìn)程的終端是相綁定的,終端關(guān)閉了,這個(gè)終端中的所有普通進(jìn)程都會(huì)關(guān)閉,原因還是在于會(huì)話這個(gè)概念,因?yàn)橐粋€(gè)終端中的所有進(jìn)程其實(shí)是一個(gè)會(huì)話,而守護(hù)進(jìn)程是不會(huì)被關(guān)閉的
(4)服務(wù)器(server):守護(hù)進(jìn)程一般是用來(lái)實(shí)現(xiàn)一個(gè)服務(wù)器的,服務(wù)器就是一個(gè)一直在運(yùn)行的程序,當(dāng)我們需要某種幫助的時(shí)候,服務(wù)器程序可以為我們提供,比如當(dāng)我們的程序想要nfs服務(wù)器的幫助實(shí)現(xiàn)nfs通信,我們就可以調(diào)用服務(wù)器程序得到服務(wù)器程序的幫助,可以調(diào)用服務(wù)器程序來(lái)進(jìn)行這些服務(wù)操作。
服務(wù)器程序一般都是守護(hù)進(jìn)程,都是被實(shí)現(xiàn)成守護(hù)進(jìn)程,所以具有長(zhǎng)期運(yùn)行和終端控制臺(tái)脫離的特點(diǎn),所以守護(hù)進(jìn)程其實(shí)就是一種特殊的進(jìn)程,不受終端控制臺(tái)約束的進(jìn)程,為我們整個(gè)系統(tǒng)提供某種服務(wù),當(dāng)我們需要某種服務(wù)的時(shí)候,就可以調(diào)用這個(gè)守護(hù)進(jìn)程的服務(wù)程序來(lái)進(jìn)行得到幫助
(4)我們自己也可以實(shí)現(xiàn)一個(gè)守護(hù)進(jìn)程,比如你寫的這個(gè)程序,你也想達(dá)到某種服務(wù),脫離終端控制臺(tái)的影響,就可以將其設(shè)計(jì)成守護(hù)進(jìn)程。
(1)syslogd,是系統(tǒng)日志守護(hù)進(jìn)程,提供syslogd功能
(2)cron, 這個(gè)守護(hù)進(jìn)程使用來(lái)實(shí)現(xiàn)操作系統(tǒng)的一個(gè)時(shí)間管理的,比如在Linux中要實(shí)現(xiàn)一個(gè)定時(shí)時(shí)間到了才執(zhí)行程序的功能就需要cron,比如定期垃圾清理,就可以定好時(shí)間,沒到這個(gè)時(shí)間就會(huì)執(zhí)行垃圾清理的這個(gè)程序,就要用到cron
守護(hù)進(jìn)程其實(shí)就是一個(gè)能長(zhǎng)期運(yùn)行,能脫離控制臺(tái)的約束的一個(gè)進(jìn)程,可以長(zhǎng)期運(yùn)行,我們需要的時(shí)候,可以通過調(diào)用守護(hù)進(jìn)程來(lái)得到想要的。因?yàn)橐恢痹谶\(yùn)行為我們服務(wù),所以可以說(shuō)每一個(gè)守護(hù)進(jìn)程是一個(gè)服務(wù)程序,很多個(gè)守護(hù)進(jìn)程在一起,就形成了服務(wù)器
(1)每一個(gè)守護(hù)進(jìn)程都可以由一個(gè)普通進(jìn)程實(shí)現(xiàn)成一個(gè)守護(hù)進(jìn)程,但是一般情況下,只有你的這個(gè)進(jìn)程你想讓他一直長(zhǎng)期運(yùn)行,最終和其他的守護(hù)進(jìn)程一起構(gòu)成一個(gè)服務(wù)器的情況下,才會(huì)讓他變成守護(hù)進(jìn)程
(2)我們可以寫一個(gè)函數(shù),create_daemon,目的是讓一個(gè)普通進(jìn)程一調(diào)用這個(gè)函數(shù),就會(huì)被實(shí)現(xiàn)變成守護(hù)進(jìn)程(3)create_daemon函數(shù)實(shí)現(xiàn)的要素
@1:子進(jìn)程等待父進(jìn)程退出
br/>(3)create_daemon函數(shù)實(shí)現(xiàn)的要素
@1:子進(jìn)程等待父進(jìn)程退出
@3:調(diào)用chdir將當(dāng)前工作目錄設(shè)置為/, chdir("/");目的是讓其不依賴于別的文件系統(tǒng),設(shè)置成根目錄就可以讓開機(jī)時(shí)就運(yùn)行,而且不依賴于別的文件系統(tǒng)@4:umask設(shè)置為0以取消任何文件權(quán)限屏蔽,確保將來(lái)這個(gè)進(jìn)程有最大的文件操作權(quán)限
br/>@4:umask設(shè)置為0以取消任何文件權(quán)限屏蔽,確保將來(lái)這個(gè)進(jìn)程有最大的文件操作權(quán)限
for (i=0; i<xxx; i++)
{
close(i);
}
xxx是當(dāng)前系統(tǒng)所擁有的一個(gè)進(jìn)程文件描述符的上限,這個(gè)是一個(gè)不定值,所以我們要?jiǎng)討B(tài)獲取當(dāng)前系統(tǒng)對(duì)一個(gè)進(jìn)程的文件描述符的最大值。通過一個(gè)函數(shù)來(lái)獲取,這個(gè)函數(shù)叫sysconf,這個(gè)函數(shù)可以獲取當(dāng)前操作系統(tǒng)的所有配置信息的,原型是 long sysconf(int name);你要獲取哪一個(gè)參數(shù)的信息,就把這個(gè)參數(shù)的名字傳進(jìn)去就行,參數(shù)的名字是用宏來(lái)體現(xiàn)的,可以通過man手冊(cè)來(lái)查找,獲取系統(tǒng)所允許打開的一個(gè)進(jìn)程文件描述符的上限數(shù)目的宏是OPEN_MAX或者_(dá)SC_OPEN_MAX,函數(shù)的返回值就是想要的數(shù)目
@6:將0、1、2定位到/dev/null。 這個(gè)設(shè)備文件相當(dāng)于OS中的一個(gè)垃圾堆。方法是:
open("/dev/null") //對(duì)應(yīng)返回的文件描述符是0
open("/dev/null") //對(duì)應(yīng)返回的文件描述符是1
open("/dev/null") //對(duì)應(yīng)返回的文件描述符是2
`void openlog(const char *ident, int option, int facility);`
@1:打開一個(gè)日志文件
@2:第一個(gè)參數(shù)是打開這個(gè)文件的程序的名字
@3:第二參數(shù)和第三個(gè)參數(shù)都是一些選項(xiàng),用宏定義來(lái)標(biāo)識(shí)的,看man手冊(cè)
@4:option
LOG_CONS 這個(gè)宏,當(dāng)日志本身壞了時(shí)候,或者寫不進(jìn)去的時(shí)候,就會(huì)將錯(cuò)誤信息輸出到控制臺(tái)
LOG_PID 這個(gè)宏,我們寫入到日志中信息每一條都會(huì)有我們寫入日志文件的這個(gè)進(jìn)程的PID
@5:facility 表示當(dāng)前寫入到日志中的信息,是一個(gè)什么樣的log信息,和什么有關(guān)的
LOG_AUTH 有這個(gè)宏,表示是和安全,檢驗(yàn),驗(yàn)證有關(guān)的
LOG_CRON 這個(gè)宏,表示是和定時(shí)的守護(hù)進(jìn)程相關(guān)的信息 clock daemon(cron an at)
LOG_FTP 如果是一個(gè)FTP服務(wù)器相關(guān)的信息,就是這個(gè)宏
LOG_USER 如果我們寫入到日志文件中的日志信息是普通的用戶層的不是什么特殊的,就這個(gè)宏
void syslog(int priority, const char *format, ...);
@1:第一個(gè)參數(shù)表示這條日志信息的重要程度,也是用宏來(lái)標(biāo)識(shí)的
@2:LOG_DEBUG 有這個(gè)宏表示是最不重要的日志信息
@3:LOG_INFO 有這個(gè)宏表示系統(tǒng)的正常輸出消息
@4:LOG_NOTICE 有這個(gè)宏表示這條日志信息,是比較顯著的,比較顯眼的,公告
@5:LOG_WARNING 是一條警告,要注意了
@6:LOG_ERR 錯(cuò)誤了,出錯(cuò)了
@7:LOG_CRIT 出現(xiàn)緊急的情況了,要馬上去處理
@8:LOG_ALERT 立刻要去行動(dòng)了,不然會(huì)完蛋
@9:LOG_EMERG 系統(tǒng)已經(jīng)不行了
void closelog(void);
一般log信息都在OS的/var/log/messages這個(gè)文件中存著的,但是Ubuntu中l(wèi)og信息是在/var/log/syslog文件中存著的。都是在虛擬文件系統(tǒng)中
操作系統(tǒng)中有一個(gè)syslogd守護(hù)進(jìn)程,開機(jī)運(yùn)行,關(guān)機(jī)結(jié)束這個(gè)守護(hù)進(jìn)程來(lái)負(fù)責(zé)日志文件的寫入和維護(hù)
,syslogd是獨(dú)立運(yùn)行的一個(gè)進(jìn)程,你不用他,他也在運(yùn)行。我們當(dāng)前的進(jìn)程可以通過調(diào)用openlog這個(gè)系統(tǒng)調(diào)用來(lái)打開一條和syslogd相連接的通道,然后通過syslog向syslogd這個(gè)守護(hù)進(jìn)程發(fā)消息,然后syslogd在將消息寫入到日志文件系統(tǒng)中
所以syslogd其實(shí)就是日志文件系統(tǒng)的一個(gè)服務(wù)器進(jìn)程,提供日志服務(wù)的。
(1)我們弄好了守護(hù)進(jìn)程的時(shí)候,守護(hù)進(jìn)程是長(zhǎng)時(shí)間運(yùn)行不退出的,除非關(guān)機(jī),所以我們運(yùn)行了幾次守護(hù)進(jìn)程,就會(huì)出現(xiàn)幾個(gè)守護(hù)進(jìn)程,這樣是不好的。
(2)我們希望一個(gè)程序是有單例運(yùn)行功能的,就是說(shuō)我們之前運(yùn)行了一程序后,已經(jīng)有了一個(gè)進(jìn)程了,我們不希望在出現(xiàn)一個(gè)這個(gè)進(jìn)程了,在運(yùn)行的這個(gè)程序的時(shí)候就會(huì)直接退出或者提示程序已經(jīng)在運(yùn)行了
(3)方法就是,在運(yùn)行這個(gè)程序的時(shí)候,我們?cè)谶@個(gè)程序的里面開始的位置,我們判斷一個(gè)文件是否存在,如果這個(gè)文件不存在的的話,我們的程序就會(huì)運(yùn)行,并且創(chuàng)建這個(gè)文件,就會(huì)出現(xiàn)一個(gè)進(jìn)程,如果這個(gè)文件存在的話,就會(huì)退出這個(gè)程序,不讓其繼續(xù)運(yùn)行,這樣就會(huì)保證有了單例功能。
(4)可以在一個(gè)程序的開始加上一個(gè)open("xxx", O_RDWR | O_TRUNC | O_CREAT | O_EXCL, 0664);
如果這個(gè)文件存在的話,就被excl這個(gè)標(biāo)志影響,open就會(huì)錯(cuò)誤,我們可以在后面用if來(lái)判斷errno這個(gè)值是否和EEXIST這個(gè)宏相等如果和這個(gè)宏相等了,就說(shuō)明是文件已經(jīng)存在引起的錯(cuò)誤,errno是在errno.h中定義的。當(dāng)我們這個(gè)進(jìn)程運(yùn)行結(jié)束時(shí),我們讓這個(gè)進(jìn)程在結(jié)束之前在把這個(gè)文件刪除掉,刪除一個(gè)文件用的函數(shù)是remove函數(shù),我們用atexit函數(shù)來(lái)注冊(cè)一個(gè)進(jìn)程結(jié)束之前運(yùn)行的一個(gè)清理函數(shù),讓這個(gè)函數(shù)中用remove函數(shù)來(lái)刪除這個(gè)文件
1、進(jìn)程間通信,就是進(jìn)程和進(jìn)程之間的通信,有可能是父子進(jìn)程之間的通信,同一個(gè)進(jìn)程組之間的通信,同一個(gè)會(huì)話中的進(jìn)程之間的通信,還有可能是兩個(gè)沒有關(guān)聯(lián)的進(jìn)程之間的通信.
2、之前我們都是在同一個(gè)進(jìn)程之間通信的,都是出于一個(gè)地址空間中的,譬如在一個(gè)進(jìn)程中的不同模塊中(不同的函數(shù),不同的文件a.c,b.c等之間是用形參和實(shí)參的方式,和全局變量的方式進(jìn)行通信的,a.c的全局變量,b.c中也可以用),最后形成了一個(gè)a.out程序,這個(gè)程序在運(yùn)行的時(shí)候就會(huì)創(chuàng)建一個(gè)進(jìn)程,進(jìn)程里去運(yùn)行這個(gè)程序。這是一個(gè)進(jìn)程中的通信。
3、兩個(gè)進(jìn)程之間的通信,兩個(gè)進(jìn)程是處在兩個(gè)不同的地址空間中的,也就是a進(jìn)程中的變量名字和值在a進(jìn)程的地址空間有一個(gè),但是在b進(jìn)程中的變量名字可以在b進(jìn)程的地址空間中和a進(jìn)程的變量名字一樣,因?yàn)閮烧呤窃趦蓚€(gè)不同的地址空間中的,每一個(gè)進(jìn)程都認(rèn)為自己獨(dú)自享有4G的內(nèi)存空間,這兩者實(shí)現(xiàn)的進(jìn)程間通信就不能通過函數(shù)或者全局變量進(jìn)行通信了,因?yàn)榛ハ嗟牡刂房床坏?,所以兩個(gè)進(jìn)程,兩個(gè)不同地址空間的通信是很難的。
4、一般像大型的程序才會(huì)用多進(jìn)程之間的通信,像GUI,服務(wù)器之類的,其他的程序大部分都是單進(jìn)程多線程的
5、Linux中提供了多種進(jìn)程間通信的機(jī)制
(1)無(wú)名管道和有名管道,這兩種管道可以提供一個(gè)父子進(jìn)程之間的進(jìn)程通信
(2)systemV IPC: 這個(gè)unix分裂出來(lái)的內(nèi)核中進(jìn)程通信有:信號(hào)量、消息隊(duì)列、共享內(nèi)存
(3)Socket域套接字(是BSD的unix分支發(fā)展出來(lái)的),Linux里面的網(wǎng)絡(luò)通信方式。最早Linux發(fā)明這個(gè)是為了實(shí)現(xiàn)進(jìn)程間通信的,實(shí)際上網(wǎng)絡(luò)通信也是進(jìn)程間的通信,一個(gè)電腦上的瀏覽器進(jìn)程和另一個(gè)電腦上的服務(wù)器程序進(jìn)程之間的通信
1和2只能是同一個(gè)操作系統(tǒng)之間的不同的進(jìn)程之間的通信,3是可以實(shí)現(xiàn)兩個(gè)電腦上的不同操作系統(tǒng)之間的進(jìn)程間通信
(4)信號(hào),a進(jìn)程可以給b進(jìn)程信號(hào)或者操作系統(tǒng)可以給不同的進(jìn)程發(fā)信號(hào)
6、Linux的IPC機(jī)制(進(jìn)程間通信)中的管道
1、說(shuō)管道的話一般指的是無(wú)名管道,有名管道我們一般都會(huì)明確的說(shuō)有名管道,有名管道是fifo
2、管道(無(wú)名管道):我們之間進(jìn)程之間是不能直接一個(gè)進(jìn)程訪問到另一個(gè)進(jìn)程的地址空間然后實(shí)現(xiàn)進(jìn)程間通信的,所以這種管道的通信方式的方法就是,在內(nèi)核中維護(hù)了一個(gè)內(nèi)存區(qū)域(可以看成是一個(gè)公共的緩沖區(qū)或者是一個(gè)管道),有讀端和寫端,管道是單向通信的,半雙工的,讓a進(jìn)程和b進(jìn)程通過這塊內(nèi)存區(qū)域來(lái)實(shí)現(xiàn)的進(jìn)程間的通信。
(1)、管道信息的函數(shù):pipe、write、read、close、pipe創(chuàng)建一個(gè)管道,就相當(dāng)于創(chuàng)建了兩個(gè)文件描述符,一個(gè)使用來(lái)讀的,一個(gè)是用來(lái)寫的,a進(jìn)程兩個(gè)文件描述符,b進(jìn)程兩個(gè)文件描述符,所以是a進(jìn)程在用寫的文件描述符像內(nèi)核維護(hù)的那一個(gè)內(nèi)存區(qū)域(管道)中寫東西的時(shí)候,b進(jìn)程要通過他的讀文件描述符read到管道中的a進(jìn)程寫的內(nèi)容。所以也就是半雙工的通信方式,但是我們?yōu)榱送ㄐ诺目煽啃裕瑫?huì)將這一個(gè)管道變成單工的通信方式,將a進(jìn)程的讀文件描述符fd=-1,不用這個(gè)讀文件描述符,將b進(jìn)程的寫文件描述符等于-1,也不用這個(gè),實(shí)現(xiàn)了單工的通信方式,這樣可以提高通信的可靠性,如果想要雙工的通信,就在pipe創(chuàng)建一個(gè)管道,將a進(jìn)程中的寫文件描述符fd廢棄掉,將b進(jìn)程的讀文件描述符廢棄掉,這樣兩個(gè)管道一個(gè)實(shí)現(xiàn)從左往右的通信,一個(gè)實(shí)現(xiàn)從右往左的通信,就實(shí)現(xiàn)了雙工,同時(shí)也提高了通信的可靠性。
(2)、管道通信只能在父子進(jìn)程之間通信
(3)管道的代碼實(shí)現(xiàn)思路:一般是先在父進(jìn)程中pipe創(chuàng)建一個(gè)管道然后fork子進(jìn)程,子進(jìn)程繼承父進(jìn)程的管道fd
3、有名管道(fifo)
(1)解決了只能在父子進(jìn)程之間通信的問題
(2)有名管道,其實(shí)也是內(nèi)核中維護(hù)的一個(gè)內(nèi)存區(qū)域,不過這塊內(nèi)存的表現(xiàn)形式是一個(gè)有名字的文件
(3)有名管道的使用方法:先固定一個(gè)文件名,然后我們?cè)趦蓚€(gè)進(jìn)程分別使用mkfifo創(chuàng)建fifo文件,然后分別open打開獲取到fd,然后一個(gè)讀一個(gè)寫。
(4)任何兩個(gè)進(jìn)程都可以實(shí)現(xiàn)半雙工的通信,因?yàn)橛忻艿朗莾?nèi)核維護(hù)的一個(gè)內(nèi)存區(qū)域,一個(gè)有名字的文件,所以我們要先固定一個(gè)文件的名字(這個(gè)文件是真是存在的,我們外在弄出來(lái)的),比如abcd.txt,然后我們?cè)趦蓚€(gè)進(jìn)程中分別用mkfifo(mkfifo的是同一個(gè)文件的名字)創(chuàng)建fifo管道文件,然后用open分別打開這個(gè)有名字的文件,一個(gè)進(jìn)程得到了兩個(gè)fd,然后一個(gè)讀一個(gè)寫
(5)有名管道的通信函數(shù):mkfifo、open、write、read、close
7、systemV IPC
(1)消息隊(duì)列:消息隊(duì)列其實(shí)就是內(nèi)核中維護(hù)一塊內(nèi)存區(qū)域,這個(gè)內(nèi)存區(qū)域是fifo的,是一個(gè)隊(duì)列,我們a進(jìn)程向這個(gè)隊(duì)列中放東西,我們b進(jìn)程從這個(gè)隊(duì)列中讀東西,fifo(隊(duì)列)是先進(jìn)先出的,是一種數(shù)據(jù)結(jié)構(gòu)。可以實(shí)現(xiàn)廣播,將信息發(fā)送出去,形成多個(gè)隊(duì)列。(適合于廣播,或者單播)
(2)信號(hào)量(適合于實(shí)現(xiàn)進(jìn)程通信間的互斥):信號(hào)量實(shí)質(zhì)就是一個(gè)計(jì)數(shù)器,更本質(zhì)的來(lái)說(shuō)其實(shí)就是一個(gè)用來(lái)計(jì)數(shù)的變量,這個(gè)信號(hào)量一般是用來(lái)實(shí)現(xiàn)互斥和同步的。比如開始的時(shí)候這個(gè)信號(hào)量int a =1,當(dāng)我們a進(jìn)程想要訪問一個(gè)公共的資源的時(shí)候,比如說(shuō)是一個(gè)函數(shù)的時(shí)候,我們a進(jìn)程就會(huì)去判斷這個(gè)信號(hào)量a是否等于,如果等于1,a進(jìn)程就知道當(dāng)前這個(gè)公共的資源函數(shù),沒有被別的進(jìn)程使用,a進(jìn)程就會(huì)用這個(gè)函數(shù),當(dāng)用的時(shí)候,a進(jìn)程會(huì)將信號(hào)量a=1,變成一個(gè)其他的值,這樣b進(jìn)程這個(gè)時(shí)候如果也想用這個(gè)公共的資源的時(shí)候,就會(huì)發(fā)現(xiàn)a這個(gè)信號(hào)變了,所以
b進(jìn)程就知道有別的進(jìn)程用這個(gè)函數(shù)了,所以b進(jìn)程就會(huì)等,等到a進(jìn)程用完了之后,a進(jìn)程就會(huì)信號(hào)量a的變成原先的1,這樣b進(jìn)程就知道別的進(jìn)程用完了,之后b進(jìn)程就會(huì)用這個(gè)函數(shù),這樣的類似的思路,就可以實(shí)現(xiàn)互斥的概念。同步也是可以實(shí)現(xiàn)的用信號(hào)量,比如a進(jìn)程走一步,就將信號(hào)量a的值加1,b進(jìn)程來(lái)判斷信號(hào)量a是否加1了,如果加1了,b進(jìn)程也跟著走一步,b走完以后就將信號(hào)量的值恢復(fù)到原先,a開始走,a將信號(hào)量加1,b又走,b又將信號(hào)量減1,因?yàn)閮蓚€(gè)進(jìn)程之間本身是異步的,因?yàn)榛ハ喽伎床坏綄?duì)方在做什么,但是可以通過信號(hào)量的這個(gè)機(jī)制,來(lái)實(shí)現(xiàn)節(jié)奏上的同步,隊(duì)列的感覺。
(3)共享內(nèi)存(適合于進(jìn)程通信時(shí)需要通信大量的信息量時(shí)),跟上面兩種的共享的內(nèi)存不同的是,這個(gè)共享內(nèi)存是大片的內(nèi)存。兩個(gè)進(jìn)程間的的虛擬地址同時(shí)映射到了物理內(nèi)存中一大片
8、剩余兩類IPC
(1)信號(hào)
(2)unix域套接字,socket
# multiprocessing內(nèi)置模塊包含了多進(jìn)程的很多方法
from multiprocessing import Process
import os
import time
def func(*args, **kwargs):
while True:
time.sleep(1)
print('子進(jìn)程 pid: ', os.getpid()) # 查看當(dāng)前進(jìn)程的pid
print('func的父進(jìn)程是: ', os.getppid()) # 查看父進(jìn)程的pid
print(args)
print(kwargs['haha'])
if __name__ == '__main__': # 在windows操作系統(tǒng)上,創(chuàng)建進(jìn)程啟動(dòng)進(jìn)程必須需要加上這句話,其他操作系統(tǒng)不需要
# target為要綁定注冊(cè)的函數(shù)(進(jìn)程), args為要傳給綁定注冊(cè)的函數(shù)(進(jìn)程)的位置參數(shù). kwargs為要傳給綁定注冊(cè)的函數(shù)(進(jìn)程的動(dòng)態(tài)關(guān)鍵字參數(shù)),kwargs參數(shù)是一個(gè)字典類型
p = Process(target=func,args=(5, 4, 3, 2, 1), kwargs={'haha' : 'hehe'}) # 將func函數(shù)注冊(cè)到進(jìn)程后得到一個(gè)進(jìn)程對(duì)象p # 創(chuàng)建了一個(gè)進(jìn)程對(duì)象p
p.start() # 開啟了子進(jìn)程,此時(shí)func就是相對(duì)于本父進(jìn)程的另外一個(gè)進(jìn)程,屬于另外一個(gè)應(yīng)用程序,操作系統(tǒng)創(chuàng)建一個(gè)新的進(jìn)程func子進(jìn)程,并且子進(jìn)程進(jìn)入就緒態(tài),當(dāng)有時(shí)間片時(shí)(也就是其他進(jìn)程(這里是父進(jìn)程)阻塞時(shí)),func子進(jìn)程就開始執(zhí)行了
while True:
time.sleep(2)
print('父進(jìn)程 pid: ', os.getpid()) # 查看當(dāng)前進(jìn)程的pid
print('__main__的父進(jìn)程是: ', os.getppid()) # 查看父進(jìn)程的pid
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。