创建守护进程的一般步骤:
(1) 创建子进程,退出父进程
为了脱离控制终端需要退出父进程,之后的工作都由子进程完成。在Linux中父进程先于子进程退出会造成子进程成为孤儿进程,而每当系统发现一个孤儿进程时,就会自动由1号进程(init)收养它,这样,原先的子进程就会变成init进程的子进程。
ps –ef | grep ProcName 通过PID/PPID查看进程的父子关系
(2) 在子进程中创建新的会话
使用系统函数setsid来完成。
man 2 setsid 查看关于setsid函数的说明
setsid – creates a session and sets theprocess group ID
#include <unistd.h>
pid_t setsid(void);
setsid() creates a new session if thecalling process is not a process
group leader. The calling process is theleader of the new session, the
process group leader of the new process group,and has no controlling
tty. The process group ID and session ID of
the callingprocess are set to the PID of the calling process. The
calling process will bethe only process in this new process group and in
this new session.
进程组:是一个或多个进程的集合。进程组有进程组ID来唯一标识。除了进程号PID之外,进程组ID也是一个进程的必备属性。每个进程组都有一个组长进程,其组长进程的进程号等于进程组ID,且该进程组ID不会因组长进程的退出而受到影响。
setsid函数作用:用于创建一个新的会话,并担任该会话组的组长。调用setsid有3个作用
(a) 让进程摆脱原会话的控制;
(b) 让进程摆脱原进程组的控制;
(c) 让进程摆脱原控制终端的控制;
使用setsid函数的目的:由于创建守护进程的第一步调用了fork函数来创建子进程再将父进程退出。由于在调用fork函数时,
子进程拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端等并没有改变,因此,这还不是真正意义上的独立开了。使
用setsid函数后,能够使进程完全独立出来,从而摆脱其他进程的控制。
(3) 改变当前目录为根目录
使用fork创建的子进程继承了父进程的当前的工作目录。由于在进程运行中,当前目录所在的文件系统是不能卸载的,这对以后的使用会造成诸多的麻
烦。因此,通常的做法是让根目录”/”作为守护进程的当前工作目录。这样就可以避免上述的问题。如有特殊的需求,也可以把当前工作目录换成其他的路径。改
变工作目录的方法是使用chdir函数。
(4) 重设文件权限掩码
文件权限掩码:是指屏蔽掉文件权限中的对应位。例如,有个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限(对应二进制
为,rwx,
101)。由于fork函数创建的子进程继承了父进程的文件权限掩码,这就给子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置为0(即,不屏蔽
任何权限),可以增强该守护进程的灵活性。设置文件权限掩码的函数是umask。通常的使用方法为umask(0)。
(5) 关闭文件描述符
用fork创建的子进程也会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读写,但它们一样消耗系统资源,而且可
能导致所在的文件系统无法卸载。在使用setsid调用之后,守护进程已经与所属的控制终端失去了联系,因此从终端输入的字符不可能达到守护进程,守护进
程中用常规方法(如printf)输出的字符也不可能在终端上显示出来。所以,文件描述符为0、1、2(即,标准输入、标准输出、标准错误输出)的三个文
件已经失去了存在的价值,也应该关闭。
(6) 守护进程退出处理
当用户需要外部停止守护进程时,通常使用kill命令停止该守护进程。所以,守护进程中需要编码来实现kill发出的signal信号处理,达到进程正常退出。
下面是一个简单的实现:
-
#include<stdio.h>
-
#include<stdlib.h>
-
#include<string.h>
-
#include<fcntl.h>// open
-
#include<sys/types.h>
-
#include<sys/stat.h>
-
#include<unistd.h>
-
#include<sys/wait.h>
-
#include<signal.h>
-
-
#define MAXFILE 65535
-
-
volatile sig_atomic_t _running = 1;
-
int fd;
-
-
// signal handler
-
void sigterm_handler(int arg)
-
{
-
_running = 0;
-
}
-
-
int main()
-
{
-
pid_t pid;
-
char *buf = "This is a Daemon, wcdj\n";
-
-
/* 屏蔽一些有关控制终端操作的信号
-
* 防止在守护进程没有正常运转起来时,因控制终端受到干扰退出或挂起
-
* */
-
signal(SIGINT, SIG_IGN);// 终端中断
-
signal(SIGHUP, SIG_IGN);// 连接挂断
-
signal(SIGQUIT, SIG_IGN);// 终端退出
-
signal(SIGPIPE, SIG_IGN);// 向无读进程的管道写数据
-
signal(SIGTTOU, SIG_IGN);// 后台程序尝试写操作
-
signal(SIGTTIN, SIG_IGN);// 后台程序尝试读操作
-
signal(SIGTERM, SIG_IGN);// 终止
-
-
// test
-
//sleep(20);// try cmd: ./test &; kill -s SIGTERM PID
-
-
-
// [1] fork child process and exit father process
-
pid = fork();
-
if(pid < 0)
-
{
-
perror("fork error!");
-
exit(1);
-
}
-
else if(pid > 0)
-
{
-
exit(0);
-
}
-
-
// [2] create a new session
-
setsid();
-
-
// [3] set current path
-
char szPath[1024];
-
if(getcwd(szPath, sizeof(szPath)) == NULL)
-
{
-
perror("getcwd");
-
exit(1);
-
}
-
else
-
{
-
chdir(szPath);
-
printf("set current path succ [%s]\n", szPath);
-
}
-
-
// [4] umask 0
-
umask(0);
-
-
// [5] close useless fd
-
int i;
-
//for (i = 0; i < MAXFILE; ++i)
-
for (i = 3; i < MAXFILE; ++i)
-
{
-
close(i);
-
}
-
-
// [6] set termianl signal
-
signal(SIGTERM, sigterm_handler);
-
-
// open file and set rw limit
-
if((fd = open("outfile", O_CREAT|O_WRONLY|O_APPEND, 0600)) < 0)
-
{
-
perror("open");
-
exit(1);
-
}
-
-
printf("\nDaemon begin to work..., and use kill -9 PID to terminate\n");
-
-
// do sth in loop
-
while(_running)
-
{
-
if (write(fd, buf, strlen(buf)) != strlen(buf))
-
{
-
perror("write");
-
close(fd);
-
exit(1);
-
}
-
-
usleep(1000*1000);// 1 s
-
}
-
close(fd);
-
-
-
// print data
-
if((fd = open("outfile", O_RDONLY)) < 0)
-
{
-
perror("open");
-
exit(1);
-
}
-
char szBuf[1024] = {0};
-
if(read(fd, szBuf, sizeof(szBuf)) == -1)
-
{
-
perror("read");
-
exit(1);
-
}
-
printf("read 1024 bytes:\n%s\n", szBuf);
-
-
close(fd);
-
-
return 0;
-
}
-
-
/*
-
gcc -Wall -g -o test test.c
-
ps ux | grep -v grep | grep test
-
tail -f outfile
-
kill -s SIGTERM PID
-
*/
参考网址:http://blog.csdn.net/delphiwcdj/article/details/7364343
例二:
-
#include<unistd.h>
-
#include<signal.h>
-
#include<stdio.h>
-
#include<stdlib.h>
-
#include<sys/param.h>
-
#include<sys/types.h>
-
#include<sys/stat.h>
-
#include<time.h>
-
-
void init_daemon()
-
{
-
int pid;
-
int i;
-
pid=fork();
-
if(pid<0)
-
exit(1); //创建错误,退出
-
else if(pid>0) //父进程退出
-
exit(0);
-
-
setsid(); //使子进程成为组长
-
pid=fork();
-
if(pid>0)
-
exit(0); //再次退出,使进程不是组长,这样进程就不会打开控制终端
-
else if(pid<0)
-
exit(1);
-
-
//关闭进程打开的文件句柄
-
for(i=0;i<NOFILE;i++)
-
close(i);
-
chdir("/root/test"); //改变目录
-
umask(0);//重设文件创建的掩码
-
return;
-
}
-
-
void main()
-
{
-
FILE *fp;
-
time_t t;
-
init_daemon();
-
while(1)
-
{
-
sleep(60); //等待一分钟再写入
-
fp=fopen("testfork2.log","a");
-
if(fp>=0)
-
{
-
time(&t);
-
fprintf(fp,"current time is:%s\n",asctime(localtime(&t))); //转换为本地时间输出
-
fclose(fp);
-
}
-
}
-
return;
-
}
运行下面的命令:
cc testfork2.c -o testfork2
./testfork2
ps -ef|grep testfork2 可以查找到对应的进程
kill -9 1231杀死进程
参考网址:http://www.cnblogs.com/ringwang/p/3528093.html
例3.
-
/*daemon process*/
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <string.h>
-
#include <fcntl.h>
-
# include <sys/types.h>
-
#include <unistd.h>
-
#include <sys/wait.h>
-
#define MAXFILE 65535
-
int main(){
-
pid_t pc;
-
int i,fd,len;
-
char *buf="This is a Dameon TEST!\n";
-
len=strlen(buf);
-
pc=fork(); /*first step*/
-
if(pc<0){
-
printf("error fork\n");
-
exit(1);
-
}
-
else if(pc>0)
-
exit(0);
-
setsid(); /*second step*/
-
chdir("/"); /*thrid step*/
-
umask(0); /*forth step*/
-
for(i=0;i<MAXFILE;i++)
-
close(i);
-
while(1){
-
if((fd=open("/tmp/dameon.log",O_CREAT|O_WRONLY|O_APPEND,0600))<0){
-
perror("open");
-
exit(1);
-
}
-
write(fd,buf,len+1);
-
close(fd);
-
sleep(10);
-
}
-
-
}
阅读(1457) | 评论(0) | 转发(0) |