關(guān)于建設(shè)官方網(wǎng)站的申請(qǐng)品牌如何做推廣
環(huán)境:centos7.6,騰訊云服務(wù)器
Linux文章都放在了專欄:【Linux】歡迎支持訂閱🌹
相關(guān)文章推薦:
【Linux】馮.諾依曼體系結(jié)構(gòu)與操作系統(tǒng)
進(jìn)程概念
什么是進(jìn)程?
進(jìn)程是什么?我們打開任務(wù)管理器可以看到有很多的程序正在運(yùn)行狀態(tài),并且上面寫著進(jìn)程二字。難道進(jìn)程就是指這些被運(yùn)行起來的程序嗎?課本上對(duì)于進(jìn)程是這么說的:程序的一個(gè)執(zhí)行實(shí)例,正在執(zhí)行的程序等。

windows下的進(jìn)程
但是實(shí)際上這種說法并不完全準(zhǔn)確?(舉個(gè)例子,一個(gè)僅僅進(jìn)入學(xué)校的人,并不能算是這個(gè)學(xué)校的學(xué)生,只有這個(gè)人的信息被加載到學(xué)校的教務(wù)系統(tǒng),并且被這個(gè)學(xué)校所管理的人,才稱得上學(xué)生。這里的學(xué)校就是指內(nèi)存,數(shù)據(jù)只有加載到內(nèi)存,并被OS所管理,這才算是一個(gè)完整的進(jìn)程)。

os管理進(jìn)程
在前文我們知道了cpu一般不會(huì)與外設(shè)進(jìn)行直接溝通,而是與內(nèi)存打交道。所以我們?cè)诖疟P上的程序被運(yùn)行時(shí),要將數(shù)據(jù)與代碼加載到內(nèi)存中。由操作系統(tǒng)來進(jìn)行管理。具體怎么來管理呢?先描述,再組織。
所謂的先描述實(shí)際上是指OS會(huì)用一個(gè)特定的結(jié)構(gòu)體(PCB/task_struct)來提取該進(jìn)程的各種屬性(這里的屬性與加載到內(nèi)存中的數(shù)據(jù)與代碼無關(guān),或者說僅僅只有一點(diǎn)點(diǎn)的關(guān)系,即可以通過對(duì)應(yīng)的 task_struct找到該進(jìn)程的代碼和數(shù)據(jù))。組織是指OS會(huì)以鏈表或者其它的數(shù)據(jù)結(jié)構(gòu)將各個(gè)task_struct組織起來,方便管理。(對(duì)進(jìn)程的修改--->對(duì)鏈表或者其它數(shù)據(jù)結(jié)構(gòu)的增刪查改,比如我們結(jié)束一個(gè)進(jìn)程,實(shí)際上就是刪掉數(shù)據(jù)結(jié)構(gòu)中對(duì)應(yīng)的pcb)
★上面啰嗦一大堆,無非就是說兩個(gè)事:
結(jié)論一:進(jìn)程=os內(nèi)核關(guān)于該進(jìn)程的相關(guān)數(shù)據(jù)結(jié)構(gòu)(PCB/task_struct)+當(dāng)前程序加載到內(nèi)存的代碼與數(shù)據(jù)。
結(jié)論二:OS如何管理進(jìn)程?先描述(pcb/task_struct),再組織(鏈表等數(shù)據(jù)結(jié)構(gòu))。
PCB — 進(jìn)程控制塊
進(jìn)程需要被OS管理,管理的本質(zhì)就是先描述,再組織。而PCB就是用來描述進(jìn)程的一種特定結(jié)構(gòu)體。在Linux系統(tǒng)下的PCB就是task_struct。
task_struct內(nèi)容分類
task_struct結(jié)構(gòu)體中主要包含了以下信息,了解一下即可:
標(biāo)示符: 描述本進(jìn)程的唯一標(biāo)示符,用來區(qū)別其他進(jìn)程。
狀態(tài): 任務(wù)狀態(tài),退出代碼,退出信號(hào)等。
優(yōu)先級(jí): 相對(duì)于其他進(jìn)程的優(yōu)先級(jí)。
程序計(jì)數(shù)器: 程序中即將被執(zhí)行的下一條指令的地址。
內(nèi)存指針: 包括程序代碼和進(jìn)程相關(guān)數(shù)據(jù)的指針,還有和其他進(jìn)程共享的內(nèi)存塊的指針
上下文數(shù)據(jù): 進(jìn)程執(zhí)行時(shí)處理器的寄存器中的數(shù)據(jù)[休學(xué)例子,要加圖CPU,寄存器]。
I/O狀態(tài)信息: 包括顯示的I/O請(qǐng)求,分配給進(jìn)程的I/O設(shè)備和被進(jìn)程使用的文件列表。
記賬信息: 可能包括處理器時(shí)間總和,使用的時(shí)鐘數(shù)總和,時(shí)間限制,記賬號(hào)等。
其他信息
如何查看進(jìn)程
第一種方法:通過ps指令
我們輸入ps axj就可以查看當(dāng)前所有進(jìn)程信息,同時(shí),由于進(jìn)程信息較多,我們可以利用之前學(xué)過的管道,以及grep用來篩選過濾,從而拿到我們想要的進(jìn)程信息。(ps -l可以查看當(dāng)前bash下的進(jìn)程信息)如下:

第二種方法:通過函數(shù)getpid()
在上面task_struct內(nèi)容中,有一個(gè)是標(biāo)識(shí)符,用來區(qū)分其他進(jìn)程。這里的標(biāo)識(shí)符,實(shí)際就是指PID。每一個(gè)進(jìn)程都有自己的pid,并且每一次運(yùn)行,pid的值都會(huì)發(fā)生變化(一般都是逐漸遞增,當(dāng)下一次重新登陸時(shí),又是隨機(jī)值)。

而我們可以通過系統(tǒng)調(diào)用函數(shù)getpid()來獲取當(dāng)前進(jìn)程的pid。我們可以用man手冊(cè)來查詢getpid()的使用。我們可以通過如下的簡(jiǎn)單代碼來驗(yàn)證。
#include<stdio.h>
//頭文件包含
#include<unistd.h>
#include<sys/types.h>
int main()
{while(1){//獲取當(dāng)前進(jìn)程pidprintf("hello world,我的pid:%d\n",getpid()); sleep(1); } return 0; }

第三種方法:在路徑/proc下查看
事實(shí)上,我們?cè)趫?zhí)行一個(gè)程序時(shí),會(huì)在/proc目錄下生成一個(gè)以當(dāng)前程序的pid命名的目錄文件,該目錄文件內(nèi)包含了當(dāng)前進(jìn)程的所有信息。并且還有一個(gè)特點(diǎn):當(dāng)該程序停止運(yùn)行時(shí),以pid命名的文件會(huì)自動(dòng)銷毀。

如何中止進(jìn)程
★三種方法:
我們可以通過指令kill -9進(jìn)程pid來中止進(jìn)程
通過熱鍵ctrl c來中止當(dāng)前進(jìn)程
通過指令killall 進(jìn)程名稱 來中止進(jìn)程

三種方式終止進(jìn)程
父子進(jìn)程
PPID
進(jìn)程之間存在父子關(guān)系,我們知道,bash是命令行解釋器,當(dāng)我們?cè)诿钚休斎胫噶顖?zhí)行一個(gè)進(jìn)程時(shí),我們執(zhí)行的進(jìn)程就是bash的子進(jìn)程。究竟是否如我們所說?我們可以驗(yàn)證一下,當(dāng)然,在此之前我們要先談一下PPID,PPID就是當(dāng)前進(jìn)程的父進(jìn)程的pid。

fork創(chuàng)建子進(jìn)程
我們也可以通過系統(tǒng)調(diào)用函數(shù)fork用來給當(dāng)前進(jìn)程創(chuàng)建子進(jìn)程。我們用man手冊(cè)查詢fork的用法。

我們可以通過如下代碼來進(jìn)行簡(jiǎn)單測(cè)試:
#include<stdio.h>
#include<assert.h>
#include<unistd.h>
int main()
{//我們用ret來接受fork的返回值pid_t ret=fork();assert(ret!=-1);//斷言一下//我們通常使用if語句,用來進(jìn)行執(zhí)行流分流if(ret == 0) { while(1){ //此時(shí)是子進(jìn)程,fork給子進(jìn)程返回0printf("我是子進(jìn)程,pid:%d,ppid:%d,ret:%d,&ret:%p\n",getpid(),getppid(),ret,&ret); sleep(1); } } else if(ret >0){ while(1){ //此時(shí)是父進(jìn)程,fork給父進(jìn)程返回子進(jìn)程的pidprintf("我是父進(jìn)程,pid:%d,ppid:%d,ret:%d,&ret:%p\n",getpid(),getppid(),ret,&ret);sleep(1); } }return 0;}

運(yùn)行結(jié)果
我們發(fā)現(xiàn),確實(shí)如此,不過這里有一個(gè)疑問,為什么ret的地址相同,但是ret的值卻不相同呢?我們從未見過這種現(xiàn)象:同一個(gè)變量竟然有兩個(gè)返回值。這是為什么呢?實(shí)際上,我們?cè)诤瘮?shù)的最后有一個(gè)return 0,而fork之后的代碼是父子進(jìn)程共享的,也就是說,return這個(gè)語句被執(zhí)行了兩次,并且當(dāng)return執(zhí)行時(shí),函數(shù)體內(nèi)部是已經(jīng)執(zhí)行完了的。所以會(huì)有兩個(gè)返回值。
為了驗(yàn)證fork之后的代碼,被父子進(jìn)程共享,我們可以寫一個(gè)簡(jiǎn)簡(jiǎn)單單的代碼用來測(cè)試一下:
#include<stdio.h>
#include<unistd.h>int main(){int ret = fork();//這里只有一個(gè)printfprintf("hello proc,pid:%d,ppid:%d,ret:%d\n", getpid(),getppid(),ret); sleep(1);return 0;}

運(yùn)行結(jié)果
我們發(fā)現(xiàn),確實(shí)如此。不僅如此,進(jìn)程還具有獨(dú)立性,我們可以定義一個(gè)全局變量,父進(jìn)程對(duì)
全局變量進(jìn)行修改,我們發(fā)現(xiàn)不會(huì)影響到子進(jìn)程中的那個(gè)全局變量。我們也可以進(jìn)行驗(yàn)證:
#include<stdio.h>
#include<unistd.h>
//定義全局變量
int a=100;
int main(){pid_t ret=fork();if(ret == 0){while(1){//子進(jìn)程,不對(duì)a做修改printf("我是子進(jìn)程,a=%d,&a=:%p\n",a,&a);sleep(1);}}else {while(1){//父進(jìn)程,對(duì)全局變量a進(jìn)行修改 a+=100;//假如進(jìn)程不具有獨(dú)立性,那么父進(jìn)程的修改也會(huì)影響子進(jìn)程,究竟會(huì)不會(huì)呢?printf("我是父進(jìn)程,a=%d,&a=:%p\n",a,&a);sleep(1);}}return 0;}

結(jié)果
我們發(fā)現(xiàn)確實(shí)如此,不過為什么同一個(gè)地址,對(duì)內(nèi)容進(jìn)行修改卻不會(huì)互相影響呢?實(shí)際上這里的地址并不是真正的物理地址,并且當(dāng)進(jìn)程嘗試對(duì)數(shù)據(jù)進(jìn)行修改時(shí),還會(huì)觸發(fā)寫時(shí)拷貝。(具體放在后面進(jìn)程地址空間章節(jié)詳細(xì)講解)。
總結(jié)
★上面寫了這么多,總結(jié)如下:
命令行啟動(dòng)的程序,都會(huì)變成bash的子進(jìn)程
我們可以通過fork為當(dāng)前進(jìn)程創(chuàng)建子進(jìn)程,fork的返回值給子進(jìn)程返回0,給父進(jìn)程返回子進(jìn)程的pid,創(chuàng)建進(jìn)程失敗時(shí)返回-1
fork之后的代碼被父子進(jìn)程共享(但是誰先運(yùn)行不確定)
進(jìn)程具有獨(dú)立性,父子進(jìn)程也是如此,對(duì)其中一個(gè)進(jìn)程的修改不會(huì)影響另一個(gè)進(jìn)程
獨(dú)立性體現(xiàn)在兩方面:1、代碼方面是可讀的
2、數(shù)據(jù)方面,當(dāng)一個(gè)執(zhí)行流嘗試修改數(shù)據(jù)時(shí),OS會(huì)給我們的進(jìn)程觸發(fā)寫時(shí)拷貝(后面章節(jié)詳細(xì)講解)。
end.
生活原本沉悶,但跑起來就會(huì)有風(fēng)!🌹