Daemon的实现

来源:互联网 发布:魅族note6软件分身 编辑:程序博客网 时间:2024/06/07 03:24

注:Linux下可以直接调用damon函数来实现daemon进程,没必要重新实现,理解其原理即可。

在Linux中专门提供了一个函数来完成这个daemon化的过程,这个函数的原型如下

int daemon ( int __nochdir, int __noclose) ; 

如果__nochdir的值为0,则将切换工作目录为根目录;如果__noclose为0,则将标准输入,输出和标准错误都重定向到/dev /null。

经过这个函数调用后的程序将运行在后台,成为一个daemon程序,而linux下大多的服务都是以此方式运行的。

我们来看一个简单的例子。例如编写例子程序test.c

#include <unistd.h>  #include <stdio.h>   int do_sth( )  {  //Add what u want  return 0 ;  }  int main( )  { daemon( 0 , 0 ) ;  while ( 1 )  { do_sth( ) ; sleep( 1 ) ;  }  } 

编译并运行

[ leconte@ localhost daemon] $ gcc -o test test.c [ leconte@ localhost daemon] $ ./ test 

程序进入了后台,通过ps查看进程情况,可以看到进程的父进程id为1,即init进程

用lsof查看test进程所打开的文件,可以看到文件描述符0,1,2都被重定向到/dev/null

并且能够看到,进程的当前工作目录(cwd)为根目录/,daemon函数已经帮我们完成了daemon化的过程,接下来我们只需要关注于程序功能的实现了。

 

Linux 守护进程

Linux 守护进程概述

Linux Daemon (守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。 Linux 系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括系统日志进程 syslogd 、 web 服务器 httpd 、邮件服务器 sendmail 和数据库服务器 mysqld 等。
 
守护进程一般在系统启动时开始运行,除非强行终止,否则直到系统关机都保持运行。守护进程经常以超级用户( root )权限运行,因为它们要使用特殊的端口( 1-1024 )或访问某些特殊的资源。
 
一个守护进程的父进程是 init 进程,因为它真正的父进程在 fork 出子进程后就先于子进程 exit 退出了,所以它是一个由 init 继承的孤儿进程。守护进程是非交互式程序,没有控制终端,所以任何输出,无论是向标准输出设备 stdout 还是标准出错设备 stderr 的输出都需要特殊处理。
 
工作原理
Linux 守护进程的工作模式是服务器 / 客户机( Server/Client ),服务器在一个特定的端口上监听( Listen )等待客户连接,连接成功后服务器和客户端通过端口进行数据通信。守护进程的工作就是打开一个端口,并且监听( Listen )等待客户连接。如果客户端产生一个连接请求,守护进程就创建( Fork )一个子服务器响应这个连接,而主服务器继续监听其他的服务请求。


其实,linux提供了daemon函数用于创建守护进程,实现原理与上文中介绍的是一样的。

#include <unistd.h>

int daemon(int nochdir, int noclose);

1. daemon()函数主要用于希望脱离控制台,以守护进程形式在后台运行的程序。

2. 当nochdir为0时,daemon将更改进城的根目录为root(“/”)。

3. 当noclose为0是,daemon将进城的STDIN, STDOUT, STDERR都重定向到/dev/null。

 

daemon的实现大致如下:

int daemon( int nochdir,  int noclose )
{
   pid_t pid;
   if ( !nochdir && chdir("/") != 0 ) //如果nochdir=0,那么改变到"/"根目录
       return -1;
   
   if ( !noclose ) //如果没有noclose标志
   {
        int fd = open("/dev/null", O_RDWR); 
        if ( fd  <  0 )
            return -1;

 

       /* 重定向标准输入、输出、错误 到/dev/null,

键盘的输入将对进程无任何影响,进程的输出也不会输出到终端

*/

dup(fd, 0);

dup(fd, 1);

dup(fd, 2);     

close(fd);

}

   pid = fork();  //创建子进程.
   if (pid  <  0)  //失败
      return -1;
   if (pid > 0)
       _exit(0); //返回执行的是父进程,那么父进程退出,让子进程变成真正的孤儿进程.

//创建的 daemon子进程执行到这里了
   if ( setsid()  < 0 )   //创建新的会话,并使得子进程成为新会话的领头进程
      return -1;
   return 0;  //成功创建daemon子进程
}

 

使用实例:

int main()

{

    daemon(1, 1); //参数根据需求确定

    /*  在这里添加你需要在后台做的工作代码  */

}

如何杀死这样的进程:

通过ps+grep找到对应的后台进程,使用kill命令将进程杀死;也可创建shell脚本对进程的启动、关闭、重启进行自动管理,参考下文:

http://blog168.chinaunix.net/space.php?uid=20196318&do=blog&id=28824

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>


int main(int argc, char *argv[])
{
char strCurPath[PATH_MAX];


if(daemon(1, 1) < 0)
{
perror("error daemon.../n");
exit(1);
}
sleep(10);


if(getcwd(strCurPath, PATH_MAX) == NULL)
{
perror("error getcwd");
exit(1);
}
printf("%s/n", strCurPath);
return 0;
}
原创粉丝点击