在Linux(以Redhat Linux Enterprise Edition 5.3爲例)下,有時需要編寫Service。Service也是程序,一般隨系統啓動用戶不干預就不退出的程序,可以稱爲Service。Linux下的Service一般稱爲Daemon。
以上是廣義的Service的定義。Linux下的服務一般放在/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的啓動與否。
一個例子
下面是用c++語言寫的一個Service,此Service在/tmp/random文件中,每隔5秒生成一個4位隨機數字。通過g++ -o myrand myrand.cpp編譯。然後把myrand放到/root/bin/文件夾下。
myrand.cpp
/* 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);
}
}
下面是一個腳本,名字是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 -v 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 -v 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 {1}quot;Usage: $0 {start|stop|status}"
exit 1
esac
exit $ret
例子的一些說明
例子中腳本的下面兩行既是給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等,分別用來殺掉服務和查看服務狀態。