Linux :程序及相關概念詳解(理解)
目錄
程序
基本解釋:
基本概念:程式的一個執行例項,正在執行的程式等。
核心觀點:擔當分配系統資源的實體。
描述程序PCB:
簡稱為PCB(process control block),Linux作業系統中的PCB是:task struct
task_struct:
在linux中描述程序的結構體叫做task_struct,是linux核心的一種結構,會被裝載到RAM裡並且包含程序資訊
內容分類:
- 識別符號:描述本程序的唯一識別符號,用來區別其他程序
- 狀態:任務狀態,退出程式碼,退出訊號等。
- 優先順序:相對於其他程序的優先順序
- 程式計數器:程式中即將被執行的下一條指令的地址
- 記憶體指標:包括程式程式碼和程序相關資料的指標,還有其他程序共享的記憶體塊的指標
- 上下文資料:程序執行時處理器的暫存器中的資料
- I/O狀態資訊:包括顯示的I/O請求,分配給程序的I/O裝置和被程序使用的檔案列表
- 其他資訊
檢視程序:
ps aux 是用BSD的格式來顯示 這個程序
顯示的專案有:USER , PID , %CPU , %MEM , VSZ , RSS , TTY , STAT , START , TIME , COMMAND
ps -ef 是用標準的格式顯示這個程序
顯示的專案有:UID , PID , PPID , C , STIME , TTY , TIME , CMD
“|” 管道符:是將前面命令輸出作為管道後面命令的輸入
grep:一種強大的文字搜尋工具,它能使用正則表示式搜尋文字,並把匹配的行打印出來。
通過系統呼叫獲取程序識別符號
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 int main()
5 {
6 printf("pid %d \n",getpid());
7 printf("ppid %d \n",getppid());
8 return 0;
9 }
- 子程序id——PID
- 父程序id——PPID
- 子程序與父程序程式碼段相同,但是資料各自獨有。
通過系統呼叫建立程序(fork)
fork的特點在於它被呼叫一次 ,卻返回兩次,它可能有三種不同的返回值(創建出錯返回-1,父程序返回值的子程序id,子程序返回的0)
1:父子程序程式碼共享,私有一份(採用寫時拷貝)
2:fork有兩個返回值,fork的返回值起到分流的作用。我們的使用者通過fork的返回值來判斷,到底哪個程序是父程序,哪個是子程序。
———對於父程序來說返回自是子程序的id(創建出錯失敗返回-1)
————對於子程序來說返回值是0(創建出錯失敗無子程序)
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 int main()
5 {
6 int ret = fork();
7 if(ret < 0)
8 {
9 perror("fork error\n");
10 return 1;
11 }
12 else if(ret == 0)
13 {
14 printf("this is child ,ret:%d,pid:%d\n",ret,getpid());
15 }
16 else
17 {
18 printf("this is father ,ret:%d.ppid:%d\n",ret,getppid());
19 }
20 sleep(1);
21 return 0;
22 }
程序狀態
為了知道正在執行的程序是什麼含義,我們需要知道程序是什麼狀態?
- R執行狀態:它表明程序要麼在執行中,要麼在執行佇列中。
- S睡眠狀態: 意味著程序等待事件完成(也叫做可中斷睡眠)
- D磁碟休眠狀態:這個狀態的程序通常會等待IO的結束(也叫不可中斷睡眠)
- T停止狀態:可以通過傳送SIGSTOP訊號來停止程序,這個被暫停的程序可以通過SIGCONT訊號讓程序繼續執行
- X死亡狀態:只是一個返回狀態,不會在任務列表中看見這個狀態
- (t追蹤狀態)
殭屍程序
Z(zombie)殭屍狀態是一個比較特殊的狀態。當程序退出並且父程序沒有讀取到子程序的返回碼時就會產生殭屍程序
殭屍程序會以終止狀態保持在程序表中,並且會一直等待父程序讀取退出狀態碼
只要子程序退出,父程序還在執行,但父程序沒有讀取子程序狀態,子程序進入Z狀態
殭屍程序產生原因:
0:子程序先於父程序退出
1:程序退出為了儲存自己退出的原因,因此這個退出後資源不會
被完全釋放。等待父程序來獲取退出狀態,然後釋放子程序所有資源
2:假如父程序沒有關心子程序的退出,那麼這時候退出子程序將成為一個殭屍程序
殭屍程序危害
資源洩漏,正常程序可能無法建立
- 程序的退出狀態必須被維持下去,因為它要傳遞給父程序資訊,可父程序一直不讀取,那子程序就一直處於Z狀態
- 維護退出狀態本身就是要用資料維護,也屬於程序基本資訊,所以儲存在task_struct(PCB)中,換句話說,Z狀態一直不退出,PCB就一直維護
- 父程序建立很多子程序不回收,會造成記憶體資源浪費,因為資料結構物件本身就要佔用記憶體
建立一個維持30秒殭屍程序的例子
1 #include<stdio.h>
2 #include<stdlib.h>
3
4 int main()
5 {
6 pid_t pid = fork();
7 if(pid < 0)
8 {
9 printf("error\n");
10 return -1;
11 }
12 else if(pid == 0)
13 {
14 printf("child %d is z...\n",getpid());
15 }
16 else
17
18 { printf("parent %d\n",getppid());
19 sleep(30);
20 }
21 return 0;
22 }
維持殭屍的例子:
1 #include<stdio.h>
2 #include<errno.h>
3 #include<unistd.h>
4 #include<stdlib.h>
5 int main()
6 {
7 pid_t pid;
8 pid = fork();
9 if(pid < 0)
10 {
11 perror("fork");
12 exit(0);
13 }
14 else if(pid == 0)
15 {
16 exit(0);
17 }
18 else
19 {
20 while(1)
21 {
22 printf("I am zombie\n");
23 sleep(1);
24 }
25 }
26 return 0;
27 }
kill -9 也不能殺死殭屍
孤兒程序
父程序先退出,子程序就稱為“孤兒程序”
這個孤兒程序將被孤兒院 (1號程序)init程序所領養,子程序退出後將由init程序來收回資源,因此孤兒程序並不會有什麼危害。
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<errno.h>
4 #include<unistd.h>
5
6 int main()
7 {
8 pid_t pid = fork();
9 if(pid < 0)
10 {
11 perror("fork error");
12 }
13 if(pid == 0)
14 {
15 printf("this is child,pid:%d\n",getpid());
16 //輸出程序id
17 //睡眠五秒,保證父程序先退出
18 sleep(5);
19 printf("child:pid:%d,ppid:%d\n",getpid(),getppid());
20 printf("child process is exited\n");
21 }
22 else
23 {
24 printf("this is father,pid:%d\n",getpid());
25 //父程序睡眠一秒,保證子程序輸出程序id
26 sleep(1);
27 printf("father process is exited\n");
28 }
29 return 0;
30 }
程序優先順序
- cpu資源分配的先後順序,就是指程序的優先權
- 優先權高的程序有優先執行權利,配置程序優先權對多工環境的linux很有用,可以改善系統性能
- 還可以吧程序執行在指定的CPU上,大大改善整體效能
互動式程序:優先順序高
批處理程序:優先順序低
PRI:程序的優先順序,越小越早執行
NI:nice值,表示程序可被執行的優先順序修正值 -20~19 一共四十個級別(renice)
(nice為負值時,優先順序值會變低,即優先順序會變高)
PRI(new) = PRI(old)+ nice
(nice值不是程序的優先順序,但是程序nice值會影響到程序優先順序變化,可以理解為nice是優先順序修正資料)
修改程序優先順序的命令
- nice
- renice
啟動前調整:nice
開始執行程式就指定nice值:nice -n -5 ./test
調整已經存在的程序的nice:renice
renice -5 -p PID
- 競爭性:系統程序數目眾多,而CPU資源只有少量,所以程序之間是有競爭屬性的,為了高效完成任務,跟合理競爭相關資源,便有了優先順序
- 獨立性:多程序執行,需要獨享各自資源,多程序執行期間互不干擾
- 並行:多個程序在多個CPU下分別,同時進行執行,這稱之位並行
- 併發:多個程序在一個CPU下采用程序切換的方式,在一段時間之內,讓多個程序都能夠推進,稱之為併發
環境變數
基本概念::一般是指在作業系統中用來指定作業系統執行環境的一些引數。如:我們在編寫c++程式碼時,在連結的時候,從來不知道我們所連結的動態靜態庫在哪裡,但是照樣可以連結成功,生成可執行程式,原因就是有相關環境變數幫助編譯器進行查詢。(環境變數通常具有某些特殊功能,還有在系統中通常具有全域性特性)
———— 環境變數相當於給系統或使用者設定一些引數,不同的環境變數其不同的作用,“Path”就是一個變數裡面儲存了一些常用命令所存放的目錄路徑。用來告訴系統, 當要求系統執行一個程式而沒有告訴它程式所在的完整路徑時, 系統除了在當前目錄下面尋找此程式外, 還應到哪些目錄下去尋找
常見環境變數
- PATH*:指定命令的搜尋路徑
- HOME*: 指定使用者的主工作目錄(及使用者登入Linux系統時,預設的目錄)
- HISTSIZE*: 指儲存歷史命令記錄的條數
- SHELL*: 當前shell,它的值通常是/bin/bash
檢視環境變數的方法:echo $環境變數名稱
相關的命令
- echo:顯示某個環境變數值
- export:設定一個新的環境變數
- env:顯示所有環境變數
- unset:清除環境變數
- set:顯示本地定義的shell變數和環境變數
- 每個程式都會收到一張環境表,環境表是一個字元指標陣列,每個指標指向一個以‘\0’結尾的環境字串。
- 常用getenv和putenv函式來訪問特定的環境變數
- 環境變數通常是全域性變數,可以被子程序繼承下去