進程的控制
什么是進程?
狹義上來說:進程是操作系統上運行的一個程序。
廣義上來說:進程是一個具有一定獨立功能的程序關于某個數據集合的一次運行活動。它是操作系統動態執行的基本單元,在傳統的操作系統中,進程既是基本的分配單元,也是基本的執行單元。
進程控制是進程管理中最基本的功能。它用于創建一個新進程,終止一個已完成的進程,或者去終止一個因出現某事件而使其無法運行下去的進程,還可負責進程運行中的狀態轉換。
Linux系統上進程的幾種狀態:
- 運行狀態:進程正在運行,或者在運行隊列中等待運行。
- 可中斷等待狀態:進程在等待某個事件的完成,在等待中不可以被信號或定時器喚醒,必須等待直到等待的事情發生。
- 僵死狀態:進程已經終止,但是進程的描述符還在,直到父進程的wait函數釋放。
-
停止狀態:進程收到系統發出的信號(
SIGSTOP
SIFTOP
SIGTIN
SIGTOU
)之后停止運行或者該進程正在被跟蹤(調試應用程序gdb
,進程處于跟蹤狀態)。
進程的創建
進程的創建一是操作系統來創建。二是由父進程創建。
-
使用
fork
函數來分配一個新的進程。函數 函數頭文件 函數原型 函數功能 fork
#include<sys/types> & #include <unistd.h>
pid_t fork(void)
創建一個新的子進程,繼承原有進程(父進程)的很多屬性``(用戶ID 組ID 當前工作環境 等等)
vfork
#include<sys/types> & #include <unistd.h>
pid_t vfork(void)
類似fork但是使用vfork時系統會讓子進程共享父進程的地址空間,父進程與子進程的操作互相可見。
函數 相同 不同 fork
都是創建一個子進程
占用資源的不同,fork占用較大的系統資源,fork一個新的進程會將父進程的很多都會復制,增大了消耗
vfork
子進程完全運行在父進程的地址空間上,子進程對于父進程的改變都變得可見。
函數 | 相對于父進程來說 |
---|---|
fork |
父進程與子進程運行的順序不一定,父進程與子進程運行的時候,兩個會搶系統的調用時間。 |
vfork |
子進程先比父進程運行,等子進程運行完之后,父進程再運行。 |
創建守護進程
什么是守護進程?這是一段來自維基百科的描述。
-
在一個多任務的電腦操作系統中,守護進程(英語:daemon)是一種在后臺執行的電腦程序。此類程序會被以進程的形式初始化。守護進程程序的名稱通常以字母“d”結尾:例如,syslogd就是指管理系統日志的守護進程。
通常,守護進程沒有任何存在的父進程(即PPID=1),且在UNIX系統進程層級中直接位于init之下。守護進程程序通常通過如下方法使自己成為守護進程:對一個子進程運行fork,然后使其父進程立即終止,使得這個子進程能在init下運行。這種方法通常被稱為“脫殼”。
系統通常在啟動時一同起動守護進程。守護進程為對網絡請求,硬件活動等進行響應,或其他通過某些任務對其他應用程序的請求進行回應提供支持。守護進程也能夠對硬件進行配置(如在某些Linux系統上的devfsd),運行計劃任務(例如cron),以及運行其他任務。
Linux創建一個守護進程創建
孤兒進程與僵尸進程
-
當父進程先于子進程退出時,子進程會被
init
或這systemd
接收為子進程。這一過程亦被稱為收養,雖然有PID為1的系統進程
當做父進程,但是創建他的父進程已經被系統回收了。Ubuntu 18.04 LTS的環境下時,收養的父進程為圖形界面的進程。只有進入字符界面運行時,對應收養的父進程才為1
-
創建孤兒進程與孤兒進程
#include <sys/types.h> #include <unistd.h> #include <sys/wait.h> #include <stdio.h> //孤兒進程,父進程先于子進程退出。 int main(void){ int pid; pid = fork(); if(pid == 0){ printf("I am child process.\n"); sleep(5); //確保子進程后于父進程退出,子進程由系統收養并wait掉。 } if(pid > 0){ printf("I am father process.\n"); sleep(1); } return 0; } #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> #include <stdio.h> //孤兒進程,父進程先于子進程退出。 int main(void){ int pid; pid = fork(); if(pid == 0){ printf("I am child process.\n"); exit(0); } //死循環,父進程沒有使用wait函數等待子進程運行,沒有人清理將是進程。 if(pid > 0){ printf("I am father process.\n"); while(1){} } return 0; }
wait 和 waitpid
-
wait
和waitpid
原型 作用 pid_t wait(int *statloc); 父進程暫停,等待子進程結束 pid_t waitpid(pid_t pid, int *statloc, int options); 父進程暫停,等到特定的子進程結束
進程的退出
正常退出 | 異常退出 |
---|---|
調用函數exit\_exit
|
調用about函數 或者由系統發出signal 退出。 |
exit()與_ecit()的區別
函數 | 區別 |
---|---|
exit() |
調用該函數后,系統會先執行一些清除操作,然后返還給內核。 |
_exit() |
調用該函數后,馬上拋給內核。 |
exit與return的區別
區別 | 操作 |
---|---|
exit |
exit 用于結束一個程序 |
return |
return 用于函數,返回后回到調用的上一層函數 |
exit
的參數,正常退出參數為0,異常退出參數為非零值。
執行新的程序
-
父進程
fork
一個子進程后,使用exec
函數來調用另外的可執行程序代替當前進程的可執行映像。進程一旦調用
exec
函數后,相當于將原有的進程殺死,只保留進程ID,對于系統而言,同樣的進程,執行的操作不一樣了。 -
exec函數家族
**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[]);
-
例子:
其他的
-
獲得進程ID
getpid
獲得進程IDgetppid
-
設置用戶和用戶組
-
設置有效用戶ID和實際用戶ID
setpid
-
設置實際組和有效組
setgid
若進程具有root權限,則函數將實際用戶ID有效用戶ID設置為參數UID。
若進程沒有root權限,但UID為實際用戶,setuid只將有效用戶ID設為UID。
-
-
-
改變進程的優先級。
nice
函數函數 原型 作用 nice int nice(int increment) 改變程序的優先級 getpriority int getpriority(int which, int who) 獲取文件優先級 setpriority int setpriority(int which, int who, int prio) 設置文件優先級