文章目錄
- 1、進程間關系
- 1.1 進程組
- 1.2 組長進程
- 2、會話?
- 2.1 查看會話
- 2.2 創建會話
- 3、控制終端
- 4、作業控制
- 4.1 前臺/后臺進程
- 5、守護進程
- 5.1 如何創建守護進程?
- 5.2 殺掉守護進程
1、進程間關系
主要描述兩個名稱概念:即進程組和組長進程。
1.1 進程組
什么是進程組?通過名稱可以看到它包含很多進程,所有進程在一個組內,當然名稱雖然是進程組,其實在Linux中,單個進程也是有自己的組。首先,通過看一個進程在Linux中是如何描述的?
PPID:
表示該運行的進程所屬的父進程ID信息,即該進程的父親。
PID:
表示該運行進程的唯一標識符。
PGID:
表示改進程所屬進程組的ID,(由于是單進程因此進程組ID就是自己的PID,也就是為什么說進程組中可以有一個進程。)
SID:
表示會話ID信息
TTY:
表示該進程所屬于哪一個終端,(顯示的是終端號,本質就是在/dev/pts下的一個文件)
UUID:
表示是哪一個用戶執行的該進程,即用戶名,只不過Linux中是用數字描述的。
我們看到描述一個進程的信息有PPID、PID、PGID等,接下來要講述的就是PGID?
首先來看一個指令,該指令是由3個相同的指令組合,用管道連接的 ,我們知道在Linux中一條運行的指令就是一個進程。
從圖中可以看到啟動了3個進程,他們同屬于一個組,因為PGID都是393911
,仔細觀察看到該進程組ID就是第一個運行起來的進程PID。
總結就是:當多個進程在運行時候,他們有各自的PPID、PID,但是PGID是相同的,因為是一個進程組,并且規定進程組ID就是第一進程執行時的PID。
上面看到的是由多個進程組成的進程組,所描述的信息就是上面所展示的,那么只有一個進程運行的時候又是如何描述的?如下顯示的:
上面就是單個進程運行時,他的進程組就是自己的PID,也符合:第一個進程運行的PID作為PGID。
看到多個進程和單個進程的進程組描述如上,那么當我們在一個進程中又創建了子進程,然后讓父進程直接退出,此時進程又是如何描述的?如下:
可以看到當一開運行進程的時候,父進程在程序中設置的是延時5秒,此時進程有自己的PPID、PID、PGID,由于是單進程組成的進程組,所以是自己的PID,當5秒結束后,程序中緊接著創建子進程,并且讓父進程直接退出,此時該進程就變為了孤兒進程,被1號進程領養,但所屬的進程組ID是不變的,還是第一個進程運行的PID,只是PID變為了子進程的PID了。
總結:在多進程中實際上以進程組的關系執行的,進程組就是一個或多個進程的集合,一個進程組可以有多個進程,也可以只有一個進程。PGID就是第一運行進程的PID,同時只有當最后一個進程退了,該組才全部退出,與組長進程是否退出沒有關系。
1.2 組長進程
通過上面的描述,顯而易見組長進程就是在進程組中第一個運行的進程,對應的PGID就是該進程的PID。
查看對應列屬性的數據指令:ps -eo 對應進程列屬性
2、會話?
會話和上面的進程組有關系,即在一個會話中可以包含一個或者多個進程組,因此一個或多個進程組集合就是一個會話。
2.1 查看會話
通過上面的圖可以看出,每創建一個終端登錄界面,登錄后就會有對應的SID,因此會話就可以理解為從用戶登錄該終端開始到該終端退出關閉,在該過程中所操作的所有事。通過SID,即會話ID來區分不同會話區。
2.2 創建會話
在看到會話所表示的區域后,我們自己如何創建新的會話呢?
即通過setseid
函數創建會話。該函數使用前提就是不能讓組長進程來創建會話。
#include <unistd.h>
/*
*功能:創建會話
*返回值:創建成功返回 SID, 失敗返回-1 */
pid_t setsid(void);
為了不讓創建的進程是組長進程,通常是在進程中創建子進程來創建會話。
3、控制終端
簡單來說,控制終端就是用戶在通過終端登錄系統過后得到的一個shell進程,登陸后所在的終端就是該shell的控制終端。控制終端保存在PCB中的,因此在shell中啟動的所有進程都屬于該登錄的終端。 每個進程的標準輸入、輸出、錯誤都會指向該控制終端。在理解控制終端后,它還和會話、進程組有這些關系:
①一個會話擁有一個控制終端,通常打開的首個會話進程打開終端后,該終端就是該會話的控制終端。
②和控制終端建立的會話的首進程叫控制進程。
③在一個會話中的多個進程組中可以被分為一個前臺進程組和一個或多個后臺進程組。控制終端的指令只能發送給前臺進程組。
4、作業控制
什么是作業?每個學生都知道作業這件事,但這里的作業是指用戶為了完成某件事而啟動的進程。一個作業可以包含一個進程或者是進程組,進程之間相互協調。
作業控制?
誰控制?在Shell中分前后臺來控制,控制的就是作業或者是進程組。一個前臺作業可以是多個進程組,后臺也可以是多個。Shell可以同時運行一個前臺作業或多個作業前臺作業,即為作業控制。
4.1 前臺/后臺進程
我們用戶輸入執行程序指令時大多數都是前臺運行的,前臺運行有個特點就是只有等前臺運行結束后用戶再輸入指令才有用,才會被控制終端接收。
./exe:
該方式運行的就是前臺進程。
變為后臺進程方式:./exe &
在同一個會話中,可以允許多個后臺進程(組),但前臺進程(組)只允許一個。
前后臺進程的標志:看誰應該從標準輸入中獲取數據。明確的是只有前臺進程才可以獲取。
把后臺變為前臺:fg 進程名
,也可以通過作業號進行移動。
前臺變為后臺:先停止前臺進程(ctrl+z),在bg 進程名
5、守護進程
守護進程不受控制終端關閉的影響,而是長時間運行的。我們就是把在會話的中的進程組拿出來放在單獨的放到獨立的會話中,此時該進程組就是守護進程。
那么如何創建會話,前面說了通過setsid()
函數,注意不能是組長進程。
既然不能是組長進程,我們在寫程序的時候,主函數中就是父進程,第一個先運行的,即為組長進程,因此不能在主函數中創建會話,需要在子進程中創建會話。讓父進程直接退出。此時就會想到孤兒進程,因此守護進程就是孤兒進程的一種特殊。
5.1 如何創建守護進程?
步驟:
①忽略信號。
②創建子進程,在其中創建會話。
③更改工作目錄。
④重定向標準輸入輸出到/dev/null。
#pragma once#include<iostream>
#include<string>
#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>//更改工作目錄
const char *root="/";//根目錄
const char *dev_null="/dev/null";void Demo(bool isChdir,bool isClose)
{//1、忽略可能引起程序錯誤的信號signal(SIGCHLD,SIG_IGN);signal(SIGPIPE,SIG_IGN);//2、創建子進程if(fork()>0){exit(0);//父進程退出}//3、子進程中創建會話setsid();//4、查看是否更改CWDif(isChdir){chdir(root);//更改為根目錄}//5、此時已經是守護進程了,不需要和用戶的標準輸入、輸出、錯誤關聯if(isClose){close(0);close(1);close(2);}else{//也可以重定向int fd=open(dev_null,O_RDWR);if(fd>0){//后者重定向到前者dup2(fd,0);dup2(fd,1);dup2(fd,2);close(fd);}}
}
通過調用編寫的函數,把進程變為守護進程
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include "Demo.hpp"
int main()
{// sleep(5);// if(fork()>0)// {// exit(1);// }// while(1)// {// std::cout<<"child PPID:"<<getppid()<<" PID:"<<getpid()<<std::endl;// sleep(1);// }//Demo(false, false);while (1){std::cout << "hello" << std::endl;sleep(1);}return 0;
}
5.2 殺掉守護進程
kill -9 PID