http://www.cnblogs.com/zhangze/articles/1871973.html
守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务,或等待处理某些发生的事件。守护进程是一种很有用的进程。Linux的大多数服务器就是用守护进程实现的。比如,Internet服务器inetd,Web服务器httpd等。同时,守护进程完成许多系统任务。比如,作业规划进程crond,打印进程lpd等。
守护进程的编程本身并不复杂,复杂的是各种版本的Unix的实现机制不尽相同,造成不同Unix环境下守护进程的编程规则并不一致。这需要读者注意,照搬某些书上的规则(特别是BSD4.3和低版本的System V)到Linux会出现错误的。下面将全面介绍Linux下守护进程的编程要点并给出详细实例。
一. 守护进程及其特性
守护进程最重要的特性是后台运行。在这一点上DOS下的常驻内存程序TSR与之相似。其次,守护进程必须与其运行前的环境隔离开来。这些环境包括未关闭的文件描述符,控制终端,会话和进程组,工作目录以及文件创建掩模UMASK等。这些环境通常是守护进程从执行它的父进程(特别是shell)中继承下来的。最后,守护进程的启动方式有其特殊之处。它可以在Linux系统启动时从启动脚本/etc/rc.d中启动,可以由作业规划进程crond启动,还可以由用户终端(通常是shell)执行。
总之,除了这些特殊性以外,守护进程与普通进程基本上没有什么区别。因此,编写守护进程实际上是把一个普通进程按照上述的守护进程的特性改造成为守护进程。如果读者对进程有比较深入的认识就更容易理解和编程了。
二. 守护进程的编程要点
前面讲过,不同Unix环境下守护进程的编程规则并不一致。所幸的是守护进程的编程原则其实都一样,区别在于具体的实现细节不同。这个原则就是要满足守护进程的特性。同时,Linux是基于Syetem V的SVR4并遵循Posix标准,实现起来与BSD4相比更方便。编程要点如下;
1. 在后台运行
为避免控制终端将Daemon挂起,要放入后台执行。方法是在进程中调用fork,使父进程终止,让Daemon在子进程中后台执行。
if(pid=fork())
exit(0);//是父进程,结束父进程,子进程继续
2. 脱离控制终端,登录会话和进程组
有必要先介绍一下Linux中的进程与控制终端,登录会话和进程组之间的关系:进程属于一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。
控制终端,登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们,使之不受它们的影响。方法是在第1点的基础上,调用setsid()使进程成为会话组长:
setsid();
说明:当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长。setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。
3. 禁止进程重新打开控制终端
现在,进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端:
if(pid=fork())
exit(0);//结束第一子进程,第二子进程继续(第二子进程不再是会话组长)
4. 关闭打开的文件描述符
进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们:
for(i=0;i 关闭打开的文件描述符close(i);>
5. 改变当前工作目录
进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。对于需要转储核心,写运行日志的进程将工作目录改变到特定目录如/tmp
chdir('/')
6. 重设文件创建掩模umask
进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除:umask(0);
7. 处理SIGCHLD信号
处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将 SIGCHLD信号的操作设为SIG_IGN。
signal(SIGCHLD,SIG_IGN);
这样,内核在子进程结束时不会产生僵尸进程。这一点与BSD4不同,BSD4下必须显式等待子进程结束才能释放僵尸进程。
三. 守护进程实例
守护进程实例包括两部分:主程序test.c和初始化程序init.c。主程序每隔一分钟向/tmp目录中的日志test.log报告运行状态。初始化程序中的init_daemon函数负责生成守护进程。读者可以利用init_daemon函数生成自己的守护进程。
1. init.c清单
代码:
#include < unistd.h >
#include < signal.h >
#include < sys/param.h >
#include < sys/types.h >
#include < sys/stat.h >
void init_daemon(void)
{
int pid;
int i;
if( pid = fork() )
exit(0); //是父进程,结束父进程
else if( pid < 0 )
exit(1); //fork失败,退出
//是第一子进程,后台继续执行
setsid(); //第一子进程成为新的会话组长和进程组长
//并与控制终端分离
if( pid = fork() )
exit(0); //是第一子进程,结束第一子进程
else if( pid < 0 )
exit(1); //fork失败,退出
//是第二子进程,继续
//第二子进程不再是会话组长
for( i = 0; i < NOFILE; ++i ) //关闭打开的文件描述符
close(i);
chdir( “/tmp” ); //改变工作目录到/tmp
umask(0);//重设文件创建掩模
return;
}
2. test.c清单
#include < stdio.h >
#include < time.h >
void init_daemon(void); //守护进程初始化函数
int main(int argc, char *argv[])
{
FILE *fp;
time_t t;
init_daemon(); //初始化为Daemon
while(1) //每隔一分钟向test.log报告运行状态
{
sleep(60); //睡眠一分钟
if( (fp=fopen('test.log','a') ) >= 0 )
{
t=time(0);
fprintf(fp,'I'm here at %s\\n',asctime(localtime(&t)) );
fclose(fp);
}
}
}
以上程序在RedHat Linux6.0下编译通过。步骤如下:
编译:gcc -g -o test init.c test.c
执行:./test
查看进程:ps ef
从输出可以发现test守护进程的各种特性满足上面的要求
Linux operating system daemon programming
Daemon is a special running process in the background. It is independent of the control terminal, and it periodically implementes a task or wait for the processing of certain events. Daemon is a very useful process. Most of the server is to use Linux daemon implementation. For example, Internet server, inetd, Web server, httpd and so on. At the same time, many daemons complete the system tasks. For example, the operational planning process, crond, lpd printing process, etc..
Daemon program itself is not complicated and complex is the realization of the various versions of Unix different mechanisms, resulting in various Unix programming environments daemon is not consistent with the rules. This requires readers to pay attention to, copying some of the rules of the book (especially BSD4.3 and low version of System V) to Linux will appear wrong. Full description below daemon programming under Linux and gives detailed examples of key points.
1. Daemon process and its characteristics
Daemon of the most important feature is running in the background. At this point under DOS TSR program TSR similar. Second, the daemon must run with the environmental isolate the former. The environment includes not closed file descriptor, the control terminal, session and process group, the working directory and file creation mask and so on. The environment is often daemon from the implementation of its parent process (in particular the shell) in inherited. Finally, start the daemon way some are different. It can boot Linux from the startup script / etc / rc.d to start, by the operation planning process crond start, but also by the user terminal (usually shell) implementation.
In short, apart from these special other than the ordinary process daemon is basically no difference. Therefore, the preparation of the daemon is actually a normal process in accordance with the above characteristics daemon transformed into daemon. If readers have more in-depth understanding of the process more easily understood and programmed.
2. Daemon process programming point
Earlier mentioned, different Unix programming environment daemon is not consistent with the rules. Fortunately, the daemon programming principle is the same, except that different specific implementation details. This principle is to meet the characteristics of daemons. Meanwhile, Linux is based on the SVR4 Syetem V and follow the Posix standard, to achieve them more convenient than with the BSD4. Programming elements are as follows;
1. In the background.
To avoid the controlling terminal will hang Daemon into the background. Is in the process to terminate the parent process calls fork to make Daemon in the child process executed in the background.
if (pid = fork ())
exit (0); / / is the parent process to end the parent process, child process to continue
2. From the control terminal, the logon session and process group
It is necessary to introduce Linux in the process and control terminal, logon session, and the relationship between process group: the process is a process group, process group number (GID) is the head of the process, the process of No. (PID). Log session can contain multiple process groups. These share a control terminal process group. The process control terminal is usually to create the login terminal.
Control terminal, the logon session and process group is usually inherited from the parent process. Our goal is to get rid of them, so that from their effects. Method is based on point 1, call setsid () to process a session leader:
setsid ();
Description: When the process is a session leader when the setsid () call failed. But the first point has been to ensure the process is not a session leader. setsid () call is successful, the process of becoming a new leader and new process session leader, and with the original log out of session and process group. As the session process of the exclusive control of the terminal, process and control the terminal from the same time.
3. Prohibit the process to re-open the Control Terminal
Now, the process has become a non-terminal of the session leader. But it can re-apply to open a control terminal. Can no longer be a session leader to bring the process to ban the process of re-open the Control Terminal:
if (pid = fork ())
exit (0); / / end of the first child process, the second child process to continue (the second child process is no longer a session leader)
4. Close open file descriptors
Process from the parent process that created it inherited open file descriptors. If not closed, will be a waste of system resources, resulting in the process where the file system can not remove and cause unexpected errors. Close them by the following methods:
for (i = 0; i close the open file descriptor close (i);>
5. To change the current working directory
Process activities, the working directory where the file system can not be unloaded. Generally need to change the working directory to the root directory. The need to dump core, was running log of the process will change the working directory to a specific directory such as / tmpchdir ("/")
6. Reset the file creation mask
Process from the parent process that created it inherited the file creation mask. It may modify the daemon to access files created position. To prevent this, remove the file creation mask: umask (0);
7. SIGCHLD signal handling
SIGCHLD signal handling is not required. But for some of the process, especially the server process often comes at the request of the process of handling requests for generators. If the parent does not wait for the end of the child process, child process will be the zombie process (zombie) to take up system resources. If the child process the parent process to wait for the end of the process will increase the burden on the parent, affect the concurrent performance of the server process. In Linux, you can simply set the operation of the signal SIGCHLD SIG_IGN.
signal (SIGCHLD, SIG_IGN);
In this way, the end of the kernel in the child process does not produce zombie process. This is different with BSD4, BSD4 must explicitly wait for the child under the end of the process to release the zombie process.
3. Daemon instance
Daemon instance consists of two parts: the main program test.c and initialization procedures init.c. Every one minute to the main program / tmp directory to run the log test.log report states. Initialization function is responsible for generating the init_daemon daemon. Readers can use init_daemon functions generate their own daemon.
1. Init.c list
# Include
# Include
# Include
# Include
# Include
void init_daemon (void)
(
int pid;
int i;
if (pid = fork ())
exit (0); / / is the parent process to end the parent process
else if (pid <0)
exit (1); / / fork failed, exit
/ / Is the first child process, the background to continue
setsid ();// first child process to become the new head of the session leader and the process
/ / And the separation and control terminal
if (pid = fork ())
exit (0); / / is the first child process, the end of the first child process
else if (pid <0)
exit (1); / / fork failed, exit
/ / Is the second child process to continue
/ / The second child process is no longer a session leader
for (i = 0; i
close (i);
chdir ("/ tmp ");// change working directory to / tmp
umask (0); / / reset file creation mask
return;
)
2. Test.c list
# Include
# Include
void init_daemon (void); / / daemon initialization function
main ()
(
FILE * fp;
time_t t;
init_daemon ();// initialize Daemon
while (1) / / report every one minute of running to the test.log
(
sleep (60); / / Sleep for a minute
if ((fp = fopen ("test.log", "a"))> = 0)
(
t = time (0);
fprintf (fp, "Im here at% sn", asctime (localtime (& t)));
fclose (fp);
)
)
)
Under these procedures RedHat Linux6.0 compile. Steps are as follows:
Compile: gcc-g-o test init.c test.c
Executive:. / Test
View process: ps-ef
From the output test daemon can be found to meet the various characteristics of the above requirements.