Linux网络编程[如何打造守护进程(daemon)]

来源:互联网 发布:深圳淘宝摄影 编辑:程序博客网 时间:2024/06/08 07:10

Linux网络编程[如何打造守护进程(daemon)]

  1. 守护进程的含义
  2. 守护进程的相关编程步骤
  3. 守护进程的出错处理
  4. 打造一个我们自己的守护进程

守护进程的含义

守护进程(daemon)是运行在后台的一种特殊的进程.它独立于控制终端并且周期性的执行某种(特殊)任务,或者一直在等待某些事情的发生.它不需要用户输入就能够运行,并且能提供某种服务,不是对整个系统就是对某个用户的程序进行服务.linux系统的大多数服务器就是通过守护进程进行实现的.

常见的守护进程:系统日志进程syslogd,Web服务器的httpd,数据库服务器mysqld等

特点:
1: 守护进程一般在系统启动的时候开始运行,除非强制终止,否则知道系统关机都保持运行,守护经常要以超级用户(root)权限运行,因为它要使用特殊的端口(1-1024)或访问某些特殊的资源
2:守护进程的父亲进程是init进程,因为它真正的父亲进程在fork出子进程后就先于子进程exit退出了,所以它是一个init领养的孤儿进程.守护进程是非交互程序,没有控制终端,没有任何的输出,无论向标准输出(stdout)还是出错输出(stderr)都是需要特殊的处理
3:守护进程一般是以d结尾的

守护进程的相关编程步骤

1:使用umask将文件模式创建屏蔽字设为0
2:调用fork,然后让父进程退出(exit)
3:调用setsid创建一个新会话
4:将当前的工作目录更改为根目录(/)
5:关闭不需要的文件描述符

简单解释:
1:setsid()函数

#include<unistd.h>pid_t setsid(void)注意:只有当该进程不是一个进程组长时,才回去成功创建一个会话期1:摆脱原来会话的控制.该进程变成一个新的会话其的首进程2:摆脱原进程组,成为一个新进程的组长3:摆脱终端控制.如果在调用setsid()前面,该进程有控制终端,那么在调用后,该进程就会与此进程解除联系,如果该进程是一个进程组的组长,那么此函数就会返回错误

fork()创建子进程,父进程exit()退出
由于守护进程是脱离控制终端的,因此,完成第一步后就会在Shell终端里造成程序已经运行完毕的假象。之后的所有工作都在子进程中完成,而用户在Shell终端里则可以执行其他命令,从而在形式上做到了与控制终端的脱离,在后台工作。

在子进程中调用 setsid() 函数创建新的会话
在调用了 fork() 函数后,子进程全盘拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端等并没有改变,因此,这还不是真正意义上的独立开来,而 setsid() 函数能够使进程完全独立出来。

在子进程中调用 chdir() 函数,让根目录 ”/” 成为子进程的工作目录
这一步也是必要的步骤。使用fork创建的子进程继承了父进程的当前工作目录。由于在进程运行中,当前目录所在的文件系统(如“/mnt/usb”)是不能卸载的,这对以后的使用会造成诸多的麻烦(比如系统由于某种原因要进入单用户模式)。因此,通常的做法是让”/”作为守护进程的当前工作目录,这样就可以避免上述的问题,当然,如有特殊需要,也可以把当前工作目录换成其他的路径,如/tmp。改变工作目录的常见函数是chdir。

在子进程中调用 umask() 函数,设置进程的文件权限掩码为0
文件权限掩码是指屏蔽掉文件权限中的对应位。比如,有个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限。由于使用fork函数新建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置为0,可以大大增强该守护进程的灵活性。设置文件权限掩码的函数是umask。在这里,通常的使用方法为umask(0)。

在子进程中关闭任何不需要的文件描述符

同文件权限码一样,用fork函数新建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。
在上面的第二步之后,守护进程已经与所属的控制终端失去了联系。因此从终端输入的字符不可能达到守护进程,守护进程中用常规方法(如printf)输出的字符也不可能在终端上显示出来。所以,文件描述符为0、1和2 的3个文件(常说的输入、输出和报错)已经失去了存在的价值,也应被关闭。

守护进程退出处理
当用户需要外部停止守护进程运行时,往往会使用 kill 命令停止该守护进程。所以,守护进程中需要编码来实现 kill 发出的signal信号处理,达到进程的正常退出。

守护进程的出错处理

守护进程出错处理:

 1;由于守护进程完全脱离了控制终端,因此不能向其他程序一样输出错误信息到控制台的方式来通知程序员 2:通常的版本就是使用syslog的方式,将出错信息输入到"var/log/syslog"系统日志文件中去 3:syslog是linux中的系统日志管理服务通过守护进行syslogd来进行维护的

syslog服务说明:

 1:openlog函数用于打开系统日志服务的一个连接 2:syslog函数用于向日志文件中写入信息,在这里可以规定消息的优先级,消息的输出格式等 3:closelog函数用于关闭系统日志服务的连接函数原型:  #include<syslog.h>  void openlog(char *ident,int option,int facility);  参数:        ident:向每个消息加入的字符串,通常为程序的名称    option:          LOG_CONS  若日志消息不能通过发送至syslog,将该消息写入到控制台          LOG_NDELAY  立即打开linux域数据报套接口至syslog守护进程,通常,在记录第一条消息之前,该套接口不打开          LOG_PERROR  除了将日志消息发送给syslog外,还将它写入至stderr          LOG_PID          每条消息都包含进程ID,此选择项可供对每个请求都fork一个子进程的守护进程使用    facility:          LOG_AUTH  授权程序如login,su,getty等等          LOG_CRON  cron和at          LOG_DAEMON 系统的守护进程,如ftpd和routed等          LOG_KERN 内核产生的消息          LOG_LOCAL0~7 保留由本地使用          LOG_LPR     行打系统,如lpd,lpc等          LOG_MAIL    邮件系统          LOG_NEWSU   senet网络新闻系统          LOG_SYSLOG  syslog守护进程本身          LOG_USER  来自其他用户进程的消息          LOG_UUCP  UUCP系统    #include<syslog.h>      void syslog(int priority,char *format, ...);      void closelog(void);  参数:       priority:消息优先级       LOG_EMERG     紧急(系统不可使用,最高优先级)       LOG_ALERT     必须立即修复       LOG_CRIT      临界条件(例如:硬设备出错)       LOG_ERR       出错条件       LOG_WARNING   警告条件       LOG_NOTICE    正常,但是很重要的条件       LOG_INFO      信息性的消息       LOG_DEBUG     调试拍错的消息(最低的优先级)

打造一个我们自己的守护进程

/* * =========================================================================== * *       Filename:  daemon2.c *    Description:   *        Version:  1.0 *        Created:  2017年05月16日 23时06分48秒 *       Revision:  none *       Compiler:  gcc *         Author:   (),  *        Company:   * * =========================================================================== */#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<string.h>#include<syslog.h>#include<sys/types.h>#include<signal.h>static int flag = 1;void sig_handler(int signo){  if(signo == SIGQUIT){    flag = 0;  }}void create_deamon();int main(int agrc,char *argv[]){  if(signal(SIGQUIT,sig_handler) == SIG_ERR){    printf("sigquit error");    exit(1);  }  create_deamon();   openlog("medeamon",LOG_CONS | LOG_PID,0);  int count = 0;  while(flag){    syslog(LOG_INFO,"info;%d\n",count);    sleep(1);    count++;  }  closelog();  return 0;}void create_deamon(){  pid_t pid;  //使用umask创建屏蔽字  umask(0);  //fork进程  pid = fork();  if(pid == -1){    printf("fork error");    exit(EXIT_FAILURE);  }else if(pid > 0){    exit(1);  }else{    //设置sessionid    if(setsid() < 0 ){      printf("setsid error");      exit(1);    }    //将其设置为根路径    chdir("/");  }}

执行的效果图如下所示:
这里写图片描述

在/var/log/syslog下文件所示
这里写图片描述

当然在unistd头文件中,包含了这样一个函数daemon的声明:

 #include<unistd.h> int daemon(int nochdir,int noclose); 它也是用来创建守护进程的,成功返回0,出错返回-1 其主要的原理就类似上述demo中的create_daemon()函数

具体的使用不在赘述

欢迎持续访问博客

原创粉丝点击