今天是中秋節,祝各位小伙伴中秋節快樂,記得吃月餅吖

圖片來自網路,侵聯刪
Linux行程基本操作
1.行程基本概念
在Linux中行程資訊被保存在task_struct(PCB)
2.查看行程的方法
ps
ps aux | greap myproc
ls /proc
3.創建行程
fork():創建一個子行程
#include<iostream>
#include<unistd.h>
using namespace std;
int main()
{
fork();
while(1)
{
printf("hehe\n");
}
return 0;
}
fork之前的代碼,被父行程執行,fork之后的代碼,父子都可以執行
fork之后,父子行程代碼共享
fork之后,父子進行那個先運行不確定,取決于作業系統調度演算法
fork函式會有兩次回傳值,給父行程回傳子行程pid,給子行程回傳0

6 pid_t id=fork();
7 if(id==0)
8 {
9 while(1)
10 {
11 printf("我是子行程\n");
12 sleep(1);
13 }
14 }
15 else if(id>0)
16 {
17 while(1)
18 {
19 printf("父行程\n");
20 sleep(2);
21 }
22 }
23 else
24 {
25 printf("行程創建失敗\n");
26 }
27 return 0

結果:父子行程同時執行
4.行程的狀態
行程狀態------>資料化-------->行程資料被保存到tack_struct中
行程主要有以下幾種狀態:
R狀態:
可以同時存在多個R狀態的行程
R狀態的行程不一定是正在運行的,表示隨時可以呼叫該行程
系統中所有處于R狀態的行程都會被連接起來形成調度佇列(run_queue)
S狀態:休眠狀態(淺度睡眠)通常用來等待某種事件發生,隨時可以被喚醒,也可以被殺掉
//休眠狀態
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("I am Running\n");
sleep(10000000);
printf("Ending\n");
return 0;
}
D狀態:深度睡眠狀態,D狀態沒有辦法模擬,表示該行程不會被殺掉,即便是作業系統,除非重啟殺掉,或者主動醒來

T狀態:將行程進行暫停
如上圖所示,摁下19sigstop即可暫停行程
X狀態:死亡行程
Z狀態:僵尸狀態
行程退出,在作業系統層面,曾經申請的資源,并不是被立即釋放,而是要暫存一段事件,供OS(父行程)進行讀取,,而父行程沒有讀取,叫做僵尸狀態
為什么要有僵尸行程:
行程創建的目的:完成某種作業
當任務完成的時候,呼叫方應該指導任務完成得怎么樣
(除非不關心)
sjw@iZ2zedu4njy79sqivntvprZ test_9_11]$ cat test.c
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("I am Running\n");
sleep(10000000);
printf("Ending\n");
return 20;
}
[sjw@iZ2zedu4njy79sqivntvprZ test_9_11]$ echo $?//查看行程碼,查看最近一次行程退出時得行程碼(如回傳值return 0等)
//行程退出時,行程資訊(退出碼)是會被暫時保存起來的,相關資訊被保存到task_struct,此時,該task_struct相關資料不應該被釋放掉 Z
當有行程來讀取資訊時,task_struct被釋放
如何讀取資訊:行程wait
行程退出的資訊(退出碼)會被暫時保存起來
保存在task_struct中,如果沒有人讀取,此時,task_struct相關資料不應該被釋放
模擬僵尸狀態:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t id=fork();
if(id==0)
{
int count=5;
while(count)
{
printf("I am child, pid:%d, ppid:%d, count:%d\n",getpid(),getppid(),count--);
sleep(1);
}
printf("child exit......\n");
exit(-1);
}
else if(id>0)
{
while(1)
{
printf("I am father,pid:%d, ppid:%d\n",getpid(),getppid());
sleep(1);
}
}
else
{
printf("fork fail\n");
}
return 0;
}
[sjw@iZ2zedu4njy79sqivntvprZ test_9_11]$ make clean
rm -rf *.o myproc
[sjw@iZ2zedu4njy79sqivntvprZ test_9_11]$ make
gcc -c test.c -o test.o
gcc -o myproc test.o
[sjw@iZ2zedu4njy79sqivntvprZ test_9_11]$ ./myproc
I am father,pid:9259, ppid:308
I am child, pid:9260, ppid:9259, count:5
I am father,pid:9259, ppid:308
I am child, pid:9260, ppid:9259, count:4
I am father,pid:9259, ppid:308
I am child, pid:9260, ppid:9259, count:3
I am father,pid:9259, ppid:308
I am child, pid:9260, ppid:9259, count:2
I am father,pid:9259, ppid:308
I am child, pid:9260, ppid:9259, count:1
I am father,pid:9259, ppid:308
child exit......
I am father,pid:9259, ppid:308
I am father,pid:9259, ppid:308
I am father,pid:9259, ppid:308
I am father,pid:9259, ppid:308
I am father,pid:9259, ppid:308
^Z
然后在另外一個視窗輸入以下監控腳本:

while :; do ps aux | head -1 && ps aux | grep myproc|grep -v grep;echo "#############################"; sleep 1; done
剛開始運行時,二者都是S狀態,到子行程運行完畢退出進行時,子行程編程僵尸行程

僵尸行程的危害:
造成記憶體浪費
造成記憶體泄漏
孤兒行程
Linux中,行程關系,主要是父子關系,
孤兒行程:父行程退出,子行程還在運行
孤兒進行會立即被系統領養(作業系統:1號行程Init)
? 監控腳本:
while :; do ps axj | head -1 && ps axj | grep myproc|grep -v grep;echo "#############################"; sleep 1; done
#include<stdio.h>
#include<stdlib.h>
#include<unstd.h>
int main()
{
pid_t id=fork();
if(id==0)
{
while(1)
{
printf("I am child,pid:%d,ppid:%d\n",getpid(),getppid());
sleep(1);
}
}
else if(id>0)
{
int count=5;
while(count)
{
printf("I am father,pid:%d,ppid:%d,count:%d\n",getpid(),getppid(),count--);
}
exit(-1);
}
return 0;
}
查看行程:
ps aux |grep Mytest
行程是能夠知道自己當前所處的作業目錄的
5.行程的優先級
cpu分配資源的先后順序,即是行程的優先級,優先級越高說明行程被執行的越早
在Linux下輸入:
ps -l

出現如上圖所示的結果,有幾個內容需要我們關注一下:
UID:代表執行者的身份
PID:當前行程的代號
PPID:父行程的代號
PRI:代表行程的優先級,該數字越低,代表行程的優先級越高,被執行的越早(默認該值為80)
NI:代表行程的nice值
NI與PRI的關系:
PRI用來表示行程的優先級,該值越低,代表行程的優先級越高,被執行的越早,該值默認為80,而NI用來改變行程的優先級,NI的取值范圍為-2019,PRI(new)=PRI(old)+NI,也就是說我們通過修改NI的值來改變行程的優先級,那么被修改后的行程優先級PRI的取值范圍為6099,60代表優先級最高的,99代表優先級最低(ps:每次修改PRI后再次修改時PRI默認為80,在80的基礎上進行修改)
修改行程優先級的方法:
top命令--->輸入r--->輸入想要修改的行程的pid--->輸入NI值--->q保存退出

如上圖所示我們將上面的行程NI修改為20,而PRI變為60,即最高優先級
其它概念:
獨立性:多個行程獨自運行,獨自享受各種資源,多個行程之間互不干擾
并行:多個行程在多個CPU上分別,同時進行運行
并發:多個行程在一個CPU下采用行程切換的方式,在一段時間內,讓多個行程得以推進,稱為并發
6.環境變數
環境變數一般使之作業系統中用來指定運行環境得一些引數
如:撰寫c/c++代碼時,在連接的時候,從來不知道我們所連接的動態靜態庫在哪里,但是照樣鏈接得動,就是相關環境變數幫助環境變數進行查找
常見環境變數
PATH:指定當前搜索路徑
HOME:指定用戶的主作業目錄(即用戶登錄到LInux系統中時,默認的目錄)
SHELL:當前Shell,它的值通常為/bin/bash
查看環境變數的方法
echo $NAME//NAME:你的環境變數名稱

我們執行mpproc時前面必須加./,而執行ls之類的卻不用加,就是因為ls進行了環境變數的配置,我們也可以進行配置,使之向ls一樣直接執行
sudo cp -f mpproc /usr/bin
//直接將我們的可執行程式mpproc復制到/usr/bin目錄下,但是這種方法不推薦,因為以后如果同名的或者該目錄下檔案過多可能會造成誤刪


這樣我們就可以像ls一樣執行mpproc,除了這種方式之外還有一個更推薦的方法
首先我們先洗掉這次的配置:
sudo rm -rf /usr/bin/mpproc
//注意不要直接把bin目錄直接給刪了

這樣我們就洗掉了上次的配置,比較推薦的是下面這種方法,這種寫法當我們退出服務器后路勁會自動消除:
export PATH=$PATH:/home/sjw/Linux_Learning/test_9_17
//直接將路徑設定到環境變數當中去
//export:設定環境變數

查看PATH:
echo $PATH

查看HOME:

echo $HOME
查看SHELL:
echo $SHELL

顯示當前所有的環境變數
env

set:顯示本地系統中所有的環境變數
export :設定自己的環境變數
export myvalue=100

如上所示我們設定自己的環境變數并進行查看
unset:取消自己設定的環境變數
unset myvalue
//取消剛剛設定的環境變數

6.環境變數的組織方式
每個程式都會收到一張環境表,環境表是一個字符指標陣列,每個指標指向一個以‘\0’結尾的環境變數字符
這里我們以c/c++程式為例:
所有的c/c++程式從main函式開始執行,main函式是沒有引數的,但實際上main是有引數的
int main(int argc,char*argc[],char*envp[])
{
}
//argc用來統計命令列引數
//argc是一個字符指標陣列,里面每一個都是字符型的指標,指向命令列引數
//envp也是一個字符指標陣列,里面每一個都是字符型的指標,指向環境變數信
//息,而環境變數資訊也是通過這樣傳遞給一個函式的
什么是命令列引數,下面我來帶大家演示一下
#include<stdio.h>
#include<string.h>
int main(int argc, char*argv[],char*envp[])
{
for(int i=0;i<argc;i++)
{
printf("%s\n",argv[i]);
}
if(strcmp(argv[1],"-a")==0)
{
printf("hello world\n");
}
else
{
printf("哈哈\n");
}
}
gcc test.c -o Mytest -std=c99
./myproc -a

這次我們使main帶引數,然后進行編譯,鏈接,在最后執行時我們在命令列上輸入 ./Mytest -a
這時argv就發揮了作用,用來存盤這兩個命令列引數,argc用來記錄命令列引數的個數,利用這個命令列引數我們可以在剛開始時就進行判斷進行分支陳述句的執行
再比如:
#include<stdio.h>
#include<string.h>
int main(int argc, char*argv[],char*envp[])
{
printf("argc:%d\n",argc);
for(int i=0;i<argc;i++)
{
printf("argv[%d]:%s\n",i,argv[i]);
}
return 0;
}

如上圖所示,即將命令列引數進行了保存
接下來我們來聊一聊envp,它也是一個字符指標陣列,指向系統的環境變數資訊,通過envp我們可以呼叫到系統的環境變數資訊
#include<stdio.h>
#include<string.h>
int main(int argc, char*argv[],char*envp[])
{
int i=0;
while(envp[i])
{
printf("envp[%d]:%s\n",i,envp[i]);
i++;
}
return 0;
}
//envp以'\0'進行結尾

仔細觀察就會發現這和上面利用env命令查看到的系統環境變數資訊是一致的
在Windows中也存在命令列引數,也可以通過envp來獲取系統環境變數資訊
//列印Windows下環境變數的資訊
#include<stdio.h>
//命令列引數的寫法
int main(int argc,char*argv[],char*envp[])
{
int i = 0;
while (envp[i])
{
printf("Windows:envp[%d]:%s\n", i, envp[i]);
i++;
}
return 0;
}

如上所示即為Windows下的環境變數資訊,同學名還可以在自己的電腦上試一試
環境變數是一個系統級別的全域變數,更本原因是bash之下所有的行程都可以獲取
通過系統函式呼叫查看環境變數
getenv(“NAME”)//NAME為環境變數名稱
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(int argc, char*argv[],char*envp[])
{
//顯示PATH環境變數名稱
printf("%s",getenv("PATH"));
return 0;
}

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/type>
//定義全域變數
int g_val=100;
int main(int argc,char*argv[],char*envp[])
{
pid_t id=fork();
if(id==0)
{
//在子行程中改變這個全域變數,觀察父行程中的全域變數是否發生變化
g_val=200;
printf("child:pid:%d,ppid:%d,g_val:%d,&g_val:%p\n",getpid(),getppid(),g_val,&g_val);
}
else
{
sleep(2);
printf("father:pid:%d,ppid:%d,g_val:%d,&g_val:%p\n",getpid(),getppid(),g_val,&g_val);
}
sleep(1);
}

如上所示,我們在上面代碼中定義了一個全域變數g_val,而我們在子行程中改變了g_val的值為200,但是在父行程中g_val的值仍然為100,而且兩個行程中g_val的地址是一樣的
證明:該地址不是物理地址,而是虛擬地址
是語言層面上見到的地址而不是物理地址
我們在c/c++語言所見到的地址都是虛擬地址,物理地址一般看不到,由作業系統統一進行管理
OS(作業系統)負責將虛擬地址轉化為物理地址
每一個行程都有一個行程地址空間,都有一個映射串列
子行程的寫入,不會影響父行程,行程之間具有獨立性,不會互相影響
寫的時候單獨拷貝一塊空間,資料層面上發生了分離
今天是中秋節,祝各位小伙伴節日快樂,也感謝大家的收藏,評論,轉發,期待下次再見
To Be Continued…
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/302003.html
標籤:其他
上一篇:你想知道的字串函式全家桶都在這
