【網摘】在Linux下編寫Daemon

在Linux下編寫Daemon

轉自:http://blog.163.com/prevBlogPerma.do?host=manyhappy163&srl=1644768312010718111142260&mode=prev

在Linux(以Redhat Linux Enterprise Edition 5.3爲例)下,有時需要編寫Service。Service也是程序,一般隨系統啓動用戶不干預就不退出的程序,可以稱爲Service。Linux下的Service一般稱爲Daemon。

以上是廣義的Service的定義。Linux下的Service一般放在/etc/init.d文件夾下。瀏覽一下這個文件夾下的文件,可以發現在Linux下編寫Service一般遵循的原則。

 

一 Linux下編寫Service一般遵循的原則
1)真正運行的Service一般放在某個bin目錄下(/bin,/usr/bin,etc)。
2)/etc/init.d文件夾下一般是shell腳本,用來控制bin目錄下的Service。
3)/etc/init.d文件夾下的shell腳本一般接受至少兩個參數,start和stop。還有其他常用的可選參數如status,reload,restart,等。
4)/etc/init.d文件夾下的shell腳本至少包括兩行註釋,一行告訴chkconfig此服務運行的runlevel,啓動優先級,結束優先級。一行告訴chkconfig此服務的描述。
 

二 Linux的啓動過程和RunLevel
要理解Linux的啓動過程和RunLevel,可以先瀏覽一下/etc/inittab文件。在/etc/inittab中定義了下面7種RunLevel。每個Service可以設置自己在哪個RunLevel下運行。可以調用/sbin/init <runlevel>進入相應的RunLevel,比如運行/sbin/init 6就會導致系統重啓。如果在某個RunLevel下某個服務不能啓動,導致系統啓動失敗,可以進入沒有配置此服務的RunLevel來禁用或修改此服務(有點類似Windows下的安全模式)。

#   0 - halt (Do NOT set initdefault to this)
#   1 - Single user mode
#   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
#   3 - Full multiuser mode
#   4 - unused
#   5 - X11
#   6 - reboot (Do NOT set initdefault to this)


/etc/inittab文件下還定義了缺省RunLevel。如下,代表缺省RunLevel是5。
id:5:initdefault:
 

在/etc文件夾下,執行ls -d rc*,會列出下面這些文件和目錄:
rc  rc0.d  rc1.d  rc2.d  rc3.d  rc4.d  rc5.d  rc6.d  rc.d  rc.local  rc.sysinit
rc是一個腳本,在/etc/inittab中,會根據RunLevel執行rc <runlevel>。rc腳本會到相應的rcN.d中去執行下面的腳本。rc.local是最後調用的腳本,可以放一些用戶自定義的任務在裏面。

進入rcN.d文件夾下,會發現以S和K開頭的腳本的鏈接,S和K後面還帶2位數字。其中S代表Start,K代表Kill。S開頭的腳本,在rc中調用的時候會帶start參數;K開頭的腳本,在rc中調用的時候會帶stop參數。S和K後面帶的2位數字代表Service的優先級,數字越大,越後執行。這些腳本的鏈接的目的地多半都在/etc/init.d文件夾下。

現在一切都清晰了。我們可以通過在相應的rcN.d文件夾下按既定的規範創建/etc/init.d下腳本的軟鏈接的方式來控制系統啓動和退出時服務的啓動和結束。但是用手動的方式創建軟鏈接來管理畢竟不方便,RedHatLinux提供了chkconfig來幫助創建這些軟鏈接。只要放在/etc/init.d下的服務控制腳本符合前面提到的chkconfig的約定(註釋chkconfig 和 description),就可以用chkconfig --add <service>  chkconfig --list <service> chkconfig --del <service>等命令來控制service的啓動與否。
 

三 一個例子
1)下面是用c++語言寫的一個Service,此Service在/tmp/random文件中,每隔5秒生成一個4位隨機數字。通過g++ -o myrand myrand.cpp編譯。然後把myrand放到/root/bin/文件夾下。

/* myrand.cpp
 * this program read 4 chars from /dev/random in each iteration,
 * and then adjust it to 0-9. Finally the 4 chars are written
 * to /tmp/random. This is only for testing /dev/random, and
 * at the same time serve as a example service.
 
*/
#include 
<iostream>
#include 
<fstream>
using namespace std;
#include 
<unistd.h> 

int main()
{
    
while (true)
    {
                    ifstream ifile(
"/dev/random");
                    
char ch;
                    
char str[5];
                    str[
4]=0;
                    
int i;
                    
for(i=0;i<4;i++)
                    {
                                    ifile 
>> ch;
                                    
if(ch<0) ch=-ch;
                                    ch 
= ch % 10;
                                    ch
='0' + ch;
                                    str[i]
=ch;
                    }


                    ofstream ofile(
"/tmp/random");
                    ofile 
<< str << endl;
                    sleep(
5);
    }
}

 

 

2) 下面是一個腳本,名字是myrandservice,放在/etc/init.d文件夾下:

#!/bin/sh
#
# chkconfig: 2345  80 50
# description: myrandservice is for testing how to write service in Linux 
#              
# processname: myrandservice

# Source function library.

. /etc/rc.d/init.d/functions

ret
=0

start() {
    
# check fdb status
                echo "start myrandservice"
                daemon 
/root/bin/myrand &
                ret
=$?


stop() {
    echo 
"stop myrandservice"
                
kill -9 $(ps -ef | grep myrand | grep -grep | awk '{print $2}')
                ret
=$?


status() {
                
local result
                echo 
"check status of myrandservice..."
                
#lines=$( ps -ef | grep myrand | grep -v grep  |  )
                #echo $lines

                result=$( ps -ef | grep myrand | grep -v myrandservice | grep -grep | wc -l )
                
#echo $result
                if [ $result -gt 0 ] ; then
                                echo 
"my randservice is up"
                                ret
=0
                
else
                                echo 
"my randservice is down"
                                ret
=1
                fi
                echo 
"check status of myrandservice...done."


# See how we were called.
case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  status)
        status 
        ;;
  
*)
        echo $
"Usage: $0 {start|stop|status}"
        
exit 1
esac

exit $ret

 

3)使用/sbin/chkconfig --add myrandservice 來將次daemon設置爲自動啓動。 同時可以在rc3.d,rc4.d,rc5.d下面看到相應的腳本鏈接被自動地創建了。

 

4)例子的一些說明
例子中腳本的下面兩行既是給chkconfig用的。其中2345代表此服務在RunLevel 2, 3, 4, 5下開啓;80代表啓動優先級;50代表結束優先級。如果RunLevel處不添值,用“-”代替,則代表此服務在任何runlevel下都不會自動啓動,需要手動啓動。可以通過service <service-name> start/stop/status等來控制或查詢Service。

# chkconfig: 2345  80 50
# description: myrandservice is for testing how to write service in Linux  

腳本中的daemon函數存在於/etc/rc.d/init.d/functions中。daemon會重定向輸出到/dev/null,也會設置是否生成coredump文件。通過daemon啓動的程序,即使用戶退出了命令行shell,也會保證Service會運行而不會退出。在/etc/rc.d/init.d/functions中還包括其他一些有用的函數,如killproc,status等,分別用來殺掉服務和查看服務狀態。

 

四 deamon的另一種實現

不使用/etc/rc.d/init.d/functions中的daemon,則Daemon程序設計要遵從以下過程:

(1)       程序運行後調用fork,並讓父進程退出。子進程獲得一個新的進程ID,但繼承了父進程的進程組ID

(2)       調用setsid創建一個新的session,使自己成爲新session和新進程組的leader,並使進程沒有控制終端(tty)

(3)       設置文件創建mask0,避免創建文件時權限的影響。

(4)       關閉不需要的打開文件描述符。因爲Daemon程序在後臺執行,不需要於終端交互,通常就關閉STDINSTDOUTSTDERR。其它根據實際情況處理。

(5)       Daemon無法輸出信息,可以使用SYSLOG或自己的日誌系統進行日誌處理。(可選)

(6)       編寫管理DaemonSHELL腳本,使用serviceDaemon進行管理和監控。(可選)

 

參考:

http://www.cnblogs.com/khler/archive/2011/01/30/1947971.html


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章