Linux應用編程之——進程

Linux應用編程之——進程

進程:這個我想大家都應該有所瞭解,進程是進程實體的運行過程,是系統進行資源分配和調度的基本單位,進程由PCB(進程控制塊)控制。
就好比什麼呢,你在shell下面,可以同時運行好幾個main函數,這些就是代表好幾個進程。

進程狀態

進程有如下幾種狀態:
R 運行狀態。嚴格來說,應該是”可運行狀態”,即表示進程在運行隊列中,處於正在執行或即將運行狀態,只有在該狀態的進程纔可能在 CPU 上運行,而同一時刻可能有多個進程處於可運行狀態。
S 可中斷的睡眠狀態。處於這個狀態的進程因爲等待某種事件的發生而被掛起,比如進程在等待信號。
D 不可中斷的睡眠狀態。通常是在等待輸入或輸出(I/O)完成,處於這種狀態的進程不能響應異步信號。
T 停止狀態。通常是被shell的工作信號控制,或因爲它被追蹤,進程正處於調試器的控制之下。
Z 退出狀態。進程成爲殭屍進程。
X 退出狀態。進程即將被回收。
s 進程是會話其首進程
l 進程是多線程的
. 進程屬於前臺進程組。
< 高優先級任務。
在這裏插入圖片描述

相關函數

1、創建進程

啓動進程

int system(const char *command);

這個system ()函數是C標準庫中提供的,它主要是提供了一種調用其它程序的簡單方法。讀者可以利用system()函數調用一些應用程序,它產生的結果與從 shell中執行這個程序基本相似。事實上,system()啓動了一個運行着/bin/sh的子進程,然後將命令交由它執行。

pid_t fork(void);//不接收參數。調用一次會返回兩次!!!
//返回值等於0的就是子進程,返回值大於0的就是父進程。

init進程可以啓動一個子進程,它通過fork()函數從原程序中創建一個完全分離的子進程,當然,這只是init進程啓動子進程的第一步,後續還有其他操作的。不管怎麼說,fork()函數就是可以啓動一個子進程
具體的創建方式如下

main()
{
	pid_t pid=fork();
	if(pid==0){  //子進程
	}
	if(pid>0{//父進程
	}
	if(pid<0{//出錯
	}
}

在這裏插入圖片描述

注意點:如果子進程查詢父進程pid的時候,如果父進程已經死掉了,那麼就會直接返回init進程的pid
可以通過命令 ps -aux | grep “1990” 查看
ps -aux
-A :所有的進程均顯示出來,與 -e 具有同樣的效用;
-a : 顯示現行終端機下的所有進程,包括其他用戶的進程;
-u :以用戶爲主的進程狀態 (當前用戶);
x :通常與 a 這個參數一起使用,可列出較完整信息。

2、獲取進程

getpid(當前pid)   
getppid(當前父pid)  
getuid   
geteuid 
getgid 
getegid

3、 exce系列函數

事實上,使用fork()函數啓動一個子進程是並沒有太大作用的,因爲子進程跟父進程都是一樣的,子進程能幹的活父進程也一樣能幹,因此世界各地的開發者就想方設法讓子進程做不一樣的事情,因此就誕生了exce系列函數,這個系列函數主要是用於替換進程的執行程序,它可以根據指定的文件名或目錄名找到可執行文件,並用它來取代原調用進程的數據段、代碼段和堆棧段,在執行完之後,原調用進程的內容除了進程號外,其他全部被新程序的內容替換。另外,這裏的可執行文件既可以是二進制文件,也可以是Linux下任何可執行腳本文件。簡單來說就是覆蓋進程,舉個例子,A進程調用exce系列函數啓動一個進程B,此時進程B會替換進程A,進程A的內存空間、數據段、代碼段等內容都將被進程B佔用,進程A將不復存在。
exec 族函數有 6 個不同的 exec 函數,函數原型分別如下:

int execl(const char *path, const char *arg, ...)

int execlp(const char *file, const char *arg, ...)

int execle(const char *path, const char *arg, ..., char *const envp[])

int execv(const char *path, char *const argv[])

int execvp(const char *file, char *const argv[])

int execve(const char *path, char *const argv[], char *const envp[])

這些函數可以分爲兩大類, execl、 execlp和execle的參數個數是可變的。execv、execvp和execve的第2個參數是一個字符串數組,參數以一個空指針NULL結束,無論何種函數,在調用的時候都會通過參數將這些內容傳遞進去,傳入的參數一般都是要運行的程序(可執行文件)、腳本等。

總結來說,可以通過它們的後綴來區分他們的作用:

名稱包含 l 字母的函數(execl、 execlp 和execle)接收參數列表”list”作爲調用程序的參數。

名稱包含 p 字母的函數(execvp 和execlp)接受一個程序名作爲參數,然後在當前的執行路徑中搜索並執行這個程序;名字不包含p字母的函數在調用時必須指定程序的完整路徑,其實就是在系統環境變量”PATH”搜索可執行文件。

名稱包含 v 字母的函數(execv、execvp 和 execve)的命令參數通過一個數組”vector”傳入。

名稱包含 e 字母的函數(execve 和 execle)比其它函數多接收一個指明環境變量列表的參數,並且可以通過參數envp傳遞字符串數組作爲新程序的環境變量,這個envp參數的格式應爲一個以 NULL 指針作爲結束標記的字符串數組,每個字符串應該表示爲”environment =

virables”的形式。

下面作者就具體某個函數做介紹:

函數:

int execl(const char *path, const char *arg, ...)

execl()函數用於執行參數path字符串所代表的文件路徑(必須指定路徑),接下來是一系列可變參數,它們代表執行該文件時傳遞過去的argv[0]、argv[1]… argv[n],最後一個參數必須用空指針NULL作爲結束的標誌。

4、 終止進程

在Linux系統中,進程終止(或者稱爲進程退出,爲了統一,下文均使用”終止”一詞)的常見方式有5種,可以分爲正常終止與異常終止:

正常終止:

從main函數返回。
調用exit()函數終止。
調用_exit()函數終止。
異常終止:

調用abort()函數異常終止。
由系統信號終止。
在Linux系統中,exit()函數定義在stdlib.h中,而_exit()定義在unistd.h中,exit()和_exit()函數都是用來終止進程的,當程序執行到exit()或_exit()函數時,進程會無條件地停止剩下的所有操作,清除包括 PCB在內的各種數據結構,並終止當前進程的運行。
在這裏插入圖片描述

5、 wait

函數原型

pid_t wait(int *wstatus);
pid_t waitpid(pid_t pid, int *wstatus, int options);

waitpid()函數 的作用和wait()函數一樣,但它並不一定要等待第一個終止的子進程,它還有其他選項,比如指定等待某個pid的子進程、提供一個非阻塞版本的wait()功能等。實際上 wait()函數只是 waitpid() 函數的一個特例,在 Linux內部實現 wait 函數時直接調用的就是 waitpid 函數。
wait()函數有幾點需要注意的地方:

wait()要與fork()配套出現,如果在使用fork()之前調用wait(),wait()的返回值則爲-1,正常情況下wait()的返回值爲子進程的PID。
參數wstatus用來保存被收集進程退出時的一些狀態,它是一個指向int類型的指針,但如果我們對這個子進程是如何死掉毫不在意,只想把這個殭屍進程消滅掉,(事實上絕大多數情況下,我們都會這樣做),我們就可以設定這個參數爲NULL。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章