创建守护进程的步骤:
1)父进程中执行fork后,执行exit退出;
2)在子进程中调用setsid;(脱离控制台)
3)让根目录“/”成为子进程的工作目录;
4)把子进程的umask变为0;
5)关闭任何不需要的文件描述符。
其中,setsid函数创建一个新会话和一个新进程组,然后守护进程成为新会话的会话领导,以及新进程组的进程组领导。
chdir函数,根据参数pathname设置当前工作。
umask函数,umask调用把守护进程的umask设置为0,这样取消了来着父进程的umask,它们能潜在的干扰创建文件和目录。(指定权限)
一旦系统调用setsid,它不再有控制终端。
可以通过syslog提供服务,记录守护进程的各种输出信息。
openlog函数打开日志,syslog写入日志,closelog关闭日志。
例子如下:
int main(int arg,char *args[])
{
pid_t pid = fork();
if(pid == -1)
{
return;
}
if(pid > 0)
{
exit(0);
}
if(pid == 0)
{
setsid();
/* chdir("/");//防止程序干扰其它比如拔插U盘的工作
umask(0);
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO); */
}
syslog(LOG_INFO,"mydaemon is OK");
while(1)
{
sleep(1);
}
return EXIT_SUCCESS;
}
上面注释掉的程序不是必须要的。
一般守护进程的makefile文件中:可执行文件的名字最后是字母d。
和一个守护进程通信,要向它发送信号。
int status = 0;
void catch_Signal(int Sign)
{
switch(Sign)
{
case SIGINT:
printf("SIGINTSignal\n");
exit(0);//SIGINT来了后,进程退出
case SIGALRM:
printf("SIGALRMSignal\n");
alarm(5);
break;
case SIGUSR1:
status = 1;
break;
}
}
int mysignal(int signo,void (*func)(int))
{
struct sigaction act, oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
return sigaction(signo, &act, &oact);
}
void setdaemon()
{
pid_t pid = fork();
if(pid == -1)
{
return;
}
if(pid > 0)
{
exit(0);
}
if(pid == 0)
{
setsid();
/* chdir("/");//防止程序干扰其它比如拔插U盘的工作
umask(0);
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO); */
}
return;
}
int main(int arg,char *args[])
{
setdaemon();
mysignal(SIGINT, catch_Signal); //SIGINT:控制守护进程退出
mysignal(SIGALRM, catch_Signal);
mysignal(SIGUSR1, catch_Signal);
int i = 0;
while(1)
{
if(status == 1)
printf("hello:%d\n", i++);
sleep(1);
}
return EXIT_SUCCESS;
}
通过shell脚本启动守护进程:(执行多次,只启动一个守护进程)
#!/bin/sh
WHOAMI=`whoami`
PID=`ps -u $WHOAMI | grep signd |awk '{print $1}'`
if (test "$PID" ="") then
signd
fi
通过shell脚本结束守护进程:(执行一次,将所有已存在的守护进程杀死)
#!/bin/sh
WHOAMI=`whoami`
PID=`ps -u $WHOAMI | grep signd |awk '{print $1}'`
if(test"$PID"!="") then
kill -s 2 $PID
fi
使用FIFO与守护进程通信的例子如下:
读FIFO
void readfifo()
{
int len = 0;
char buf[1024];
memset(buf,0,sizeof(buf));
int fd = open("/home/zangyongcan/1/fifo1", O_RDONLY);
if(fd == -1)
{
printf("open file error %s\n",strerror(errno));
return;
}
while((len = read(fd,buf,sizeof(buf)) > 0)) //循环读取管道文件内容,直到管道文件被关闭,循环break
{
printf("%s\n", buf);
memset(buf, 0, sizeof(buf));
}
close(fd);
return;
}
void setdaemon() //把程序设置为daemon状态
{
pid_t pid = fork();
if(pid == -1)
{
printf("fork error!\n");
exit(0);
}
if(pid == 0)
{
setsid();
chdir("/");
umask(0);
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
if(pid > 0)
{
exit(0);
}
}
int main(void)
{
setdaemon();
readfifo();
puts("!!!Hello World!!!"); /* prints !!!Hello World!!! */
return EXIT_SUCCESS;
}
写FIFO:
void writefifo() //写fifo1管道文件内容
{
char buf[1024];
memset(buf,0,sizeof(buf));
int fd = open("/home/zangyongcan/1/fifo1", O_WRONLY);
if(fd == -1)
{
printf("write fifo1 error %s\n",strerror(errno));
return;
}
while(1)
{
memset(buf,0,sizeof(buf));
scanf("%s", buf);
write(fd, buf, sizeof(buf));
}
close(fd);
return;
}
int main(void)
{
writefifo();
return EXIT_SUCCESS;
}
如何让后台运行的daemon程序向屏幕打印信息。
daemon.c
1、执行mkfifo fifo1命令,创建一个名为fifo1的管道文件;
2、编译daemon.c,执行程序,关闭当前控制台终端端口,让程序在后台运行;
3、打开一个新的终端窗口,通过ps aux找到进程PID
4、执行
kill –s 2 PID
tty > fifo1
附:daemon.c内容如下
int mysignal(int signo,void (*func)(int))
{
struct sigaction act, oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
return sigaction(signo, &act, &oact);
}
void setdaemon()//把程序设置为daemon状态
{
pid_t pid, sid;
pid = fork();
if(pid < 0)
{
printf("fork error%s\n",strerror(errno));
exit(EXIT_FAILURE);
}
if(pid > 0)
{
exit(EXIT_SUCCESS);
}
if((sid = setsid()) < 0)
{
printf("setsidfailed %s\n",strerror(errno));
exit(EXIT_FAILURE);
}
}
void listenfifo()
{
const char *sfifoname = "fifo1";
int len = 0;
char buf[128];
memset(buf, 0,sizeof(buf));
int fd = open(sfifoname,O_RDONLY);//打开fifo1管道文件
if (fd == -1)
{
printf("open %sfailed, %s\n", sfifoname,strerror(errno));
}
len = read(fd, buf, sizeof(buf));
if(len > 0)
{
if(buf[strlen(buf)-1] =='\n')//如果读到的字符串最后一位是\n,将\n变为0
{
buf[strlen(buf)-1] = 0;//进程阻塞,直到有数据来了才返回
}
close(STDOUT_FILENO);//关闭标准输出
open(buf, O_WRONLY);
}
close(fd);
}
void catch_Signal(int Sign)
{
switch(Sign)
{
case SIGINT:
listenfifo();
break;
case SIGPIPE:
break;
}
}
int main(void)
{
setdaemon();//把进程设置为daemon状态
mysignal(SIGINT, catch_Signal);//捕捉SIGINT信号
mysignal(SIGPIPE, catch_Signal);//捕捉SIGPIPE信号
while(1)
{
puts("!!!HelloWorld!!!");/* prints!!!Hello World!!! */
sleep(1);
}
return EXIT_SUCCESS;
}