引入:
今天開始學習linux應用層開發的必備知識,進程。我們之前的實時系統中,都是任務的概念,linux中也有任務概念,同時還有進程和線程,他們之間是什麼關係呢?
其實在我們的windows中也有進程的概念,通過任務管理器就能查看到當前運行的進程。
開始學習之前先看下思維導圖,把握這節的知識點,然後在進行詳細的解讀。
我們將上面的內容進行詳細學習。
一.進程的基本知識
1.進程的概念
1>當我們打開windows時,說讓你殺死一個進程,那什麼是進程?
理解上,進行中的程序就是進程唄,程序的一次執行過程,是動態的(佔用CPU和內存),包括創建,調度,執行和消亡。
2>那麼和程序有啥區別?
很明顯,程序是靜態的,是存放在磁盤上的指令和數據的有序集合。
3>一個進程又包含哪些內容呢?
從我們的框圖中也能清晰的看到,它包括進程控制塊,也就是我們知道的PCB控制塊,還要有cpu寄存器,堆棧,還有和程序中共有的部分,正文段,用戶數據段,系統數據段。直接看框圖更好理解
2.進程的特性:
1>併發性:多進程併發執行
2>動態性:進程狀態不斷變化
3>交互性:進程間交互,同步互斥等
4>獨立性:地址空間相互獨立
3.進程的類型
主要就是三大類型
1>交互式進程:典型的就是shell命令進程,可前臺運行,也可以後臺運行
2>批處理進程:與終端無關,作業隊列中順序執行,不需要很快響應,編譯器的編譯操作,數據庫搜索等都屬於批處理進程
3>守護進程:後臺運行,系統進程都是以這種形式存在。
4.進程的狀態:
運行態,等待態,停止態,死亡態。
二.進程的常見命令
我們對進程有了一點了解,那麼我們還是要在終端上對進程進行一些操作,如查看一下狀態啊等。
1.進程的查看,常用的幾個命令
ps aux 查看所有進程信息,包括誰創建的,pid,cpu佔用率,狀態等
ps -elf
ps -elf|grep test 詳細的查看某一個進程的信息
top 查看進程動態信息。
top |grep init 詳細的查看某一個進程的動態信息
2.改變進程優先級
1> nice 按照用戶指定的優先級運行(優先級範圍-20--19)
例:
./test 然後通過ps -elf|grep test 查看test進程的信息,pid等。
nice -n 19 ./test 修改完後通過ps -elf|grep test 查看pid進程號,
然後top -p 10113(進程號),查看進程動態詳細信息,發現進程優先級已經改成功
2>renice 運行態,改變正在運行的進程優先級
例:renice 10 10113 優先級就改變了。
3.進程的前後臺操作
jobs查看後臺幾個進程
bg 將掛起的進程運行
fg 1 將後臺進程放到前臺運行
ctrl+z 前臺變爲後臺,並停止。
三.進程的創建和結束及回收
1.進程創建
pid_t fork(void); 一個fork會有兩個返回值,通過不同的返回值得到父子進程,是個岔子函數。
失敗:-1 父進程:返回子進程進程號 子進程:返回0
int main(){
pid_t pid;
int i;
pid = fork();
if(pid <0){
perror("fork");
return 0;
}else if(pid==0){
printf("son\n");
}else{
printf("father\n");
}
while(1) sleep(1);
}
2.進程的結束
void exit(int status); 結束時會刷新緩衝區。
void _exit(int status); 結束當前進程並將status返回
3.父子進程關係
1> 子進程繼承父進程內容
2> 父子進程有獨立空間,互不影響
3> 若父進程先結束,子進程成爲孤兒進程,被init進程收養,子進程變爲後臺進程
4> 若子進程先結束,父進程沒有及時回收,子進程變爲殭屍進程。
5> 子進程是從fork後開始運行,不是從main開始
6> 父子進程誰先運行依賴於操作系統調度策略
7>父子進程可以多次創建
4.進程的回收
子進程結束時由父進程回收,若不及時回收就會出現殭屍進程。
孤兒進程由init 進程回收。
1> pid_t wait(int *status)
成功:子進程進程號 失敗:EOF
子進程沒有結束,父進程一直阻塞
status爲NULL表示直接釋放進程PCB,不接收返回值(不接收遺言)
2> pid_t waitpid(pid_t pid,int *status,int option)
指定回收哪一個子進程。option 指定回收方式
3>通過以下幾個宏定義,可以判斷子進程是什麼原因死的
WIFEXITE(status)判斷子進程是否正常結束
WEXITSTATUS(status)獲取子進程返回值
WIFSIGNALED(status)判斷子進程是否被信號結束
WTERMSIG(status)獲取結束子進程的信號類型
int main(){
pid_t pid;
int i;
int status;
pid = fork();
if(pid <0){
perror("fork");
return 0;
}else if(pid==0){
printf("bbbbbb\n");
sleep(2);
exit(2);
}else{
printf("aaaaaa\n");
//wait(&status); //回收進程
sleep(4);
waitpid(pid,&status,WNOHANG); //回收指定進程
printf("st=%d\n", WEXITSTATUS(status));
}
while(1) sleep(1);
return 0;
}
四.exec函數族
爲了實現父子進程執行不同的程序,exec函數族就是爲了執行第三方程序。
以下幾個函數都是爲了執行第三方程序,只是操作方式不一樣。
int execl(const char *path, const char *arg, …);
if(execl("/bin/ls", "ls", "-a", "-l", "./", NULL) < 0) {
perror("execl");
}
int execlp(const char *file, const char *arg, …);
if(execlp("ls", "ls", "-a", "-l", "./", NULL) < 0) {
perror("execl");
} //從path路徑下查找文件
int execv(const char *path, char *const argv[]);
char *arg[] = {"ls", "-a", "-l", "./", NULL};
printf("aaaaaaaaaaah\n");
if (execv("/bin/ls", arg) < 0) {
perror("execv");
}
int execvp(const char *file, char *const argv[]);
char *arg[] = {"ls", "-a", "-l", "./", NULL};
printf("aaaaaaaaaaah\n");
if (execvp("ls", arg) < 0) {
perror("execv");
}
上面幾個函數執行了第三方函數,但是自己的函數也不能丟啊,就有了system函數
int system(const char *command);
system("ls -l -a ./");
相當於把exec函數進行了封裝,fork了一個進程,子進程exec執行操作,父進程阻塞等待子進程結束後,繼續執行
不替換程序內容。
五.守護進程
作爲三大進程之一,重要性自然不必多說,那麼什麼是守護進程,又如何創建呢??
1.守護進程特點
始終後臺運行,獨立於任何終端,週期性的執行某種任務或等待處理特定事件。
一般的進程關閉會話後,所有進程就會結束。
2.守護進程的創建過程
1>先與終端脫離關係,讓子進程變爲孤兒進程。
2>子進程創建一個新的會話,併成爲新的會話組長。
3>守護進程一直在後天運行,其工作目錄不能被卸載,重新設定當前目錄(假如是臨時目錄,容易出現問題,所以更改目錄,路徑不限定)
4>改變文件掩碼,防止設置權限時受影響
5>關閉所有從父進程繼承的打開文件
最後查看編寫的程序是否爲後臺程序,關閉終端後是否會影響進程。
只能用kill殺死進程。
創建一個守護進程,將系統時間保存到log文件中
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(){
pid_t pid;
pid = fork();
if(pid<0){
perror("fork");
return -1;
}else if(pid>0){
exit(0);
}
pid = setsid();
if(pid==-1){
perror("setsid");
return -1;
}
chdir("/");
umask(0);
int i;
for(i = 0;i<2;i++){
close(i);
}
FILE *fp;
time_t ctm;
fp = fopen("1.log","w");
while(1){
ctm = time(NULL);
fputs(ctime(&ctm),fp);
fflush(fp);
sleep(1);
}
}