[转]守护进程
[转]守护进程
http://www.cnblogs.com/coder2012/p/3168646.html
守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程常常在系统引导装入时启动,在系统关闭时终止。Linux系统有很多守护进程,大多数服务都是通过守护进程实现的。
守护进程的特点
由于在Linux中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这 个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。但是守护进程却能够突破这种限制,它从被执行开始运转,直到整个系统关闭 时才退出。如果想让某个进程不因为用户或终端或其他地变化而受到影响,那么就必须把这个进程变成一个守护进程。
守护进程的概念类似于我们手机后台运行的程序,我们看不见它,它却一直在运行。在Windows系统下也有很多服务,都是所谓的守护进程。
服务器与守护进程
Linux的大多数服务器就是用守护进程实现的。比如,Internet服务器inetd,Web服务器httpd等。同时,守护进程完成许多系统任务。
正因为服务器的特点,守护进程在服务器开发上就很重要。
守护进程的创建和使用
守护进程的创建主要分为以下几个部分:
- 创建子进程,父进程退出
- 在子进程中创建新会话
- 改变当前目录为根目录
- 重设文件权限掩码
- 关闭文件描述符
- 守护进程的退出
1.创建子进程,父进程退出
由于守护进程是脱离控制终端的,因此会在Shell终端里造成一程序已经运行完毕的假象,也就是后台运行。在Linux中父进程先于子进程退出会造成子进程成为孤儿进程,而每当系统发现一个孤儿进程时,就会自动由1号进程(init)收养它,这样,原先的子进程就会变成init进程的子进程。
if(pid=fork())
exit(0); //是父进程,结束父进程
2.在子进程中创建新会话
由于调用了fork函数时,子进程全盘拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端等并没有改变,因此,这还不是真正意义上的独立开来,而setsid函数能够使进程完全独立出来,从而摆脱其他进程的控制。
setsid函数用于创建一个新的会话,并担任该会话组的组长。调用setsid有下面的3个作用:
- 让进程摆脱原会话的控制
- 让进程摆脱原进程组的控制
- 让进程摆脱原控制终端的控制
其中,会话是一个或多个进程组的集合。通常,一个会话开始于用户登录,终止于用户退出,在此期间该用户运行的所有进程都属于这个会话期。
setsid(); //第一子进程成为新的会话组长和进程组长
3.改变当前目录为根目录
使用fork创建的子进程继承了父进程的当前工作目录。
chdir("/"); //改变工作目录到/
4.重设文件权限掩码
由于使用fork函数新建的子进程继承了父进程的文件权限掩码,它可能修改守护进程所创建程序的存取位。
所以需要把文件权限掩码设置为0,可以大大增强该守护进程的灵活性。
umask(0); //重设文件创建掩模
5.关闭文件描述符
用fork函数新建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。
for(i=0;i<MAXFILE;i++) //关闭打开的文件
close(i);
6.守护进程退出
当用户需要外部停止守护进程运行时,往往会使用 kill命令停止该守护进程。所以,守护进程中需要编码来实现kill发出的signal信号处理,达到进程的正常退出。
signal(SIGCHLD,SIG_IGN);
例子
//daemon.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)
return(1);
setsid(); //第一子进程成为新的会话组长和进程组长
//并与控制终端分离
if(pid=fork())
exit(0); //是第一子进程,结束第一子进程
else if(pid< 0)
exit(1);
//是第二子进程,继续
//第二子进程不再是会话组长
for(i=0;i< NOFILE;++i) //关闭打开的文件描述符
close(i);
chdir("/"); //改变工作目录到/
umask(0); //重设文件创建掩模
return;
}
//test.c
#include <stdio.h>
#include <time.h >
void init_daemon(void);//守护进程初始化函数
main()
{
init_daemon();//初始化为Daemon
while(1){
sleep(时间);
处理函数... ;
}
}
注意,想要关闭后台程序,可以使用"Kill 进程标识符"的命令,查看进程状态可以使用ps -ef。