《LINUX實操:如何寫SysV服務管理腳本》要點:
本文介紹了LINUX實操:如何寫SysV服務管理腳本,希望對您有用。如果有疑問,可以聯系我們。
本文目次:
1.1 SysV腳本的特性
1.2 SysV腳本要具備的才能
1.3 start函數分析
1.4 stop函數分析
1.5 reload函數分析
1.6 status、restart、force-reload等
1.7 結束語
SysV服務管理腳本和/etc/rc.d/init.d/functions文件中的幾個重要函數(包含daemon
,killproc
,status
以及幾個和pid有關的函數)"關系匪淺".本人已對該文件做了極詳細的分析和說明,參考functions文件詳細分析和說明.
?
SysV作風的服務啟動腳本有以下幾個特性:
# chkconfig
和# description
兩行.chkconfig行定義的是該腳本被chkconfig工具管理時的主要依據,包含開機和關機時的啟動、關閉順序,以及運行在哪些運行級別.description是該腳本的描述性語句.雖然這兩行以"#"開頭,但必不可少.例如,/etc/init.d/httpd腳本的前面幾行內容如下:
#!/bin/bash
#
# httpd Startup script for the Apache HTTP Server
#
# chkconfig: - 85 15
# description: The Apache HTTP Server is an efficient and extensible \
# server implementing the current HTTP standards.
# processname: httpd
# config: /etc/httpd/conf/httpd.conf
# config: /etc/sysconfig/httpd
# pidfile: /var/run/httpd/httpd.pid
#
# Source function library.
. /etc/rc.d/init.d/functions
if [ -f /etc/sysconfig/httpd ]; then # 斷定后再加載
. /etc/sysconfig/httpd
fi
?
要使用腳本管理服務進程,該腳本還要求具備以下才能,且處理邏輯越完善,腳本就越完美.
kill -9
就會出現這種問題.以上并沒有說明,管理多實例服務時的情況.這需要考慮額外的因素,例如程序自身是否支持多實例,支持的話是否應該寫多個服務腳本分別管理各程序,配置文件是否要共享,pid文件是否能共享,搜索pid時如何避免搜索出非自身實例的pid,還要注意分配鎖文件.這樣的腳本寫起來可能并不難,但這些因素必須要考慮.本文暫不介紹多實例的SysV腳本,因為和程序自身關聯性比較強.
有了以上內容,并理解了functions文件中的函數,再看/etc/init.d/下的服務啟動腳本,絕年夜多數都感覺很簡單.因為它們的思路和框架都是一致的.
因為網上以及/etc/init.d/下服務啟動腳本示例太多了,所以本文不單獨寫這類腳本,而是從幾個腳本中抽出比擬經典的部分,分別介紹start,stop,reload和status的寫法.
?
以httpd的服務管理腳本/etc/init.d/httpd為例.
start() {
echo -n $"Starting $prog: "
LANG=$HTTPD_LANG daemon --pidfile=${pidfile} $httpd $OPTIONS
RETVAL=$?
echo
[ $RETVAL = 0 ] && touch ${lockfile}
return $RETVAL
}
函數首先輸出"Starting $prog"信息,再使用daemon啟動"$httpd"法式.
在daemon語句中,"--pidfile"是daemon的參數,該參數為daemon檢測pid文件是否存在,"$httpd"進程是否已在運行.注意,這個"--pidfile"是寫在"$httpd"前面的,表現這是daemon的參數,而非"$httpd"的啟動參數.
檢測完成后,啟動程序.程序的啟動命令從"$httpd"參數開始,"$OPTIONS"是"$httpd"的啟動選項.一般呈現"$OPTIONS"這個字眼,很可能加載了/etc/sysconfig目錄下的同名文件,目的是提供程序啟動參數.
如果啟動成功,則會daemon函數會調用functions中的success函數顯示"[ OK ]",不然會顯示"[ FAILED ]".
最后,如果啟動勝利,則會創建該進程的鎖文件"$lockfile".鎖文件一般都在/var/lock/subsys目錄下.
很多時候,治理的進程也有"--pidfile"類似的選項.例如下面的啟動語句:
daemon --pidfile $pidfile $processname --pidfile=$pidfile
兩個"--pidfile"選項,但他們的作用是紛歧樣的.第一個"--pidfile"是daemon函數的參數,以便daemon能夠檢測該文件中的pid進程是否已在運行.第二個"--pidfile"是"$processname"的啟動參數,啟動時會創建此文件作為pid文件.
再看一個不使用daemon函數治理進程啟動動作的示例.以下是/etc/init.d/sshd中的start函數內容.
start()
{
[ -x $SSHD ] || exit 5
[ -f /etc/ssh/sshd_config ] || exit 6
# Create keys if necessary
if [ "x${AUTOCREATE_SERVER_KEYS}" != xNO ]; then
do_rsa_keygen
if [ "x${AUTOCREATE_SERVER_KEYS}" != xRSAONLY ]; then
do_rsa1_keygen
do_dsa_keygen
fi
fi
echo -n $"Starting $prog: "
$SSHD $OPTIONS && success || failure
RETVAL=$?
[ $RETVAL -eq 0 ] && touch $lockfile
echo
return $RETVAL
}
前面多了一大段,這和服務啟動腳本的框架無關,是程序自身要求的,但作用很簡單.無非便是判斷下程序是否可執行,配置文件是否存在,是否要創建服務端主機驗證階段的密鑰對,也便是/etc/ssh/ssh_host_{rsa,dsa}_key等幾個文件.
再下??才是服務啟動腳本中的通用邏輯部門.輸出一段信息,然后啟動程序,創建鎖文件.但這里沒有使用daemon函數管理,所以這里配合了success和failure函數以便人性化顯示"[ OK ]"或"[ FAILED ]".
?
仍然以/etc/init.d/httpd中的stop函數為例.
# When stopping httpd, a delay (of default 10 second) is required
# before SIGKILLing the httpd parent; this gives enough time for the
# httpd parent to SIGKILL any errant children.
stop() {
status -p ${pidfile} $httpd > /dev/null
if [[ $? = 0 ]]; then
echo -n $"Stopping $prog: "
killproc -p ${pidfile} -d ${STOP_TIMEOUT} $httpd
else
echo -n $"Stopping $prog: "
success
fi
RETVAL=$?
echo
[ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile}
}
前面加了一段注釋,大致意思是說這里殺死進程的行為和httpd自帶的apachectl工具停止服務的命令"apachectl -k stop"的行為是不同的.之所以我要把這一段也貼上來,也就是為了說明這一點.有些服務程序自帶進程管理工具,亦或是使用functions中的函數,完全由我們本身決定.
再看stop函數的邏輯.首先使用"status"函數檢查進程的狀態,如果進程已在運行,則使用killproc函數殺掉它,否則表現進程未運行或進程已死,但pid文件還存在.所以,在最后刪掉pidfile和lockfile.
必要注意的是,killproc殺進程時,能保證pidfile同時被刪除.但它不負責lockfile,而且執行stop之前曾手動執行了"kill -9"殺進程,那么進程雖然已死,但pid文件卻存在.因此也仍需手動rm刪除pidfile.
killproc的調用辦法為:
killproc [-p $pidfile] -[d $delay] $processname [-signal]
它的邏輯和執行進程是這樣的:
kill -15
),然后在給定的延遲時間delay內,每隔一秒檢查一次/proc下是否有對應目錄,如果發現沒有,則表現進程殺死成功,于是刪除pid文件(其實這種情況不用刪,因為TERM信號會自動做收尾動作).但如果delay都超時了,還發現進程存在,則發送KILL信號強制殺死進程,最后刪除pid文件.現在再理解killproc -p ${pidfile} -d ${STOP_TIMEOUT} $httpd
就很簡單了.
再看/etc/init.d/sshd劇本中的stop.
stop()
{
echo -n $"Stopping $prog: "
killproc -p $PID_FILE $SSHD
RETVAL=$?
# if we are in halt or reboot runlevel kill all running sessions
# so the TCP connections are closed cleanly
if [ "x$runlevel" = x0 -o "x$runlevel" = x6 ] ; then
trap '' TERM
killall $prog 2>/dev/null
trap TERM
fi
[ $RETVAL -eq 0 ] && rm -f $lockfile
echo
}
更直接,直接就killproc.但是后面還設置了runlevel的判斷情況,這就屬于法式自身屬性了,和服務管理腳本的邏輯框架無關.
末了再看mysqld中的stop函數.
stop(){
if [ ! -f "$mypidfile" ]; then
# not running; per LSB standards this is "ok"
action $"Stopping $prog: " /bin/true # pid文件都不存在,直接顯示成功
return 0
fi
MYSQLPID=`cat "$mypidfile" 2>/dev/null` # 讀取pidfile中的pid號
if [ -n "$MYSQLPID" ]; then # 如果pid不為空,則
/bin/kill "$MYSQLPID" >/dev/null 2>&1 # 先發送默認的TERM信號殺一次
ret=$?
if [ $ret -eq 0 ]; then # 如果殺成功了,則執行下面一段.
# 否則直接失敗,但這不可能.為了邏輯完整,后面仍寫了else
TIMEOUT="$STOPTIMEOUT"
while [ $TIMEOUT -gt 0 ]; do # 在延遲時間內,每隔1秒殺一次
/bin/kill -0 "$MYSQLPID" >/dev/null 2>&1 || break
sleep 1
let TIMEOUT=${TIMEOUT}-1
done
if [ $TIMEOUT -eq 0 ]; then # 如果達到延遲時間界限,則返回殺死進程超時信息
echo "Timeout error occurred trying to stop MySQL Daemon."
ret=1
action $"Stopping $prog: " /bin/false
else # 否則進程殺死成功,刪除pidfile和lockfile
rm -f $lockfile
rm -f "$socketfile"
action $"Stopping $prog: " /bin/true
fi
else
action $"Stopping $prog: " /bin/false
fi
else # 如果pid為空,則表示未成功讀取pidfile.
# failed to read pidfile, probably insufficient permissions
action $"Stopping $prog: " /bin/false
ret=4
fi
return $ret
}
雖然有點長,但有了前面SysV腳本要具備的能力的概念,stop函數的邏輯都一樣好簡單.
?
關于reload函數,主要有兩點:(1).語法檢查;(2).發送HUP信號給"master"進程.其中語法檢查要程序自身能支持,例如httpd -t
,nginx -t
.
以下是/etc/init.d/{httpd,nginx}兩個劇本中的reload函數.
## reload() in /etc/rc.d/init.d/httpd
reload() {
echo -n $"Reloading $prog: "
if ! LANG=$HTTPD_LANG $httpd $OPTIONS -t >&/dev/null; then # 語法檢查
RETVAL=6
echo $"not reloading due to configuration syntax error"
failure $"not reloading $httpd due to configuration syntax error"
else
# Force LSB behaviour from killproc # 語法檢查通過,發送HUP信號
LSB=1 killproc -p ${pidfile} $httpd -HUP
RETVAL=$?
if [ $RETVAL -eq 7 ]; then # 注意reload失敗時退出狀態碼為7
failure $"httpd shutdown"
fi
fi
echo
}
## reload() in /etc/rc.d/init.d/nginx
reload() {
configtest_q || return 6 # 語法檢查
echo -n $"Reloading $prog: "
killproc -p $pidfile $prog -HUP # 發送HUP信號
echo
}
configtest_q() {
$nginx -t -q -c $NGINX_CONF_FILE
}
case "$1" in
reload)
rh_status_q || exit 7 # reload失敗時,退出狀態碼7
$1
;;
唯一必要注意的是,reload失敗時,退出狀態碼為7.這大概已經約定俗成了吧.
再看/etc/init.d/sshd中的reload.
reload()
{
echo -n $"Reloading $prog: "
killproc -p $PID_FILE $SSHD -HUP
RETVAL=$?
echo
}
case "$1" in
reload)
rh_status_q || exit 7
reload
;;
故意思的是mysqld的reload.它直接退出不做任何動作.
case "$1" in
reload)
exit 3
;;
如果不使用killproc函數,而是使用kill敕令,那么應該找出"master" pid.可以使用functions中的pidofproc函數.例如:
pid=$(pidofprco -p pidfile $processname)
action "Reloading $prog: " kill -HUP $pid
?
status -p "$pidfile" $prog
.?
其實SysV服務啟動腳本大多都很簡單,至少它們的邏輯幾乎都一樣.在了解了functions中的幾個函數后,再把腳本的各參數(如start、stop)應該要具備的才能搞搞清楚,這類腳本完全是小菜一兩碟.
本文永遠更新鏈接地址:
更多LINUX教程,盡在維易PHP學院專欄。歡迎交流!
轉載請注明本頁網址:
http://www.snjht.com/jiaocheng/6691.html