asp網(wǎng)站制作實(shí)例教程目前網(wǎng)絡(luò)推廣平臺
?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??作者主頁:? ? ?作者主頁
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 本篇博客專欄:Linux專欄
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??創(chuàng)作時間 :2024年9月28日
基本概念:
進(jìn)程說白了其實(shí)就是一個程序的執(zhí)行實(shí)例,正在執(zhí)行的程序。
在內(nèi)核層面來說,就是一個擔(dān)當(dāng)分配資源(CPU時間,內(nèi)存)的實(shí)體
寫過代碼的都知道,當(dāng)你的代碼進(jìn)行編譯鏈接之后就會形成一個可執(zhí)行的程序了,這個程序本質(zhì)上是一個文件,是放在磁盤上的。當(dāng)我們雙擊這個程序讓他運(yùn)行起來之后,本質(zhì)上是讓這個程序加載到內(nèi)存當(dāng)中去了,因?yàn)橹挥屑虞d到內(nèi)存當(dāng)中去CPU才能對他進(jìn)行逐語句執(zhí)行,而一旦將這個程序加載到內(nèi)存之后,我們就不應(yīng)該叫他程序了,嚴(yán)格意義上應(yīng)該稱他為進(jìn)程。
描述進(jìn)程-PCB
系統(tǒng)中可以同時存在大量的進(jìn)程,當(dāng)我們使用ps aux命令時便可以看見此時存在的所有進(jìn)程
當(dāng)我們電腦開機(jī)時,打開的第一個程序其實(shí)就是操作系統(tǒng)(即操作系統(tǒng)是第一個加載到系統(tǒng)中的),我們都知道操作系統(tǒng)是管理工作的,其中一個就是進(jìn)程管理,那么我們電腦上這么多的進(jìn)程,操作系統(tǒng)是如何進(jìn)行管理的呢?
這時我想首先告訴大家一個六字真言:就是先描述,再組織,操作系統(tǒng)管理也是如此,操作系統(tǒng)作為管理者是不需要直接和被管理者進(jìn)行溝通的,當(dāng)一個進(jìn)程出現(xiàn)時,操作系統(tǒng)會直接對其進(jìn)行描述,然后對他的管理其實(shí)就是對其描述信息的管理,進(jìn)程信息被放在一個叫做進(jìn)程控制塊的數(shù)據(jù)結(jié)構(gòu)中,可以理解為進(jìn)程屬性的集合,這個就是PCB。
操作系統(tǒng)將每一個進(jìn)程進(jìn)行描述,形成一個個的進(jìn)程控制塊(PCB),并且通過雙鏈表的形式將他們連接起來。
這樣,操作系統(tǒng)只要拿到這個雙鏈表的頭指針就可以對這個雙鏈表進(jìn)行管理,這樣操作系統(tǒng)對各個進(jìn)程的管理就變成了對雙鏈表的管理。
例如我們創(chuàng)建一個進(jìn)程時首先就是將改進(jìn)程的代碼和數(shù)據(jù)加載到內(nèi)存,然后操作系統(tǒng)對此進(jìn)程進(jìn)行描述形成對應(yīng)的進(jìn)程控制塊(PCB),并將這個PCB插入到雙鏈表中,想要退出這個進(jìn)程時就直接從雙鏈表當(dāng)中刪去這個節(jié)點(diǎn)(PCB)即可。這樣一來,操作系統(tǒng)對于進(jìn)程的管理就變成了對一個雙鏈表的增刪查改。
task_struct --PCB的一種
進(jìn)程控制塊(PCB)是描述進(jìn)程的,再c++中我們稱之為面向?qū)ο?#xff0c;而在c語言當(dāng)中我們稱之為結(jié)構(gòu)體,因?yàn)長inux是用C語言寫的,當(dāng)然PCB也是用c語言寫的了,也就是用結(jié)構(gòu)體來實(shí)現(xiàn)的。
- PCB實(shí)際上是對進(jìn)程控制塊的統(tǒng)稱,在Linux中描述進(jìn)程的結(jié)構(gòu)體叫做task_struct
- task_struct是Linux中的一種數(shù)據(jù)結(jié)構(gòu),他會被裝載到RAM(內(nèi)存)里并包含進(jìn)程的信息
task_struct 內(nèi)容分類
task_struct 就是Linux中的進(jìn)程控制塊,它包含著以下的一些信息。
- 標(biāo)識符:描述本進(jìn)程的唯一標(biāo)識符,用來區(qū)別其他標(biāo)識符
- 狀態(tài):任務(wù)狀態(tài),退出代碼,推出信號等
- 優(yōu)先級:相對于其他進(jìn)程的優(yōu)先級
- 程序計(jì)數(shù)器:程序中即將被執(zhí)行的下一條指令的地址
- 內(nèi)存地址:包含程序代碼和進(jìn)程相關(guān)的指針,還有和其他進(jìn)程共享的內(nèi)存塊中的指針
- 上下文數(shù)據(jù):進(jìn)程執(zhí)行時處理器的寄存器中的數(shù)據(jù)
- I/O狀態(tài)信息:包含顯示的I/O請求等
- 記賬信息:可能包含處理器時間的總和等
- 其他信息
查看進(jìn)程
通過系統(tǒng)目錄來查看信息:
在根目錄下有一個名為proc的系統(tǒng)文件夾,其中包含了大量進(jìn)程信息,其中一些子目錄的名字為數(shù)字
這些數(shù)字其實(shí)是一某一進(jìn)程的PID,對應(yīng)的文件夾中記錄中對應(yīng)進(jìn)程的各種信息,想要查看直接輸入ls /proc/對應(yīng)的數(shù)字 即可
通過ps命令查看:
ps與對應(yīng)的指令想疊加,便可以顯示對應(yīng)的進(jìn)程的信息?
ps aux | head -1 && ps aux | grep proc | grep -v grep
通過系統(tǒng)調(diào)用獲得對應(yīng)的進(jìn)程的PID和PPID
通過系統(tǒng)調(diào)用函數(shù),getpid和getppid兩個函數(shù)分別可以獲得對應(yīng)進(jìn)程的PID和PPID
當(dāng)我們執(zhí)行該程序之后,這個程序會不停的執(zhí)行下去
我們通過ps命令得到對應(yīng)進(jìn)程的PID和PPID,就可以發(fā)現(xiàn)和getpid與getppid得到的值是一模一樣的
通過系統(tǒng)調(diào)用創(chuàng)建進(jìn)程-fork初始
fork函數(shù)創(chuàng)建子程序
加入fork函數(shù)之后,運(yùn)行結(jié)果如下:
循環(huán)打印兩行數(shù)據(jù),這兩行數(shù)據(jù)分別為:第一行是該進(jìn)程的PID和PPID,第二行是fork創(chuàng)建的子進(jìn)程的PID和PPID,我們可以發(fā)現(xiàn)fork創(chuàng)建的子進(jìn)程的PPID是子進(jìn)程的PID,說明這兩個進(jìn)程的關(guān)系為父子關(guān)系。
同樣,操作系統(tǒng)也會為這個新建的進(jìn)程創(chuàng)建PCB。
我們知道加載到內(nèi)存中的數(shù)據(jù)和代碼是屬于父進(jìn)程的,那么子進(jìn)程的數(shù)據(jù)和代碼又是從哪里來的呢?
我們可以看到,fork之前的代碼是父進(jìn)程自己執(zhí)行的,之后的代碼是父子進(jìn)程都執(zhí)行
需要注意的是,雖然父子進(jìn)程共享代碼了,但是其實(shí)是各自開辟空間(采用寫時拷貝);
小貼士:使用fork函數(shù)創(chuàng)建子進(jìn)程后就有了兩個進(jìn)程,這兩個進(jìn)程的調(diào)度順序是不確定的,取決于操作系統(tǒng)調(diào)度算法具體的實(shí)現(xiàn)。
fork返回值:
1.如果fork函數(shù)創(chuàng)建子進(jìn)程成功,那么在父進(jìn)程中返回父進(jìn)程的pid,子進(jìn)程的返回0
2.如果創(chuàng)建失敗,那么父進(jìn)程的返回-1.
Linux運(yùn)行狀態(tài):
一個進(jìn)程都創(chuàng)建而產(chǎn)生到因撤銷而銷毀的整個生命期間,有時占有處理器執(zhí)行,有時雖然可以運(yùn)行但是分不到處理器。有時雖然有空閑處理器但是由于待某個時間的發(fā)生而無法執(zhí)行,這一切就說明進(jìn)程和程序有區(qū)別,進(jìn)程是活動的且狀態(tài)變化的,所以叫做進(jìn)程
這里我們具體談一下進(jìn)程中的一些狀態(tài):
Linux源代碼中對于一些狀態(tài)的定義:
/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char *task_state_array[] = {"R (running)", /* 0*/"S (sleeping)", /* 1*/"D (disk sleep)", /* 2*/"T (stopped)", /* 4*/"T (tracing stop)", /* 8*/"Z (zombie)", /* 16*/"X (dead)" /* 32*/
};
運(yùn)行狀態(tài)——R
一個程序處于運(yùn)行狀態(tài),并不一定一定處于運(yùn)行當(dāng)中,一個進(jìn)程處于R狀態(tài),表示處于運(yùn)行當(dāng)中或者處于運(yùn)行隊(duì)列(runqueue)當(dāng)中,所有會存在多個進(jìn)程同時處于R狀態(tài)。
淺睡眠狀態(tài)——S
一個進(jìn)程處于淺睡眠狀態(tài),也叫可中斷睡眠狀態(tài),意味著等待某件事情的完成,處于淺睡眠狀態(tài)的進(jìn)程隨時可以被喚醒,也可以隨時被殺掉
當(dāng)我們在一個進(jìn)程中加入sleep(100),意思就是在這里休息一百秒,此時編譯運(yùn)行之后就會出現(xiàn)淺睡眠狀態(tài)。
深睡眠狀態(tài)——D
一個進(jìn)程處于深度睡眠狀態(tài),表示這個進(jìn)程不可以被殺掉,即便是操作系統(tǒng)也是不可以的,只有這個進(jìn)程自動喚醒才可以恢復(fù),該進(jìn)程也被稱為不可中睡眠狀態(tài)。
例如:某一進(jìn)程對磁盤進(jìn)行寫入操作時,再寫入期間,就會處于D狀態(tài),是無法被殺掉的,因?yàn)樵撨M(jìn)程必須磁盤回復(fù)是否寫入成功,以做出相應(yīng)的回應(yīng)
暫停狀態(tài)——T
在Linux中,我們可以通過向進(jìn)程發(fā)送SIGSTOP信號使進(jìn)程進(jìn)入暫停狀態(tài)(T),發(fā)送SIGCONT可以讓處于暫停狀態(tài)的進(jìn)程繼續(xù)運(yùn)行。
僵尸狀態(tài)——Z
當(dāng)一個進(jìn)程將要退出的時候,在系統(tǒng)層面,這個系統(tǒng)曾經(jīng)申請的資源并不會被馬上釋放,而是暫時存儲一段時間,以供操作系統(tǒng)或者其父進(jìn)程進(jìn)行讀取,如果信息一直未被讀取,則相關(guān)數(shù)據(jù)會一直存在,不會被釋放掉的,如果一個進(jìn)程等待著數(shù)據(jù)被讀取,那么我們就說他正處在僵尸狀態(tài)。
僵尸狀態(tài)時應(yīng)該存在的,因?yàn)槲覀冋{(diào)用一個程序時,調(diào)用方是應(yīng)該知道這個完成情況的,所以僵尸狀態(tài)必須是要存在的,以便后續(xù)的相關(guān)操作。
例如我們在編寫程序時都會在最后寫一個return 0,它的作用就是告訴操作系統(tǒng)這個程序順利完成結(jié)束了。
在Linux中,我們可以通過echo $命令來獲取最近的一次進(jìn)程的退出碼
死亡狀態(tài)——X
死亡狀態(tài)只是一個返回狀態(tài),當(dāng)一個進(jìn)程的信息被退出之后,該進(jìn)程申請的資源會立即被釋放,所以你不會在進(jìn)程狀態(tài)中看到死亡狀態(tài)。
僵尸進(jìn)程
前面我們已經(jīng)說過僵尸狀態(tài)的概念,相信大家也有了一個大致的了解,而處于僵尸狀態(tài)的進(jìn)程,就被稱為僵尸進(jìn)程。
例如,對于下面的代碼,當(dāng)程序執(zhí)行五次之后,子進(jìn)程便會退出,但是父進(jìn)程不知道它退出了,那么此時子進(jìn)程就處于僵尸狀態(tài)。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{printf("I am running...\n");pid_t id = fork();if(id == 0){ //childint count = 5;while(count){printf("I am child...PID:%d, PPID:%d, count:%d\n", getpid(), getppid(), count);sleep(1);count--;}printf("child quit...\n");exit(1);}else if(id > 0){ //fatherwhile(1){printf("I am father...PID:%d, PPID:%d\n", getpid(), getppid());sleep(1);}}else{ //fork error}return 0;
}
運(yùn)行該代碼之后,我們可以通過下面這個簡單的腳本進(jìn)行監(jiān)控
while :; do ps axj | head -1 && ps axj | grep proc | grep -v grep;echo "######################";sleep 1;done
運(yùn)行之后我們發(fā)現(xiàn):當(dāng)子進(jìn)程退出之后,子進(jìn)程就會編程僵尸狀態(tài)。
僵尸進(jìn)程的危害:
- 僵尸進(jìn)程的退出狀態(tài)會一直維持下去,因?yàn)樗枰嬖V父進(jìn)程執(zhí)行的相應(yīng)的結(jié)果信息。但是父進(jìn)程一直在不停的執(zhí)行,所以子進(jìn)程就一直處于僵尸狀態(tài)
- 僵尸進(jìn)程的信息會一直存在與task_struct(PCB)中,所以PCB就需要一直去維護(hù)
- 若一個父進(jìn)程創(chuàng)建了多個子進(jìn)程,并且不對其進(jìn)行回收,那么就會造成資源浪費(fèi),因?yàn)閿?shù)據(jù)結(jié)構(gòu)對象本身就要占據(jù)內(nèi)存
- 僵尸進(jìn)程越來越多,申請的資源無法進(jìn)行回收,那么僵尸進(jìn)程越多,實(shí)際可用的資源就越少,也就是說僵尸進(jìn)程會造成內(nèi)存泄漏
孤兒進(jìn)程:
孤兒進(jìn)程是指在操作系統(tǒng)中,其父進(jìn)程已經(jīng)結(jié)束(正?;虍惓=K止),但該進(jìn)程本身還在繼續(xù)運(yùn)行的進(jìn)程。當(dāng)一個進(jìn)程創(chuàng)建子進(jìn)程后,如果父進(jìn)程在子進(jìn)程結(jié)束之前就已經(jīng)退出,那么子進(jìn)程就會成為孤兒進(jìn)程。例如,一個父進(jìn)程啟動了一個子進(jìn)程,之后父進(jìn)程由于某種原因(如完成了它的任務(wù)或者遇到了錯誤而終止)退出,此時子進(jìn)程就變成了孤兒進(jìn)程。
對于以下代碼,父進(jìn)程執(zhí)行五次后退出,子進(jìn)程就變成了孤兒進(jìn)程:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{printf("I am running...\n");pid_t id = fork();if(id == 0){ //childint count = 5;while(1){printf("I am child...PID:%d, PPID:%d\n", getpid(), getppid(), count);sleep(1);}}else if(id > 0){ //fatherint count = 5;while(count){printf("I am father...PID:%d, PPID:%d, count:%d\n", getpid(), getppid(), count);sleep(1);count--;}printf("father quit...\n");exit(0);}else{ //fork error}return 0;
}
觀察代碼運(yùn)行,會發(fā)現(xiàn)父進(jìn)程退出后,子進(jìn)程的PPID變成了1,這就說明他被一號進(jìn)程領(lǐng)養(yǎng)了
進(jìn)程優(yōu)先級:
基本概念:
什么是進(jìn)程優(yōu)先級?優(yōu)先級實(shí)際上就是進(jìn)程獲取某些資源的先后順序,而進(jìn)程優(yōu)先級實(shí)際上就是進(jìn)程獲取CPU資源分配的先后順序,就是指進(jìn)程的優(yōu)先權(quán),優(yōu)先權(quán)高的進(jìn)程具有優(yōu)先執(zhí)行的權(quán)力
為什么要有進(jìn)程優(yōu)先級?
進(jìn)程優(yōu)先級存在的主要原因就是我們的資源是有限的,就像我們的電腦,一般都是單CPU的,一個CPU一次只能執(zhí)行一個進(jìn)程,而進(jìn)程有多個,所以需要存在進(jìn)程優(yōu)先級,確定獲取CPU資源的先后順序。
查看系統(tǒng)進(jìn)程
在Linux中輸入ps -l可以看到以下的東西:
我列出其中重要的幾個信息:
- UID:代表著執(zhí)行者的身份
- PID:代表這個進(jìn)程的代號
- PPID:代表著這個進(jìn)程由哪一個進(jìn)程發(fā)展而來,即父進(jìn)程的代號
- PRI:代表這個進(jìn)程的可被執(zhí)行的優(yōu)先級,值越小越早被執(zhí)行
- NI:代表著這個進(jìn)程的nice值
PRI和NI
- PRI代表著進(jìn)程的優(yōu)先級,這個值越小進(jìn)程的優(yōu)先級越高
- NI代表著nice值,其表示進(jìn)程可被執(zhí)行的優(yōu)先級的修正數(shù)值
- PRI(new)=PRI(old)+NI;
- 若NI值為負(fù)值,那么PRI變小,優(yōu)先級變高
- 調(diào)整進(jìn)程的優(yōu)先級,在Linux下就是調(diào)整NI,即nice值
- NI的范圍是-20-19,也就是說進(jìn)程優(yōu)先級一共分為四十個級別
注意:在Linux系統(tǒng)中,PRI默認(rèn)的值是80,也就是PRI=80+NI。
當(dāng)我們創(chuàng)建一個進(jìn)程之后,我們可以通過ps -al查看進(jìn)程的優(yōu)先級。
通過top命令修改進(jìn)程優(yōu)先級
這里的top命令其實(shí)就相當(dāng)于Windows中的任務(wù)管理器,可以調(diào)整進(jìn)程優(yōu)先級。
使用top命令后按r,然后輸入要調(diào)整的進(jìn)程的PID,然后調(diào)整后的nice值即可
注意:要想將nice調(diào)整為負(fù)值,需要加上sudo提升權(quán)限
通過renice調(diào)整進(jìn)程優(yōu)先級
輸入 renice +更改后的nice +PID即可
四個重要概念:
競爭性:由于只有一個CPU,所以資源有限,會出現(xiàn)資源競爭,為了高效完成任務(wù),合理分配CPU資源,所以會出現(xiàn)進(jìn)程優(yōu)先級
獨(dú)立性:多進(jìn)程之間運(yùn)行需要獨(dú)享各種資源,運(yùn)行期間互不打擾
并發(fā):即多個進(jìn)程在一個進(jìn)程下采用進(jìn)程切換的方式,在一段時間段內(nèi),讓多個進(jìn)程都得以共同推進(jìn),稱之為并發(fā)
并行:多個進(jìn)程在多個CPU下同時進(jìn)行
環(huán)境變量:
基本概念
環(huán)境變量一般是指在操作系統(tǒng)中指定操作系統(tǒng)運(yùn)行環(huán)境的一些參數(shù)
常見環(huán)境變量
- PATH:指定命令的搜索路徑
- HOME:指定用戶的主工作目錄
- SHELL:當(dāng)前shell,他的值一般是/bin/bash
查看當(dāng)前環(huán)境變量的方法
使用echo來查看:
echo $NAME //NAME為待查看的環(huán)境的名稱
測試PATH
大家有沒有想過這樣一個問題:為什么執(zhí)行l(wèi)s命令時不用帶./,而在執(zhí)行我們自己的可執(zhí)行程序時就必須要帶上?
容易理解的是,我們在執(zhí)行一個程序的時候,必須要先找到他在哪里,既然不帶ls就可以執(zhí)行l(wèi)s,說明操作系統(tǒng)可以找到他,而系統(tǒng)找不到我們的可執(zhí)行程序,必須帶上./來說明他在我們的當(dāng)前目錄下。
而系統(tǒng)就是通過環(huán)境變量PATH來找到ls的,查看環(huán)境變量PATH,可以看到下面內(nèi)容:
可以看到很多路徑,這些路徑通過冒號隔開,然后執(zhí)行l(wèi)s命令時,系統(tǒng)會從左到右開始尋找ls命令
而ls命令確實(shí)存在與這些路徑中的某個路徑下面。
那我們可不可以讓自己的可執(zhí)行程序不帶./就執(zhí)行呢
兩個方法:
- 一個就是在系統(tǒng)默認(rèn)的路徑下創(chuàng)建這個程序,然后生成可執(zhí)行程序或者將這個可執(zhí)行程序拷貝到PATH的某一個路徑下面
- 將可執(zhí)行程序所在的路徑導(dǎo)入到PATH這個路徑下面
部分環(huán)境變量說明:
set:顯示本地定義的shell變量和環(huán)境變量
unset:清楚環(huán)境變量
通過代碼來獲取環(huán)境變量:
你知道m(xù)ain函數(shù)其實(shí)是由參數(shù)的嘛?
我們平常情況不會使用他,所以基本不會寫出來。
在這里我們可以看到,調(diào)用main函數(shù)時向其傳遞了三個參數(shù)。
我們先來說說前兩個參數(shù)
我們在Linux下寫下這個程序并運(yùn)行:
#include <stdio.h>
int main(int argc,char *argv[])
{
for(int i=0;i<argc;i++)
{
printf("argv[%d]:%s\n",i,argv[i]); } return 0; }
執(zhí)行結(jié)果如下:
現(xiàn)在我們來說說main函數(shù)中的前兩個參數(shù),其中的第二個參數(shù)是一個字符指針數(shù)組,數(shù)組中第一個字符指針存儲的是可執(zhí)行程序的位置,其余字符指針存儲的是所給的若干選項(xiàng)。最后一個指針為空,而前面那個第一個參數(shù)就代表著字符指針數(shù)組中有效元素的個數(shù)
下面我們來寫一個簡單的代碼,這個代碼運(yùn)行起來之后會根據(jù)你所給的選項(xiàng)的不同給出不同的提示語句:
現(xiàn)在我們來說說main的第三個參數(shù):
main的第三個參數(shù)實(shí)際上是接受的環(huán)境變量表
可通過他獲得系統(tǒng)的環(huán)境變量:
通過系統(tǒng)調(diào)用獲取環(huán)境變量:
程序地址空間:
下面我們來驗(yàn)證一下:
最后:
十分感謝你可以耐著性子把它讀完和我可以堅(jiān)持寫到這里,送幾句話,對你,也對我:
1.一個冷知識:
屏蔽力是一個人最頂級的能力,任何消耗你的人和事,多看一眼都是你的不對。
2.你不用變得很外向,內(nèi)向挺好的,但需要你發(fā)言的時候,一定要勇敢。
正所謂:君子可內(nèi)斂不可懦弱,面不公可起而論之。
3.成年人的世界,只篩選,不教育。
4.自律不是6點(diǎn)起床,7點(diǎn)準(zhǔn)時學(xué)習(xí),而是不管別人怎么說怎么看,你也會堅(jiān)持去做,絕不打亂自己的節(jié)奏,是一種自我的恒心。
5.你開始炫耀自己,往往都是災(zāi)難的開始,就像老子在《道德經(jīng)》里寫到:光而不耀,靜水流深。
最后如果覺得我寫的還不錯,請不要忘記點(diǎn)贊?,收藏?,加關(guān)注?哦(。・ω・。)
愿我們一起加油,奔向更美好的未來,愿我們從懵懵懂懂的一枚菜鳥逐漸成為大佬。加油,為自己點(diǎn)贊!