http://shahmirj.com/blog/the-initd-scriptphp
http://nixcraft.com/showthread.php/13985-script-as-daemonhtml
http://blog.n01se.net/blog-n01se-net-p-145.htmlmysql
This is part 2 of my first tutorial on how to create a daemon script. I have been meaning to do this for a long time, so here it is. When I was starting out in Linux and trying to understand the whole process about daemons and how they work I was really overwhelmed and the main mistake I made was, that I thought the init.d script was some humounges language that I had to learn. This is not the case. Infact an init.d script is nothing but bash program which controls the following:linux
starting a process if its not started,ios
stopping it if it is startedc++
stopping then starting the script, in other words restartingsql
Please be aware, that if you have never programmed using bash, It would be a good idea to brush up some common knowledge before carrying any further. Google should be more than sufficient.shell
Before starting I think its only right to begin with a full init.d script, To show the different parts which can be explained later in details. So without further ado, I introduce our Script:bash
#!/bin/bash # # Daemon Name: myprogd # # chkconfig: - 58 74 # description: Our Script# Source function library. 裏面會包含daemonize的函數daemon . /etc/init.d/functions #在當前shell環境中source functions# Source networking configuration. . /etc/sysconfig/myconfig prog=myprogd lockfile=/var/lock/subsys/$prog start() { #Make some checks for requirements before continuing [ "$NETWORKING" = "no" ] && exit 1 [ -x /usr/sbin/$prog ] || exit 5 # Start our daemon daemon echo -n $"Starting $prog: " daemon --pidfile /var/run/${proc}.pid $prog RETVAL=$? echo #If all is well touch the lock file [ $RETVAL -eq 0 ] && touch $lockfile return $RETVAL } stop() { echo -n $"Shutting down $prog: " killproc $prog RETVAL=$? echo #If all is well remove the lockfile [ $RETVAL -eq 0 ] && rm -f $lockfile return $RETVAL } # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status $prog ;; restart) stop start ;; *) echo $"Usage: $0 {start|stop|status|restart}" exit 2 esac
Scarry hunh? Well lets break it down. There are 6 main parts that this init script controls:less
The shell deceleration !#/bin/bash
BEGIN INIT INFO - This is NOT a comment, this is actual deceleration. Took me a long while to recognize this
Include functions - . /etc/init.d/functions
and . /etc/sysconfig/myconfig
(Optional)
Variable deceleration - proc
and lockfile
Local Function - start()
and stop
case
to handle actions
#!/bin/bash # # Daemon Name: myprogd # # chkconfig: - 58 # description: A script that does nothing but run a sleep process for testing # requires: mysqld
This is the header that tells the shell what to use when running this script. The comment here are just for visual purposes but very important. Especially if you are planning on open-sourcing your script. You can choose to put more information here and be more descriptive, but i believe the headings above (Deamon Name, chkconfig, description and requires) are the most important ones, and should always be filled in.
init.d comes with a certian set of functions that help you control a daemon without going through the headache creating a processflow to control the starting and stopping of scripts. This file(/etc/init.d/functions) includes three major functions that we will be interested in daemon
, killproc
and status
This function starts your process and creates the PID file. The PID file stores the process-id value given to a each and every process by your kernel. You can view the current running processes and their PIDs by running ps -ef
.
The daemon function can take an optional parameter --pidfile
, This is the location and the name of the PID file that the daemon will create. If the parameter is omitted by default it will create a pidfile in /var/run/$1.pid
where $1
is the program name. The variable lets you define the PID-file name yourself should you choose to
A PID file is a file that contains simply the process id, If you look inside the /var/run/
folder on your linux machine you will see many files that store a number inside them. This is used by the init script to kill that specific Process with that id. When you start your process up, the script will create a PID file with its process ID.
For our script we are going to define a custom PID file so we can see how its used in other script
To kill any process on Linux you use the kill
command, which takes a Process ID as a parameter. This function performs a similar task, however it uses the PID file to retrieve the Process ID, this is why you can stop a daemon without giving it a PID file to kill. You can see how the PID file is now becoming more and more important.
Once we perform our kill we also need to remove the PID file as the Process ID is now no longer in use. This functions also deals with the cleanup of the PID file so a new one can be generated next time around. From a PID file prespective, daemon()
function creates it and the killproc()
function deletes it.
The status function just looks at the PID file and checks its running status.
I would like to point out all of the above function leave out other major functionality, this is done so someone starting out can understand the basics. I do recommend you look at the functions manually and work your way through them to understand all the different options.
To include these function we use
# Source function library. . /etc/init.d/functions
start() { #Make some checks for requirements before continuing [ "$NETWORKING" = "no" ] && exit 1 [ -x /usr/sbin/$prog ] || exit 5 # Start our daemon daemon echo -n $"Starting $prog: " daemon --pidfile /var/run/${proc}.pid $prog RETVAL=$? echo #If all is well touch the lock file [ $RETVAL -eq 0 ] && touch $lockfile return $RETVAL }
This functions starts of with checking weather the variable $NETWORK
is set to "no", if this is the case we will exit out of our starting procedures as we would like the networking to be on, before we go any where with our script. We don't actually require networking for our script, but I have left that in there just to show you how you can control dependencies for other environment in your system.
Where does the $NETWORK
variable come from you ask? Well this is included and defined using the include just after the functions call.
# Source networking configuration. . /etc/sysconfig/myconfig
If you look inside the file /etc/sysconfig/myconfig
, you can see the following
NETWORKING_IPV6=no HOSTNAME=local.shahmirj.com NETWORKING=yes GATEWAY=192.168.1.1
The next line [ -x /usr/sbin/$prog ] || exit 5
checks to see weather or not the actual daemon file exist for us to run or not. There is no point continuing further if the script we are trying to daemon-ize does not exist. You may need to change this according to where your script is located.
Continuing on, we see the echo
to show some output of the script starting followed by a call to daemon --pidfile /var/run/${proc}.pid $prog
. This call is just passing the actual starting of the script to our daemon function as explained above. Note our pidfile parameter call here is useless, because if left out it will create the exact same PID file as the parameter. I have left the pidfile parameter just for demonstration purposes.
After this call we use a $RETVAL
variable to store the return code of the $prog
. Which is relevant to what you set your daemon to return back to the shell inside your main function. Note that 0
is always the return code for success, this can be confusing but just try to remember this. This is also the reason why in many c++ examples you see main()
returning 0
at the end.
This is also another reason why we fork()
our script, because our child keeps on living where our parent process returns the exit code.
If we success in running your script, we touch a $lockfile. A lockfile is similar to a PID file however it serves a different purpose. A Lockfile in our context ensures only one script is running at one time. This prevents someone trying to start the script up twice. This is your responsibility as there may be certain circumstances where you want to keep starting new scripts. So after we succeed in running your script, we touch
a lockfile. On a plus side this lock file can also tell us when your process started, by looking at the date the file was modified.
stop() { echo -n $"Shutting down $prog: " killproc $prog RETVAL=$? echo #If all is well remove the lockfile [ $RETVAL -eq 0 ] && rm -f $lockfile return $RETVAL }
This is more or less the negative of the start function. Instead of starting the process it kills the existing process using a call to killproc
and if the killing of the process succeds it removes the lock file.
# See how we were called. case "$1" in start) start ;; stop) stop ;; status) status $prog ;; restart) stop start ;; *) echo $"Usage: $0 {start|stop|status|restart}" exit 2 esac
This call just handles which function to run for the right call. For example
> /etc/init.d/myprogd start > /etc/init.d/myprogd stop > /etc/init.d/myprogd restart > /etc/init.d/myprogd status
Thats all there is to it folks, Leave a comment if you want me to elaborate more on a specific part that you may be lost at. Otherwise go forth and daemon-ize.
=======================================================
A lock file is used with linux daemons to prevent the script from running multiple instances of itself.
Let me Explain the following code block........
Code:
if [ ! -e $lockfile ]; then touch $lockfile critical-section rm $lockfile else echo "critical-section is already running" fi
The if statement uses the -e (man test) file test operator to check if the variable $lockfile exists. If the lockfile is in place the script will NOT!!! run again. Notice the rm $ lockfile at the end of the code. Once your critical-section or script finishes, it will clean up after itself by removing the lock file.
Lets have a look at NRPE daemon. Nagios uses this daemon to run commands on remote servers. You will see the locking mechanism getting called. Most daemons will use the path /var/lock/subsys to write and delete lockfiles.Focus on the red sections.
Code:
NrpeBin=/usr/local/nagios/nrpe NrpeCfg=/etc/nagios/nrpe.cfg ## Define Lock file # See how we were called. case "$1" in start) # Start daemons. echo -n "Starting nrpe: " $NrpeBin -c $NrpeCfg -d echo ## create the file when started ;; stop) # Stop daemons. echo -n "Shutting down nrpe: " nrpe echo ## remove the file when stopped. ;; restart) $0 stop $0 start ;; status) status nrpe ;; *) echo "Usage: nrpe {start|stop|restart|status}" exit 1 esac exit 0 ===================================================================== 如何能不讓腳本重複執行的問題,實際就是文件鎖的概念
在SHELL中實現文件鎖,有兩種簡單的方式。
一是利用普通文件,在腳本啓動時檢查特定文件是否存在,若是存在,則等待一段時間後繼續檢查,直到文件不存時建立該文件,在腳本結束時刪除文件。爲確保腳本在異常退出時文件仍然能被刪除,能夠藉助於trap "cmd" EXIT TERM INT
命令。通常這類文件存放在/var/lock/
目錄下,操做系統在啓動時會對該目錄作清理。
另外一種方法是是使用flock
命令。使用方式以下,這個命令的好處是等待動做在flock命令中完成,無需另外添加代碼。
( flock 300 ...cmd... flock -u 300 ) > /tmp/file.lock
但flock有個缺陷是,在打開flock以後fork(),子進程也會擁有鎖,若是在flock其間有運行daemon的話,必需確保daemon在啓動時已經關閉了全部的文件句柄,否則該文件會由於daemon一直將其置於打開狀態而沒法解鎖。
/var/lock/subsys目錄的做用的說明
不少程序須要判斷是否當前已經有一個實例在運行,這個目錄就是讓程序判斷是否有實例運行的標誌,好比說xinetd,若是存在這個文件,表示已經有xinetd在運行了,不然就是沒有,固然程序裏面還要有相應的判斷措施來真正肯定是否有實例在運行。
一般與該目錄配套的還有/var/run目錄,用來存放對應實例的PID,若是你寫腳本的話,會發現這2個目錄結合起來能夠很方便的判斷出許多服務是否在運行,運行的相關信息等等。
實際上,判斷是否上鎖就是判斷這個文件,因此文件存在與否也就隱含了是否上鎖。 而這個目錄的內容並不能表示必定上鎖了,由於不少服務在啓動腳本里用touch來建立這個加鎖文件,在系統結束時該腳本負責清除鎖,這自己就不可靠(好比 意外失敗致使鎖文件仍然存在),我在腳本里通常是結合PID文件(若是有PID文件的話),從PID文件裏獲得該實例的PID,而後用ps測試是否存在該 PID,從而判斷是否真正有這個實例在運行,更加穩妥的方法是用進程通信了,不過這樣的話單單靠腳本就作不到了。
================================
To run it as a full daemon from a shell, you'll need to use setsid
and redirect its output. You can redirect the output to a logfile, or to /dev/null
to discard it. Assuming your script is called myscript.sh, use the following command:
setsid myscript.sh >/dev/null 2>&1 < /dev/null &
This will completely detach the process from your current shell (stdin, stdout and stderr). If you want to keep the output in a logfile, replace the first /dev/null
with your /path/to/logfile.
You have to redirect the output, otherwise it will not run as a true daemon (it will depend on your shell to read and write output).
Also look into creating a lock file mechanism to check that the script is not running on top of itself.
Here is basic example of what I use when implementing lock files for daemons.
Code:
if [ ! -e $lockfile ]; then touch $lockfile critical-section rm $lockfile else echo "critical-section is already running" fi
Or a more robust lockfile check.
Code:
if [ ! -e $lockfile ]; then touch $lockfile critical-section rm $lockfileelse echo "critical-section is already running" fi ====================================================================== http://alsww.blog.51cto.com/2001924/1113112
1. fork ( /directory/script.sh) :若是shell中包含執行命令,那麼子命令並不影響父級的命令,在子命令執行完後再執行父級命令。子級的環境變量不會影響到父級。
fork是最普通的, 就是直接在腳本里面用/directory/script.sh來調用script.sh這個腳本.
運行的時候開一個sub-shell執行調用的腳本,sub-shell執行的時候, parent-shell還在。
sub-shell執行完畢後返回parent-shell. sub-shell從parent-shell繼承環境變量.可是sub-shell中的環境變量不會帶回parent-shell
2. exec (exec /directory/script.sh):執行子級的命令後,再也不執行父級命令。
exec與fork不一樣,不須要新開一個sub-shell來執行被調用的腳本. 被調用的腳本與父腳本在同一個shell內執行。可是使用exec調用一個新腳本之後, 父腳本中exec行以後的內容就不會再執行了。這是exec和source的區別
3. source (source /directory/script.sh):執行子級命令後繼續執行父級命令,同時子級設置的環境變量會影響到父級的環境變量。
與fork的區別是不新開一個sub-shell來執行被調用的腳本,而是在同一個shell中執行. 因此被調用的腳本中聲明的變量和環境變量, 均可以在主腳本中獲得和使用.
轉自:
https://www.cnblogs.com/my_life/articles/4323525.html