linux 程序重要知識(詳講)
阿新 • • 發佈:2018-12-12
一、程序基礎
1.程序相關概念
程序是一個獨立可排程的任務,程序是一個抽象的實體,當系統在執行某個程式時,分 配和釋放的各種的資源
程序是一個程式的一次執行的過程
程序是程式執行資源管理的最小單位
程序和程式的區別:程序是動態的概念,它是程式執行的過程,包括建立、排程和消亡
程式是靜態的,它是一些儲存在磁碟上的指令的有序集合,沒有 任何執行的概念
正文段(.test)
程序 使用者資料段(.data) 程式
系統資料段
程序不僅包括程式的指令和資料,而且包括程式計數器值,CPU的所有暫存器以及儲存臨時資料的程序堆疊
2.linux下的程序結構
主要的程序標識:程序號(Process Identity Number , PID)
父程序號(Parent Process ID, PPID)
Linux中的程序包含三個段:
資料段:存放的是全域性變數、常量以及動態資料分配的資料空間(如malloc函式取得的空間)等
正文段:存放的是程式中的程式碼
堆疊段:存放的是函式的返回地址、函式的引數以及程式中的區域性變數
3.linux系統的程序型別
互動程序:該程序是由shell控制和執行的。互動程序既可以在前臺使用,也可以在後臺使用
批處理程序:該程序不屬於某個終端,它被提交到一個佇列中以便順序執行
守護程序:該類程序實在後臺執行,它一般在linux啟動時開始執行,系統關閉時才結束。
4.程序執行狀態
執行態(R):此時程序正在執行或者正在準備執行
等待態:此時程序正在等待某一個事件的發生或者某種系統資源(阻塞,休眠,等待,睡眠態)
可中斷態(S):接收到訊號有反應
不可中斷態(D):接收到訊號沒有反應
停止態(T):此時程序已經被終止(SIGSTOP訊號,SIGCONT訊號)
死亡態(Z):這是一個已終止的程序,但還在程序向量陣列彙總佔有一個task_struct結構體(殭屍態)
程序狀態圖:
5.程序的模式
程序的執行模式分為使用者模式和核心模式
在系統中,有一個定時器,每隔1ms產生一次中斷,中斷會進入核心(成為心跳,滴答tick);在使用者模式與核心模式切換時,只有發生中斷,才能從使用者模式進入到核心模式,系統呼叫都是靠軟體進行中斷,
6.排程程序
ps 檢視系統中的程序
top 動態顯示系統中的程序
nice 按使用者指定的優先順序執行程序
renice 改變正在執行的程序的優先順序
kill 結束程序(包括後臺程序)
bg 將掛起的程序在後臺執行
fg 把後臺的執行的程序放到前臺執行
二、程序系統呼叫
1.程序建立:fock() 產生一個程序
標頭檔案:#include<sys/types.h>//提供型別pid_t的定義 #include <unistd.h>
函式原型:pid_t fock(void);
函式返回值:0:子程序,從fock返回的地方開始執行
子程序PID(大於零的證書):父程序
-1:出錯
getpid():獲取pid號,子程序號
getppid():獲取ppid號,父程序號
孤兒程序:一個父程序退出,而它的一個或多個子程序還在執行,那麼那些子程序將成為孤兒程序。孤兒程序將被init程序(程序號為1)所收養,並由init程序對它們完成狀態收集工作。(系統啟動首先要執行init程序,init掃描孤兒程序,收養孤兒程序)
殭屍程序:一個程序使用fork建立子程序,如果子程序退出,而父程序並沒有呼叫wait或waitpid獲取子程序的狀態資訊,那麼子程序的程序描述符仍然儲存在系統中。這種程序稱之為僵死程序,通常是由父程序清理子程序的殭屍。
2.exec函式族(替換一個程序的映像)
fock函式用於建立一個程序,該子程序幾乎拷貝了父程序的全部內容
exec函式族提供了一種在程序中啟動另一個程式執行的方法。它可以根據指定的檔名或者目錄名找到可執行檔案,並用它來取代原呼叫程序的資料段、程式碼段、堆疊段。在執行完之後,原呼叫程序的內容除了程序號外,其他全部都被替換了。
什麼時候使用?
當程序認為自己不能在為系統和使用者做出任何貢獻了時就可以呼叫exec函式,讓自己執行新的程式。如果某個程序想同時執行另一個程式,它就可以呼叫fock函式建立子程序,然後在子程序中呼叫任何一個exec函式。這樣看起來就好像通過執行應用程式而產生了一個新程序一樣。
#include <unistd.h>
int execl(const char *path, const char *arg, ... /* (char *) NULL */);
int execlp(const char *file, const char *arg, .../* (char *) NULL */);
int execle(const char *path, const char *arg, ... /*, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
exec函式組使用區別:
可執行檔案查詢方式
引數表傳遞方式
環境變數的使用
exec函式族使用注意點:
在使用exec函式族時,一定要加上錯誤判斷語句
常見的錯誤原因有:
找不到檔案或者路徑,此時errno被設定為ENOENT
陣列argy和envp忘記用NULL結束,此時errno被設定為EFAULT
沒有對應可執行的的執行許可權,此時ERRNO被設定EACESS
3.exit 和 _exit
標頭檔案: exit:#include<stdlib.h>
_exit: #include <unistd.h>
函式原型: exit: void exit(int status);
_exit:void _exit(int status);
函式傳入值:status是一個證書的引數,可以利用這個引數傳遞程序結束時的狀態,通常()表示正常結束;其他的數值表示出現了錯誤,程序非正常結束。在實際程式設計時,可以用wait系統呼叫接受子程序的返回值,程序相應的處理
_exit和exit的區別:
_exit()函式的作用最為簡單:直接使程序終止結執行,清除其使用的記憶體空間,並銷燬其在核心中的資料結構
exit()函式則在這些基礎上做了一些包裝,在執行退出之前加了若干道工序
exit()函式在呼叫exit系統呼叫之前要檔案的開啟情況,把檔案緩衝區中的內容寫入檔案,就是圖中的“清理I/O緩衝”一項
4.wait 和 waitpid
wait函式:呼叫該函式使程序阻塞,直到任一個子程序結束或者是該程序接受到了一個訊號為止(等待態,可中斷)。如果該程序沒有子程序或者其子程序已經結束,wait函式會立即返回。
waitpid函式:功能和wait函式類似。不同的是可以指定某一子程序結束以及等待的方式(阻塞或非阻塞)
wait()函式
標頭檔案 #include<sys/types.h>
#include <sys/wait.h>
函式原型: pid_t wait(int *status);
函式引數: status是一個整形指標,指向的物件用來儲存子程序退出時的狀態。
status若為空,表示忽略子程序退出時的狀態
status若不為空,表示儲存子程序退出時的狀態
另外,子程序的結束狀態可由linux中一些特定的巨集來測定
waitpid()函式
標頭檔案 #include<sys/types.h>
#include <sys/wait.h>
函式原型:pid_t waitpid(pid_t pid, int *status, int options)
函式引數:pid
pid>0:只等待程序ID等於pid的子程序,不管已經有其他子程序執行結 束退出了,只要指定的子程序還沒 有結束,waitpid就會一直等下去
pid =1: 等待任何一個子程序退出,此時和wait作用一樣
pid =0:等待其他組ID等於呼叫程序的組ID的任一子程序
pid<0: 等待七組ID等於pid的絕對值的任一程序
status:獲取子程序結束資訊
options:
WNOHOG,若由pid指定的子程序並不立即只使用,到waitpid不阻塞,此時返回值為0
WUNTRACED:若某實現支援作業控制,則由pid指定的任一子程序已暫停,且其狀態自暫停以來還來報告過,則返回其狀態
0:同wait,蘇澤父程序,等待子程序退出
函式返回值:正常:結束的子程序的程序號
使用選項WNOHANG且沒有子程序結束時:0
調用出錯:-1
5.linux守護程序
守護程序,也就是通常所說的Daemon程序,是linux中的後臺服務程序,它是一個生存期較長的程序,通常獨立於控制終端並且週期性的執行某種任務或等待處理某些發生的事件
守護程序常常在系統啟動時開始執行,在系統關閉時終止
linux系統有很多守護程序,大多數服務都是用守護程序實現的
在linux中,每一個系統與使用者進行交流的介面稱為終端。從該終端開始執行的程序都會依附於這個終端,這個終端稱為這些程序的控制終端,當控制終端被關閉時,相應的程序都會被自動關閉。
守護程序能夠突破這種限制,他從開始執行,直到整個系統關閉才會退出。如果讓某個程序不會因為使用者或者終端的變化而受到影響,就必須把這個程序變成一個守護程序。
6.linux守護程序編寫步驟
(1) 建立子程序,父程序退出
第一步需要子程序在形式上做到與控制終端的脫離,由於父程序已經先於子程序退出, 子程序程式設計孤兒程序
pid=fork(); if(pid>0) { exit(0); }
(2)在子程序中建立新會話
程序組是一個或多個程序的集合。程序組由程序組ID來唯一表示,每個程序組都有一個組長程序,程序組ID就是組長程序的程序號;
會話期是一個或多個程序組的集合
setsid();
(3)改變當前目錄為根目錄
通常的做法是讓“/”或“/tmp”作為守護程序的當前工作目錄
在程序執行過程中,當前目錄所在的檔案系統時不能解除安裝的
chdir函式可以改變程序當前工作目錄
chdir("/");
(4)重設檔案許可權掩碼
檔案許可權掩碼是指檔案許可權中被遮蔽掉的對應位把檔案許可權掩碼設定為0,可以增加該守護程序的靈活性。
umask(0);
(5)關閉檔案描述符
新建的子程序會從父程序那裡繼承所有已經開啟的檔案,在建立新的會話後,該守護程序已經脫離任何控制終端,應當關閉用不到的檔案。(一般情況下只需要關閉系統自帶的三個檔案描述符就行),如果在這裡需要列印除錯,“先不脫離,關閉”。
close(0); close(1); close(2);