文章目錄
- 行程狀態
- 行程狀態的查看
- 行程狀態的種類
- 并發與并行
- R運行狀態
- S休眠狀態:(可中斷的睡眠狀態/淺度睡眠)
- D磁盤休眠狀態(不可中斷的睡眠狀態/深度睡眠)
- T停止狀態
- X死亡狀態
- Z僵尸狀態 (產生原因 / 為什么會有 / 避免方法)
- 環境變數
- 常見的環境變數的介紹
- PATH
- PWD
- HOME
- SHELL
- 環境變數的相關命令 (如何匯入環境變數)
- 本地變數與環境變數
- 環境變數的組織方式
- 環境變數的獲取 (程式中)
- 命令列引數
行程狀態
行程狀態的查看
ps aux
ps axj
#一般配合grep進行使用
ps axj | grep test #test就是行程名(舉的例子)
?
行程狀態的種類
- R (running):運行狀態
- S (sleeping):睡眠狀態
- D (disk sleep):磁盤休眠狀態
- T (stopped):停止狀態
- X (dead):死亡狀態
- Z (zombie):僵尸狀態
下面我會詳細解釋每一種狀態對應的含義:
?
并發與并行
??并發:在單處理器(一個CPU)中,在同一時刻只能有一個執行緒正在運行,只不過OS可以通過不斷的切換執行緒,讓宏觀上看起來就像是同時運行多個執行緒一樣,
??并行:在多處理器(多個CPU)中,在同一時刻可以執行與CPU數量相同個數的執行緒,
?
R運行狀態
1個CPU可以同時存在多個R狀態的行程,但是1個CPU在同一時刻只能運行一個行程!
因此,即使行程是R狀態,不一定代表它正在運行,而是代表可以被調度!
(存在調度佇列,R狀態的行程要么正在運行要么在調度佇列當中等待被調度)
?
S休眠狀態:(可中斷的睡眠狀態/淺度睡眠)
S狀態代表該行程正在等待某種事件的發生,
(S狀態下的行程可以被 Kill 掉,淺度睡眠狀態可以被喚醒,也可以被殺掉)
ps:【在程式中使用sleep(),可以讓該行程變為S狀態】

?
D磁盤休眠狀態(不可中斷的睡眠狀態/深度睡眠)
??D狀態是不可中斷的睡眠狀態,這就一位置它無法被殺死,比較常見的例子就是等待硬體設備的I/O回應,在等待期間它就是處于D狀態,我們只能等待它的I/O結束,
?
T停止狀態
?我們可以通過發送 SIGSTOP(停止) 信號去將行程設定為停止狀態,
這個暫停的行程可以通過發送 SIGCONT(繼續) 信號讓行程繼續運行,
小細節:
當一個行程被暫停后,處于T狀態,此時向它發送退出信號(比如: SIGINT信號),它并不會立刻就處理退出信號,而是會等到下一次繼續運行時(收到SIGCONT后),再進行退出操作,
//但是kill -9還是可以立刻殺死它的
?
X死亡狀態
??這只是一個回傳狀態,在任務表中看不到該狀態,
?
Z僵尸狀態 (產生原因 / 為什么會有 / 避免方法)
問題1:什么是僵尸狀態?
??子行程先退出,父行程運行,且父行程沒有讀取子行程退出的退出碼時,那么子行程就會處于僵尸狀態(Z), //一般通過讓父行程呼叫 wait 或 waitpid函式 對子行程的資源進行回收從而阻止僵尸行程的產生,
問題2:僵尸狀態產生的原因?
??當子行程退出時,在系統層面上,曾經申請的資源并不會被立即釋放,因為要供父行程進行讀取,只要父行程沒有進行讀取,那么子行程就處于僵尸狀態,
<---------------換個角度理解-------------->
??行程退出的資訊(退出碼)是會被暫時保存起來的(保存在task_struct),
??如果沒有人讀取,那么該task_struct相關資料不應該被釋放掉,此時的狀態為Z狀態,
問題3:為什么要有僵尸狀態?
??為了給子行程的呼叫方回傳當前行程完成任務時的樣子,
(因為行程被創建的目的就是為了完成某種作業,所以任務完成時,為了讓呼叫方知道任務完成的怎么樣)
問題4:我們該怎么避免僵尸行程? (3種方法)
??①wait方法:讓父行程呼叫wait或waitpid函式去回收子行程的資源
??②信號法:借助信號的機制,子行程退出的時候會給父行程發送SIGCHLD信號,我們可以捕捉SIGCHLD信號 [signal(SIGCHLD, handler);],然后再自定義SIGCHLD信號的處理方式,在SIGCHLD的信號處理函式中呼叫wait/waitpid函式進行子行程資源的回收,這樣的好處就在于,父行程可以專心做自己的事情,不用關心子行程了,子行程終止的時候會通知父行程,父行程在信號處理方法中去呼叫wait函式清理子行程,
??③孤兒行程法:在子行程終止前,讓父行程先終止,這樣就不會生成僵尸行程了,子行程會變為孤兒行程,此時由1號行程對該子行程的資源進行回收,(具體的操作可以是: 連續fork兩次,子行程在創建完孫子行程后,kill掉子行程,這樣孫子行程就會由init行程領養了)
//每個方法名字是我自己取的
?
孤兒行程
??當父行程先退出時,子行程就被稱之為“孤兒行程”,
??孤兒行程會被 1號行程 (init行程) 領養,最后也會被init行程回收,
//就相當于將父行程換成了 init行程,然后繼續正常運行,
//孤兒行程沒有危害,
?
演示僵尸狀態
#include <iostream>
#include <cstdlib>
#include <unistd.h>
using namespace std;
int main()
{
if(!fork())
{
cout << "Child Exit!" << endl;
exit(1);
}
//父行程沒有回收子行程的資源,子行程此時就是僵尸狀態了
sleep(1000);
return 0;
}
??下圖就是代碼運行的結果,子行程的行程狀態就是僵尸狀態,

?
?
環境變數
問題:環境變數是什么?
??環境變數一般指在作業系統中用來指定作業系統運行環境的一些引數,并且它們一般是具有一些功能的變數,
問題:為什么要有環境變數?環境變數有什么用?
??環境變數的使用可以讓我們的操作更加方便,如果沒有環境變數的話,很難查看一些狀態,比如當前目錄,當前用戶…
?
常見的環境變數的介紹
PATH
??PATH: 指定命令的搜索路徑
問題:為什么我們在Linux執行命令的時候不需要帶 路徑 ?比如: ls命令
??因為我們有個環境變數PATH,
??我們執行命令的時候,會先在特定的目錄(PATH變數中的所有路徑)里面去找命令,默認配置的PATH中的路徑↓↓↓

問題:怎么樣把我們自己寫的程式也變得像系統命令一樣?
只需要把我們的程式所在的路徑匯入到PATH環境變數中即可!
方法1(不推薦:將自己寫的程式 cp 到PATH的路徑當中,這樣就可以直接輸入命令去運行了,
舉個🌰:
??自己寫了一個myproc的程式,需要./myproc才能運行,但是將它cp到PATH路徑后,直接輸入myproc就可以運行了
方法2:export PATH=$PATH:[路徑]
??自己所添加的路徑,會在下次重新啟動shell時消失
?
PWD
??查看當前的作業目錄
HOME
??查看當前用戶的主作業目錄
SHELL
??查看當前的bash,它的值通常是/bin/bash
環境變數的相關命令 (如何匯入環境變數)
echo + $環境變數:顯示某個環境變數的值
echo $SHELL
輸出: /bin/bash
名稱=值(值包括字串/數字) :設定一個本地變數 //前面加上export后,即可變成環境變數
MYVAL=2333
export MYVAL2=666 #可以結合下面的圖一起食用
export:設定一個新的環境變數
#對于前面的本地變數MYVAL,我們可以通過export的方法把它變為環境變數
export MYVAL
env:查看所有環境變數
set:顯示本地定義的shell變數和環境變數
unset:清除某個環境變數
一、直接使用export 變數=值 設定我們自己的環境變數

二、如果在使用 變數=值 的時候沒有加export,設定的就是本地變數

三、將我們設定的本地變數轉為環境變數的話,還可以在其前面加一個export

本地變數與環境變數
??本地變數:(不加export)
??只在本行程(bash)內有效
??環境變數:(全域的)
??所有行程都能看到這個變數(指環境變數),且都可以使用,
?
環境變數的組織方式
??環境變數表也是一個字符指標陣列,每個指標都指向一個以 '/0’結尾的環境變數字串,
字符指標陣列的最后一個元素存放的是NULL,

?
環境變數的獲取 (程式中)
①:通過main函式的引數中的char* envp[]陣列
int main(int argc, char* argv[], char* envp[])
{
int i = 0;
for(; envp[i]; ++i)
printf("%s\n", envp[i]);
return 0;
}
②:通過第三方變數environ獲取
int main()
{
extern char** environ;
int i = 0;
for(; environ[i]; ++i)
printf("%s\n", environ[i]);
}
③:通過系統呼叫getenv與putenv
char* getenv(const char* name); //均包含于<stdlib.h>
int putenv(char* string);
getenv:從已有的環境變數中獲取指定環境變數名稱的環境變數,
putenv:string字串的格式為name=value,當環境變數中不存在該名稱,則添加字串到環境變數中;如果已經存在了name環境變數,則將name環境變數的值改為value,
int main()
{
printf("%s\n", getenv("PATH")); //getenv中指定獲取環境變數的名稱
return 0;
}
?
?
命令列引數
問題:為什么在linux中輸入命令時可以帶選項? 如:ls -l
??命令的本質是可執行程式,而你在輸入命令后,就相當于把該程式運行起來了,而輸入命令時所攜帶的引數會被收入到命令列引數中,main函式可以接收到這些引數,從而會去執行相應的操作,
argc:代表 在命令列中輸入的引數的個數
argv:存盤所有命令列引數(argv[0] 存盤的是程式名) //argv是個字符指標陣列
懂了以上的概念后,你也可以寫一個能夠接收命令列引數的小程式了!這里給出命令列引數的獲取方法,

將程式編譯鏈接后,./程式 [引數選項] [引數選項]??#像這樣在后面攜帶的引數就會被收入到命令列引數argv陣列中了
問題:為什么我們可以向main函式中傳遞命令列引數?
??main函式是由mainCRTstartup函式呼叫的,CRT是由作業系統內核呼叫的,所以可以向main函式傳遞環境變數,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/404139.html
標籤:其他
上一篇:資料結構(C語言版)—P1<初識資料結構+演算法實作>
下一篇:資料結構——線性表之順序表篇
