1. 程式人生 > >Linux守護進程

Linux守護進程

-- file 內部 lur 會話期 pass 完全 影響 規則

轉自:http://wbwk2005.blog.51cto.com/2215231/400260

  在linux或者unix操作系統中在系統的引導的時候會開啟很多服務,這些服務就叫做守護進程。為了增加靈活性,root可以選擇系統開啟的模式,這些模式叫做運行級別,每一種運行級別以一定的方式配置系統。守護進程是脫離於終端並且在後臺運行的進程。守護進程脫離於終端是為了避免進程在執行過程中的信息在任何終端上顯示並且進程也不會被任何終端所產生的終端信息所打斷。

一.守護進程簡介

  守護進程,也就是通常說的Daemon進程,是Linux中的後臺服務進程。它是一個生存期較長的進程,通常獨立於控制終端並且周期性地執行某種任務或等待處理某些發生的事件。守護進程常常在系統引導裝入時啟動,在系統關閉時終止。

Linux系統有很多守護進程,大多數服務都是通過守護進程實現的,同時,守護進程還能完成許多系統任務,例如,作業規劃進程crond、打印進程lqd等(這裏的結尾字母d就是Daemon的意思)。

  由於在Linux中,每一個系統與用戶進行交流的界面稱為終端,每一個從此終端開始運行的進程都會依附於這個終端,這個終端就稱為這些進程的控制終端,當控制終端被關閉時,相應的進程都會自動關閉。但是守護進程卻能夠突破這種限制,它從被執行開始運轉,直到整個系統關閉時才退出。如果想讓某個進程不因為用戶或終端或其他地變化而受到影響,那麽就必須把這個進程變成一個守護進程。

.進程的分類

根據守護進程的啟動和管理方式,可以分為獨立啟動守護進程

超級守護進程兩類。獨立啟動(stand_alone):該類進程啟動後就常駐內存,所以會一直占用系統資源。其最大的優點就是它會一直啟動,當外界有要求時相應速度較快,像httpd等進程;超級守護進程:系統啟動時由一個統一的守護進程xinet來負責管理一些進程,當相應請求到來時需要通過xinet的轉接才可以喚醒被xinet管理的進程。這種進程的優點時最初只有xinet這一守護進程占有系統資源,其他的內部服務並不一直占有系統資源,只有數據包到來時才會被xinet管理員來喚醒。並且我們還可以通過xinet來對它所管理的進程設置一些訪問權限,相當於多了一層管理機制。

  如果用兩個比喻來形容兩類守護進程的話一般會用銀行的業務處理窗口來類比:獨立啟動:銀行裏有一種單服務的窗口,像取錢,存錢等窗口,這些窗口邊上始終會坐著一個人,如果有人來取錢或存錢,可以直接到相應的窗口去辦理,這個處理單一服務的始終存在的人就是獨立啟動的守護進程;超級守護進程:銀行裏還有一種窗口,提供綜合服務,像匯款,轉賬,提款等業務;這種窗口附近也始終坐著一個人(xinet),她可能不提供具體的服務,提供具體服務的人在裏面閑著聊天啊,喝茶啊,但是當有人來匯款時他會大聲喊一句,小王,有人匯款啦,然後裏面管匯款的小王會立馬跑過來幫忙辦完匯款業務。

其他的人繼續聊天,喝茶。這些負責具體業務的人我們就稱之為超級守護進程。當然可能匯款人會有一些規則,可能不能往北京匯款,他就會提早告訴xinet,所以如果有人來匯款想匯往北京的話,管理員就直接告訴他這個我們這裏辦不到的,於是就根本不會去喊匯款員了,相當於提供了一層管理機制。針對這種窗口還存在多線程和單線程的區別:多線程:將所有用戶的要求都提上來,裏面的人都別閑著了,都一起幹活吧;單線程:大家都排好隊了,一個一個來,裏面的人同一時間只有一個人在工作。這裏需要註意的是超級守護進程的管理員xinet也是一個守護進程,只不過它的任務就是傳話,其實這也是一個很具體很艱巨的任務哦。當然每個守護進程都會監聽一個端口(銀行窗口),一些常用守護進程的監聽端口是固定的,像httpd監聽80端口,sshd監聽22端口等;我們可以將其理解為責任制,時候等待,有求必應。具體的端口信息可以通過cat /etc/services來查看。

.守護進程的命名規則和位置

服務進程後面通常會加一個d來表示,想負責http服務的httpd進程cron服務的crond進程,這只不過是一種約定,就想銀行裏的工作人員需要穿特定的制服是一樣的。每個守護進程都會有一個腳本,可以理解成工作內容說明書,還是需要分開來講解。獨立啟動守護進程:放在/etc/init.d/目錄下,當然也包括xinetshell腳本;超級守護進程:按照xinet中腳本的指示,它所管理的守護進程位於/et/xinetd.d/下,獨立啟動的守護進程啟動命令:

# /etc/init.d/syslog start /stop/restart

# service syslog start

其中service命令也只能啟動位於/etc/init.d/目錄先的進程,這由service命令的內容來決定的;裏面一定有case判斷,可以查看/bin/service查看其運行方式;

超級守護進程:xinet要啟動起來,並且相應服務的人員都在,及在/etc/xinet.d/ftp裏開啟了此服務。超級守護進程的管理規則首先通過/etc/xinetd.conf來設置,裏面為一些默認設置,既訪問此窗口的基本要求,像要有身份證啊,啥的,然後每個具體的服務也會有自己的一些社會自,這些設置在/etc/xinet.d/中都有相應的設置文件的。詳述超級守護進程和它的配置文件利用超級守護進程來管理(網絡)服務的最大好處是安全性較高因為超級守護進程可以通過額外的數據分析,來管理誰可以,誰不可以使用某項服務。類似於多了一層防火墻的機制,確保了安全性。掛在xinetd例的服務設置項目寫在/etc/xinetd.conf/etc/xinetd.d/*目錄的任何文件中。

1/etc/xinetd.conf:設置xinetd服務的默認參數文件

# ......

defaults

{

# Please note that you need a log_type line to be able to use log_on_success

# and log_on_failure. The default is the following :

# log_type = SYSLOG daemon info

instances = 60 # 同一服務的同時聯機最多60

log_type = SYSLOG authpriv # 登錄後會記錄到文件中的信息

log_on_success = HOST PID # 成功登錄時,會記錄的信息有哪些

log_on_failure = HOST # 若登錄失敗,記錄的信息是什麽

cps = 25 30 # 同一秒內最大聯機數量為25臺,若超過25臺,則該服務會暫停30

}

includedir /etc/xinetd.d # 在此目錄中,掛靠的服務可以有自己的參數,如果沒有定義,則遵守上面的配置

2/etc/xinetd.d/*:這個目錄裏面的文件都是掛上xinetd的所有服務,如telnetpop3等。

telnet服務為例,它的配置文件如下。

# default: on

# description: The telnet server serves telnet sessions; it uses \

# unencrypted username/password pairs for authentication.

service telnet

{

disable = no # 開關,該服務可以進行開啟

only_from = 192.168.1.0/24 # 僅允許來自網段192.168.1.0/24的連接

no_access = 192.168.1.3

flags = REUSE # 額外的參數使用REUSE

socket_type = stream # 聯機使用的數據包是TCP,如果為dgram,則使用UDP

wait = no # 多線程方式,多個請求同時啟動

user = flagonxia # 啟動者默認為flagonxia

server = /usr/sbin/in.telnetd # 服務的啟動程序文件的完整路徑名

log_on_failure += USERID # 失敗後,syslog記錄的項目

}

.查看系統打開的服務

有幾條常用的命令可以用來查看

# service iptables status #查看相應服務的狀態,用service需要服務在/etc/init.d/目錄中存在

# netstat -tulp #會列出相應的服務及其監聽的端口號等,若加n參數會列出端口號

#chkconfig --list |grep 服務名 #會列出現在當前服務的各種狀態,包括在不同運行級別下的啟情況,分為上線兩部分,上部分是獨立啟動的服務,你會看到xinetd也在,下面部分是有inet管理的超級守護進程,沒有運行級別可分的。

假設有一個服務想iptables服務想讓其在開機啟動時運行,可以通過命令chkconfig來實現,

#chkconfig --level ** iptables on

若想建立自己的服務並讓其在啟動時運行也可以,但這種任務在其腳本的書寫方式上有一定寫法,像myvbird#/bin/bash

#chkconfig: 35 80 70

#第二行的語法為chkconfig: [run_level] [start number] [stop number]

然後在外部命令行中將其加入chkconfig的管理中為:

# chkconfig --add myvbird

# chkconfig --list myvbird

myvbird 0:off 1: off 3: on 4:off 5: on 6:off

總之方式還是挺多的,可具體到網上搜索。

.創建守護進程

1.創建子進程,父進程退出

  這是編寫守護進程的第一步。由於守護進程是脫離控制終端的,因此,完成第一步後就會在Shell終端裏造成一程序已經運行完畢的假象。之後的所有工作都在子進程中完成,而用戶在Shell終端裏則可以執行其他命令,從而在形式上做到了與控制終端的脫離。在Linux中父進程先於子進程退出會造成子進程成為孤兒進程,而每當系統發現一個孤兒進程是,就會自動由1號進程(init)收養它,這樣,原先的子進程就會變成init進程的子進程。

2.在子進程中創建新會話

  這個步驟是創建守護進程中最重要的一步,雖然它的實現非常簡單,但它的意義卻非常重大。在這裏使用的是系統函數setsid,在具體介紹setsid之前,首先要了解兩個概念:進程組和會話期

  進程組:是一個或多個進程的集合。進程組有進程組ID來唯一標識。除了進程號(PID)之外,進程組ID也是一個進程的必備屬性。每個進程組都有一個組長進程,其組長進程的進程號等於進程組ID。且該進程組ID不會因組長進程的退出而受到影響。

  會話周期:會話期是一個或多個進程組的集合。通常,一個會話開始與用戶登錄,終止於用戶退出,在此期間該用戶運行的所有進程都屬於這個會話期。

  接下來就可以具體介紹setsid的相關內容

  setsid函數用於創建一個新的會話,並擔任該會話組的組長。調用setsid有下面的3個作用:讓進程擺脫原會話的控制;讓進程擺脫原進程組的控制;讓進程擺脫原控制終端的控制。那麽,在創建守護進程時為什麽要調用setsid函數呢?由於創建守護進程的第一步調用了fork函數來創建子進程,再將父進程退出。由於在調用了fork函數時,子進程全盤拷貝了父進程的會話期、進程組、控制終端等,雖然父進程退出了,但會話期、進程組、控制終端等並沒有改變,因此,還還不是真正意義上的獨立開來,而setsid函數能夠使進程完全獨立出來,從而擺脫其他進程的控制。

3.改變當前目錄為根目錄

  這一步也是必要的步驟。使用fork創建的子進程繼承了父進程的當前工作目錄。由於在進程運行中,當前目錄所在的文件系統(如“/mnt/usb”)是不能卸載的,這對以後的使用會造成諸多的麻煩(比如系統由於某種原因要進入單用戶模式)。因此,通常的做法是讓"/"作為守護進程的當前工作目錄,這樣就可以避免上述的問題,當然,如有特殊需要,也可以把當前工作目錄換成其他的路徑,如/tmp。改變工作目錄的常見函數式chdir。

4.重設文件權限掩碼

  文件權限掩碼是指屏蔽掉文件權限中的對應位。比如,有個文件權限掩碼是050,它就屏蔽了文件組擁有者的可讀與可執行權限。由於使用fork函數新建的子進程繼承了父進程的文件權限掩碼,這就給該子進程使用文件帶來了諸多的麻煩。因此,把文件權限掩碼設置為0,可以大大增強該守護進程的靈活性。設置文件權限掩碼的函數是umask。在這裏,通常的使用方法為umask(0)。

5.關閉文件描述符

  同文件權限碼一樣,用fork函數新建的子進程會從父進程那裏繼承一些已經打開了的文件。這些被打開的文件可能永遠不會被守護進程讀寫,但它們一樣消耗系統資源,而且可能導致所在的文件系統無法卸下。

 1 #include <unistd.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <stdlib.h>
 5 #include <sys/stat.h>
 6 #include <fcntl.h>
 7 #include <string.h>
 8 #include <stdio.h>
 9 
10 #define MAXFILE 65535
11 int main() {
12     int pid;
13 
14     //create daemon process
15     pid = fork();
16     if (pid > 0)
17         exit(0); //是父進程,結束父進程
18     else if (pid < 0)
19         exit(1); //fork失敗,退出
20 
21     setsid(); //第一子進程成為新的會話組長和進程組,並與控制終端分離
22     chdir("/tmp"); //改變工作目錄到/tmp
23     umask(0); //重設文件創建掩模
24     for (int i = 0; i < MAXFILE; ++i) { //關閉打開的文件描述符
25         close(i);
26     }
27     int fd;
28     char *buf = "this is a  Daemon\n";
29     int len = strlen(buf);
30     while (1) //每隔10s向test.log報告運行狀態
31     {
32         if ((fd = open("test.log", O_CREAT | O_WRONLY | O_APPEND,0600)) < 0) {
33             perror("open");
34             exit(1);
35         }
36         write(fd,buf,len+1);
37         close(fd);
38         sleep(10); //10s
39     }
40     return 0;
41 } 

Linux守護進程