性无码一区二区三区在线观看,少妇被爽到高潮在线观看,午夜精品一区二区三区,无码中文字幕人妻在线一区二区三区,无码精品国产一区二区三区免费

一口Linux
認(rèn)證:優(yōu)質(zhì)創(chuàng)作者
所在專題目錄 查看專題
Linux信號量(3)-內(nèi)核信號量
兩個線程,兩個互斥鎖,怎么形成一個死循環(huán)?
Linux庫概念,動態(tài)庫和靜態(tài)庫的制作,如何移植第三方庫
關(guān)于線程調(diào)度,你需要了解的幾個基礎(chǔ)知識點都在這里了
從概念到代碼,一文了解Linux系統(tǒng)守護進(jìn)程知識
如何用C語言操作sqlite3,一文搞懂
作者動態(tài) 更多
linux系統(tǒng)監(jiān)控工具小神器:btop
1天前
有沒有權(quán)貴開后門讓子女做軟件開發(fā)人員?
1星期前
一文包你學(xué)會網(wǎng)絡(luò)數(shù)據(jù)抓包
03-15 09:26
C語言初學(xué)者編程水平上不來?不妨嘗試這10個C語言例子
03-14 20:31
Linux rootfs:如何開機就自動添加某個用戶?
03-09 22:18

從概念到代碼,一文了解Linux系統(tǒng)守護進(jìn)程知識

一、概念:

守護進(jìn)程,也就是通常所說的Daemon進(jìn)程,是Linux中的后臺服務(wù)進(jìn)程。周期性的執(zhí)行某種任務(wù)或等待處理某些發(fā)生的事件。

Linux系統(tǒng)有很多守護進(jìn)程,大多數(shù)服務(wù)都是用守護進(jìn)程實現(xiàn)的。比如:像我們的tftp,samba,nfs等相關(guān)服務(wù)。

UNIX的守護進(jìn)程一般都命名為*d的形式,如httpd,telnetd等等。

二、生命周期:

守護進(jìn)程會長時間運行,常常在系統(tǒng)啟動時就開始運行,直到系統(tǒng)關(guān)閉時才終止。

三、守護進(jìn)程不依賴于終端

從終端開始運行的進(jìn)程都會依附于這個終端,這個終端稱為這些進(jìn)程的控制終端。當(dāng)控制終端被關(guān)閉時,相應(yīng)的進(jìn)程都會被自動關(guān)閉。咱們平常寫進(jìn)程時,一個死循環(huán)程序,咱們不知道有ctrl+c的時候,怎么關(guān)閉它呀,是不是關(guān)閉終端呀。也就是說關(guān)閉終端的同時也關(guān)閉了我們的程序,但是對于守護進(jìn)程來說,其生命周期守護需要突破這種限制,它從開始運行,直到整個系統(tǒng)關(guān)閉才會退出,所以守護進(jìn)程不能依賴于終端。

四、查看守護進(jìn)程

ps axj

a: 顯示所有 

x:顯示沒有控制終端的進(jìn)程 

j:顯示與作業(yè)有關(guān)的信息(顯示的列):會話期ID(SID),進(jìn)程組ID(PGID),控制終端(TT),終端進(jìn)程組ID(TRGID)

• 所有的守護進(jìn)程都是以超級用戶啟動的(UID為0);

• 沒有控制終端(TTY為?);

• 終端進(jìn)程組ID為-1(TPGID表示終端進(jìn)程組ID,該值表示與控制終端相關(guān)的前臺進(jìn)程組,如果未和任何終端相關(guān),其值為-1;

• 所有的守護進(jìn)程的父進(jìn)程:

歷史上,Linux 的啟動一直采用init進(jìn)程;下面的命令用來啟動服務(wù)。

這種方法有兩個缺點:
1. 啟動時間長。init進(jìn)程是串行啟動,只有前一個進(jìn)程啟動完,才會啟動下一個進(jìn)程。
2. 啟動腳本復(fù)雜。init進(jìn)程只是執(zhí)行啟動腳本,不管其他事情。腳本需要自己處理各種情況,
這往往使得腳本變得很長。

Systemd 

就是為了解決這些問題而誕生的。它的設(shè)計目標(biāo)是,為系統(tǒng)的啟動和管理提供一套完整的解決方案。

根據(jù) Linux 慣例,字母d是守護進(jìn)程(daemon)的縮寫。Systemd 這個名字的含義,就是它要守護整個系統(tǒng)。

五、進(jìn)程組、會話、控制終端

• 進(jìn)程組

shell里的每個進(jìn)程都屬于一個進(jìn)程組,創(chuàng)建進(jìn)程組的目的是用于簡化向組內(nèi)所有進(jìn)程發(fā)送信號的操作,即如果一個信號是發(fā)給一個進(jìn)程組,則這個組內(nèi)的所有進(jìn)程都會受到該信號【方便管理】。

• PGID進(jìn)程組ID

進(jìn)程組內(nèi)的所有進(jìn)程都有相同的PGID,等于該組組長的PID。(進(jìn)程組組長:進(jìn)程組中有一個進(jìn)程擔(dān)當(dāng)組長。進(jìn)程組ID(PGID)等于進(jìn)程組組長的進(jìn)程ID。已知一個進(jìn)程,要得到該進(jìn)程所屬的進(jìn)程組ID可以調(diào)用getpgrp。一個進(jìn)程可以通過另一個系統(tǒng)調(diào)用setpgrp來加入一個已經(jīng)存在的進(jìn)程組或者創(chuàng)建一個新的進(jìn)程組。

如果內(nèi)核支持_POSIX_JOB_CONTROL(該宏被定義)則內(nèi)核會為Shell 上的每一條命令行(可能由多個命令通過管道等連接)創(chuàng)建一個進(jìn)程組。從這點上看,進(jìn)程組不是進(jìn)程的概念,而是shell上才有,所以在task_struct里并沒有存儲進(jìn)程組id之類的變量。

進(jìn)程組的生命周期到組中最后一個進(jìn)程終止或其加入其他進(jìn)程組(離開本進(jìn)程組)為止。

  • 會話

一般一個用戶登錄后新建一個會話,每個會話也有一個ID來標(biāo)識(SID)。登錄后的第一個進(jìn)程叫做會話領(lǐng)頭進(jìn)程(session leader),通常是一個shell/bash。對于會話領(lǐng)頭進(jìn)程,其PID=SID。

  • 控制終端

一個會話一般會擁有一個控制終端用于執(zhí)行IO操作。會話的領(lǐng)頭進(jìn)程打開一個終端之后, 該終端就成為該會話的控制終端。與控制終端建立連接的會話領(lǐng)頭進(jìn)程也稱為控制進(jìn)程 (controlling process) 。一個會話只能有一個控制終端。

  • 前臺進(jìn)程組

該進(jìn)程組中的進(jìn)程能夠向終端設(shè)備進(jìn)行讀、寫操作的進(jìn)程組。例如登陸shell(例如bash)通過調(diào)用int tcsetpgrp(int fd, pid_t pgrp); 函數(shù)設(shè)置為某個進(jìn)程組pgrp關(guān)聯(lián)終端設(shè)備fd,該函數(shù)執(zhí)行成功后,該進(jìn)程組pgrp成為前臺進(jìn)程組。

  • 后臺進(jìn)程組

該進(jìn)程組中的進(jìn)程只能夠向終端設(shè)備寫。

  • 終端進(jìn)程組ID

每個進(jìn)程還有一個屬性,終端進(jìn)程組ID(TPGID),用來標(biāo)識一個進(jìn)程是否處于一個和終端相關(guān)的進(jìn)程組中。前臺進(jìn)程組中的進(jìn)程的TPGID=PGID,后臺進(jìn)程組的PGID≠TPGID。若該進(jìn)程和任何終端無關(guān),其值為-1。通過比較他們來判斷一個進(jìn)程是屬于前臺進(jìn)程組,還是后臺進(jìn)程組。

  • 進(jìn)程組、對話期和控制終端關(guān)系

進(jìn)程組、對話期和控制終端關(guān)系

  1. 每個會話有且只有一個前臺進(jìn)程組,但會有0個或者多個后臺進(jìn)程組。
  2. 產(chǎn)生在控制終端上的輸入(Input)和信號(Signal)將發(fā)送給會話的前臺進(jìn)程組中的所有進(jìn)程。對于輸出(Output)來說,則是在前臺和后臺共享的,即前臺和后臺的打印輸出都會顯示在屏幕上。
  3. 終端上的連接斷開時 (比如網(wǎng)絡(luò)斷開或 Modem 斷開), 掛起信號將發(fā)送到控制進(jìn)程(controlling process) 。
  4. 一個用戶登錄后創(chuàng)建一個會話。一個會話中只存在一個前臺進(jìn)程組,但可以存在多個后臺進(jìn)程組。第一次登陸后第一個創(chuàng)建的進(jìn)程是shell,也就是會話的領(lǐng)頭進(jìn)程,該領(lǐng)頭進(jìn)程缺省處于一個前臺進(jìn)程組中并打開一個控制終端可以進(jìn)行數(shù)據(jù)的讀寫。當(dāng)在shell里運行一行命令后(不帶&)創(chuàng)建一個新的進(jìn)程組,命令行中如果有多個命令會創(chuàng)建多個進(jìn)程,這些進(jìn)程都處于該新建進(jìn)程組中,shell將該新建的進(jìn)程組設(shè)置為前臺進(jìn)程組并將自己暫時設(shè)置為后臺進(jìn)程組。
  • 舉例

  1. 打開第一個終端執(zhí)行命令:
ping 127.0.0.1 -aq | grep icmp &  // 通過管道將兩個命令串接起來ping –q不顯示timeout信息,將其設(shè)置到后臺并running
  1. 在第一個終端繼續(xù)執(zhí)行命令,在前臺再新建一個進(jìn)程組?!咀⒁鉀]有&】
ping 127.0.0.1 -aq | grep icmp //在前臺再新建一個進(jìn)程組,
  1. 開啟第二個終端并運行
 ps axj | grep pts/0      即過濾只看pts/0里的會話

 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 2109  2111  2111  2111 pts/0     2538 Ss    1000   0:01 bash
 2111  2503  2503  2111 pts/0     2538 S     1000   0:00 ping 127.0.0.1 -aq
 2111  2504  2503  2111 pts/0     2538 S     1000   0:00 grep --color=auto icmp
 2111  2538  2538  2111 pts/0     2538 S+    1000   0:00 ping 127.0.0.2 -aq
 2111  2539  2538  2111 pts/0     2538 S+    1000   0:00 grep --color=auto timeo

• SID都是2111,說明大家都在一個Session里 

• 有三個進(jìn)程組PGID 2111,2503和2538。我們可以看到用|連起來的ping和grep是在一個進(jìn)程組里的。

• 2538這個進(jìn)程組是一個前臺的進(jìn)程組,因為其PGID==TGPID, 2503這個進(jìn)程組是一個后臺進(jìn)程組

  1. 在第一個終端中執(zhí)行Ctrl+C
  2. 在第二個終端里繼續(xù)ps axj | grep pts/0
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND 2109  2111  2111  2111 pts/0     2111 Ss+   1000   0:01 bash 2111  2503  2503  2111 pts/0     2111 S     1000   0:00 ping 127.0.0.1 -aq 2111  2504  2503  2111 pts/0     2111 S     1000   0:00 grep --color=auto icmp

• 2538那個前臺進(jìn)程組的所有進(jìn)程都消失了,說明信號會發(fā)給前臺進(jìn)程組的所有進(jìn)程

 • 2111,即bash所在的那個進(jìn)程組成為了前臺進(jìn)程組。

六、守護進(jìn)程創(chuàng)建流程

守護進(jìn)程創(chuàng)建流程如下:

1. 創(chuàng)建子進(jìn)程,父進(jìn)程退出 2. 在子進(jìn)程中創(chuàng)建新會話 3. 改變當(dāng)前目錄為根目錄 4. 重設(shè)文件權(quán)限掩碼 5. 關(guān)閉文件描述符 

1.創(chuàng)建子進(jìn)程,父進(jìn)程退出

由于守護進(jìn)程是脫離控制終端的,因此,完成第一步后就會在shell終端里造成一程序已經(jīng)運行完畢的假象。之后的所有后續(xù)工作都在子進(jìn)程中完成,而用戶在shell終端里則可以執(zhí)行其他的命令,從而在形式上做到了與控制終端的脫離。

由于父進(jìn)程已經(jīng)先于子進(jìn)程退出,會造成子進(jìn)程沒有父進(jìn)程,從而變成一個孤兒進(jìn)程。在Linux中,每當(dāng)系統(tǒng)發(fā)現(xiàn)一個孤兒進(jìn)程,就會自動由1號進(jìn)程收養(yǎng)。原先的子進(jìn)程就會變成init進(jìn)程的子進(jìn)程。

2. 在子進(jìn)程中創(chuàng)建新會話

setsid()函數(shù)的作用。一個進(jìn)程調(diào)用setsid()函數(shù)后,會發(fā)生如下事件:

• 首先內(nèi)核會創(chuàng)建一個新的會話,并讓該進(jìn)程成為該會話的leader進(jìn)程,• 同時伴隨該session的建立,一個新的進(jìn)程組也會被創(chuàng)建,同時該進(jìn)程成為該進(jìn)程組的組長。• 該進(jìn)程此時還沒有和任何控制終端關(guān)聯(lián)。若需要則要另外調(diào)用tcsetpgrp,前面講前臺進(jìn)程組時介紹過。

調(diào)用setsid()有以下3個作用:

• 讓進(jìn)程擺脫原會話的控制。• 讓進(jìn)程擺脫原進(jìn)程組的控制。• 讓進(jìn)程擺脫原控制終端的控制。

那么,在創(chuàng)建守護進(jìn)程時為什么要調(diào)用setsid()函數(shù)呢?

讀者可以回憶一下創(chuàng)建守護進(jìn)程的第一步,在那里調(diào)用了fork()函數(shù)來創(chuàng)建子進(jìn)程再令父進(jìn)程退出。由于在調(diào)用fork()函數(shù)時,子進(jìn)程全盤復(fù)制了父進(jìn)程的會話期、進(jìn)程組和控制終端等,雖然父進(jìn)程退出了,但原先的會話期、進(jìn)程組和控制終端等并沒有改變,因此,還不是真正意義上的獨立。而setsid()函數(shù)能夠使進(jìn)程完全獨立出來,從而脫離所有其他進(jìn)程和終端的控制。

詳細(xì)見man 2 setsid。

3.改變當(dāng)前目錄為根目

這一步也是必要的步驟。使用fork()創(chuàng)建的子進(jìn)程繼承了父進(jìn)程的當(dāng)前工作目錄。

由于在進(jìn)程運行過程中,當(dāng)前目錄所在的文件系統(tǒng)(如“/mnt/usb”等)是不能卸載的,這對以后的使用會造成諸多的麻煩(如系統(tǒng)由于某種原因要進(jìn)入單用戶模式)。

因此,通常的做法是讓“/”作為守護進(jìn)程的當(dāng)前工作目錄,這樣就可以避免上述問題。當(dāng)然,如有特殊需要,也可以把當(dāng)前工作目錄換成其他的路徑,如/tmp。改變工作目錄的常見函數(shù)是chdir()。

4. 重設(shè)文件權(quán)限掩碼

文件權(quán)限掩碼是指屏蔽掉文件權(quán)限中的對應(yīng)位。

例如,有一個文件權(quán)限掩碼是050,它就屏蔽了文件組擁有者的可讀與可執(zhí)行權(quán)限。由于使用fork()函數(shù)新建的子進(jìn)程繼承了父進(jìn)程的文件權(quán)限掩碼,這就給該子進(jìn)程使用文件帶來了諸多的麻煩。

因此,把文件權(quán)限掩碼設(shè)置為0,可以大大增強該守護進(jìn)程的靈活性。設(shè)置文件權(quán)限掩碼的函數(shù)是umask()。在這里,通常的使用方法為umask(0)。即賦予最大的能力。

5. 關(guān)閉文件描述符

同文件權(quán)限掩碼一樣,用fork()函數(shù)新建的子進(jìn)程會從父進(jìn)程那里繼承一些已經(jīng)打開的文件。這些被打開的文件可能永遠(yuǎn)不會被守護進(jìn)程讀或?qū)?,但它們一樣消耗系統(tǒng)資源,而且可能導(dǎo)致所在的文件系統(tǒng)無法被卸載。

在上面的第(2)步之后,守護進(jìn)程已經(jīng)與所屬的控制終端失去了聯(lián)系,因此,從終端輸入的字符不可能達(dá)到守護進(jìn)程,守護進(jìn)程中用常規(guī)方法(如printf())輸出的字符也不可能在終端上顯示出來。

所以,文件描述符為0、1和2的3個文件(常說的輸入、輸出和報錯這3個文件)已經(jīng)失去了存在的價值,也應(yīng)被關(guān)閉。

七、代碼實現(xiàn)

/*
 關(guān)注一口Linux
*/
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h> 
#include <fcntl.h>
#include <string.h>

int main()
{
 pid_t pid;
 int i, fd;
 char *buf = "This is a Daemon\n";

 pid = fork();
 if (pid < 0) {
  printf("Error fork\n");
  exit(1);
 } 
 
 /* 第一步,父進(jìn)程退出 */
 if (pid > 0) {
  exit(0); 
 }
 /* 第二步 */
 setsid();
 /* 第三步 */  
 chdir("/");  
 /* 第四步 */
 umask(0);
 /* 第五步 */  
 for(i = 0; i < getdtablesize(); i++) 
 {
  close(i);
 }
 
 /* 這時創(chuàng)建完守護進(jìn)程,以下開始正式進(jìn)入守護進(jìn)程實際工作
  * 注意:由于此時守護進(jìn)程完全脫離了控制終端,因此,不能像其他普通進(jìn)程
  * 一樣通過printf或者perror將錯誤信息輸出到控制終端,一種通用的辦
  * 法是使用syslog服務(wù),將程序中的出錯信息輸入到系統(tǒng)日志文件中。
  * 本程序著重演示創(chuàng)建守護進(jìn)程的步驟,暫不演示syslog。
  */
 while(1) {
  if ((fd = open("/tmp/daemon.log", 
    O_CREAT|O_WRONLY|O_APPEND, 0600)) < 0) {
   exit(1);
  }
  write(fd, buf, strlen(buf) + 1);
  close(fd);
  sleep(10);
 }
 
 exit(0);
}

執(zhí)行結(jié)果

由上圖可見:• 守護進(jìn)程./run 的UID為0;• 沒有控制終端(TTY為?);• 終端進(jìn)程組ID為-1;• 守護進(jìn)程的父進(jìn)程為1516,即systemd。

聲明:本內(nèi)容為作者獨立觀點,不代表電子星球立場。未經(jīng)允許不得轉(zhuǎn)載。授權(quán)事宜與稿件投訴,請聯(lián)系:editor@netbroad.com
覺得內(nèi)容不錯的朋友,別忘了一鍵三連哦!
贊 2
收藏 2
關(guān)注 181
成為作者 賺取收益
全部留言
0/200
成為第一個和作者交流的人吧