目錄
任務管理
jobs,fg,bg
進程組概念
任務概念
守護進程
守護進程的概念
守護進程的查看
守護進程的創建
?編輯模擬實現daemon函數
任務管理
每當有一個用戶登錄Linux時,系統就會創建一個會話(session)
任何進程都可以被設置為前臺進程,只要前臺進程被干掉系統會自動的將bash放到前臺變成前臺進程
我下面寫了一個死循環,創建一個文件 test.cc 測試它被干掉之后bash進程是否會自動頂替它
可以看到在a.out運行期間不管我們輸入多少次指令都是沒有響應的,原因就是此時的前臺進程已經不是bash了,當我們使用ctrl + c將a.out終止之后就可以看到,我們輸入的ls指令受到了響應,原因就是a.out執行結束之后,前臺進程被bash自動頂上了
jobs,fg,bg
將當前任務變成前臺任務
當我們以后臺進程的方式運行a.out時,我們發送的指令,bash是會給我們進行響應的,那我們如果想將a.out變成前臺進程應該怎么做呢?
fg + 任務號
任務號:
為了方便演示我們將a.out打印的值追加重定向到log.txt中
同樣的,我們創建三個文件 test.cc,test1.cc,test2.cc 讓它生成可執行程序
然后將這三個啟動的進程追加重定向到log.txt中
我們可以使用 jobs 指令來查看當前會話中的所有后臺進程
后臺進程的殺死方式
1.可以使用kill直接殺掉進程
2.提到前程,再使用鍵盤組合鍵殺死
暫停前臺進程 ctrl + z 組合鍵
當我們使用ctrl + z將前臺進程暫停之后,系統會將bash提到前臺,讓bash充當前臺進程
此時我們向鍵盤文件中輸入?ls pwd 指令是沒有響應的
當我們使用ctrl+z將該進程暫停之后,bash自動充當前臺進程,這時我們輸入指令就會得到響應
恢復已經暫停的進程? ?bg指令
使用方式:bg + 任務號
怎么區分前臺進程和后臺進程?
后臺進程無法從標準輸入獲取數據,所以誰擁有鍵盤文件,誰就是前臺進程
進程組概念
每個進程除了有一個進程ID之外,是屬于一個進程組,進程組是一個或多個進程的集合
通常它們與同任務相關聯,可以接收來自同一終端的各種信號。每個進程組有一個唯一的進程組ID。每個進程組都有一個組長進程。組長進程的標識是,其進程組ID等于其進程ID。組長進程可以創建一個進程組,創建該組中的進程,然后終止
注意:進程組中,只要有進程存在則該進程組就存在,這與其組長進程是否終止無關
PGID:進程組ID
SID:用來標識系統中的某一個會話(session)
TTY:該用戶使用的終端編號
注意:前臺進程的狀態后面有一個 + 號,后臺進程則沒有 + 號?
任務概念
Shell分前后臺來控制的不是進程而是任務或者進程組
一個前臺任務可以由多個進程組成,一個后臺任務也可以由多個進程組成,Shell可以運行一個前臺作業和任意多個后臺任務,這稱為作業控制
作業和進程組的區別:
如果作業中的某個進程有創建出了子進程,則子進程不屬于任務,一旦任務運行結束,Shell就把自己提到前臺,如果原來前臺進程還存在,也就是這個被創建的子進程還沒有終止,那么它將自動變為后臺進程組
守護進程
守護進程的概念
此時,我們打開三個后臺進程,我們看看用戶退出時這三個進程是否會收到影響
用戶退出之后,啟動的進程也隨之受到了影響
怎么讓這些進程不受到用戶登錄和退出的影響呢?我們可以將這些進程守護進程化
怎么讓進程守護進程化
守護進程也稱精靈進程(Daemom),是運行在后臺的一種特殊進程,它獨立于控制終端并且周期性地執行某種任務或等待處理某些發生的事件
守護進程是一種很有用的進程,Linux的大多數服務器就是用守護進程實現的,比如Internet服務器inetd,Web服務器httpd等。同時守護進程完成許多系統任務,比如作業規劃進程crond等。
Linux系統啟動時會啟動很多系統服務進程,這些系統服務進程沒有控制終端,不能直接和用戶交互。其他進程都是在用戶登錄或運行程序時創建,在運行結束或用戶注銷時終止,但系統服務進程不受用戶登錄注銷的影響,它們一直在運行著,這種進程有一個名稱叫守護進程(Daemon)。
守護進程的查看
我們可以使用ps ajx指令查看系統中的進程:
- 參數a表示不僅列出當前用戶的進程,也列出所有其他用戶的進程。
- 參數x表示不僅列出有控制終端的進程,也列出所有無控制終端的進程。
- 參數j表示列出與作業控制相關的信息。
凡是TPGID一欄寫著-1的都是沒有控制終端的進行,也就是守護進程
除此之外,在COMMAND一列用[ ]括起來的名字表示內核線程,這些線程在內核里創建,沒有用戶空間代碼,因此沒有程序文件名和命令行,通常采用以k開頭的名字,表示Kernel。
個別說明:
- udevd負責維護/dev目錄下的設備文件。
- acpid負責電源管理。
- syslogd負責維護/var/log下的日志文件。
可以看出,守護進程通常采用以d結尾的名字,表示Daemon。
守護進程的創建
1.忽略其他異常信號,以免收到信號影響到守護進程(SIGCLD,SIGPIPE,SIGSTOP)
2.將自己變成獨立的會話
3.更改當前調用進程的工作目錄
4.將標準輸入,標準輸出,標準錯誤重定向到 dev/null 中
相關說明:
1.調用setsid創建新會話的目的,是讓當前進程自稱會話與當前的bash脫離關系(創建守護進程的核心)。
2.調用setsid創建新會話時,要求調用進程不能是進程組組長,但是當我們在命令行上啟動多個進程協同完成某種任務時,其中第一個被創建出來的進程就是組長進程,因此我們需要fork創建子進程,讓子進程調用setsid創建新會話并繼續執行后續代碼,而父進程直接退出即可
3.我們一般將守護進程的工作目錄設置為根目錄,便于讓守護進程以絕對路徑的方式訪問某種資源
4.守護進程不能直接和用戶交互,也就是說守護進程已經與終端去關聯了,因此一般我們會將守護進程的標準輸入,標準輸出,標準錯誤都重定向到 /dev/null 中, /dev/null是一個字符文件(設備),通常用于屏蔽、丟棄輸入輸出信息
#include <iostream>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string>const std::string filename = "/dev/null";//守護進程化
int main()
{// 1.屏蔽信號signal(SIGCLD, SIG_IGN);signal(SIGPIPE, SIG_IGN);signal(SIGSTOP, SIG_IGN);// 2.fork子進程,子進程執行setsid(),創建守護進程,父進程退出if (fork() > 0)exit(0);setsid();// 3.將守護進程的工作路徑更改為根目錄chdir("/");// 由于守護進程沒有終端,不能和用戶進程交互,所以我們需要將標準輸入,標準輸出,標準錯誤重定向到 /dev/null下int fd = open(filename.c_str(), O_RDWR, 000);dup2(fd, 0);dup2(fd, 1);dup2(fd, 2);return 0;
}
可以看到當前進程已經和終端去關聯了
通過 ls /proc/進程id -al 命令我們可以看到該進程的工作目錄已經成功修改為了根目錄
通過 ls /proc/進程id/fd -al 命令,我們可以看到該進程的標準輸入,標準輸出,標準錯誤,重定向到了 /dev/null 中
調用daemom創建守護進程
實際當我們創建守護進程時可以直接調用daemom接口進行創建,daemom函數的函數原型如下:
int daemon(int nochdir, int noclose);
參數說明:
- 如果參數nochdir為0,則將守護進程的工作目錄該為根目錄,否則不做處理。
- 如果參數noclose為0,則將守護進程的標準輸入、標準輸出以及標準錯誤重定向到
/dev/null
,否則不做處理。
#include <iostream>
#include <unistd.h>
#include <string>const std::string filename = "/dev/null";// 守護進程化
int main()
{daemon(0, 0);// 死循環,守護進程是不退的while (true);return 0;
}
調用daemon函數創建的守護進程與我們原生創建的守護進程差距不大

模擬實現daemon函數
有了上述創建守護進程的代碼,要模擬實現daemon函數就很容易了,我們只需要設置兩個參數nochdir和noclose,當所給nochdir為0時,我們將守護進程的工作目錄該為根目錄,當所給noclose為0時,我們則將守護進程的標準輸入、標準輸出以及標準錯誤重定向到/dev/null即可。
#include <iostream>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string>const std::string filename = "/dev/null";// 守護進程化
void Daemom(const std::string &Chdir)
{// 1.屏蔽信號signal(SIGCLD, SIG_IGN);signal(SIGPIPE, SIG_IGN);signal(SIGSTOP, SIG_IGN);// 2.fork子進程,子進程執行setsid(),創建守護進程,父進程退出if (fork() > 0)exit(0);setsid();// 3.將守護進程的工作路徑更改為根目錄if(!Chdir.empty()) chdir("/");// 由于守護進程沒有終端,不能和用戶進程交互,所以我們需要將標準輸入,標準輸出,標準錯誤重定向到 /dev/null下int fd = open(filename.c_str(), O_RDWR, 000);if (fd > 0){dup2(fd, 0);dup2(fd, 1);dup2(fd, 2);}// daemon(0, 0);// 死循環,守護進程是不退的while (true);return;
}