守护进程

1什么叫做守护进程

守护进程一般在系统引导装入的时候启动,在系统关闭的时候关闭,因为他们没有控制终端,所以说他们是后台运行的,用来执行一些日长的任务

利用ps -axj可以查看,其中a是查看其他的用户所拥有的进程,x显示没有控制终端的进程状态,-j显示与作业有关的信息,主要是第一行的信息

这个ps命令要正确 的打印,系统必须支持会话,否则不能正确的显示,父进程是0的通常是内核进程,内核进程的生命周期存在于系统的整个生命周期中,他们以超级用户权限运行,没有控制终端,不能使用命令行

如何判断是否是守护进程,当TPGID=-1的时候就是守护进程,comman命令加了[]就是系统级的守护进程,没有加[]就是用户级的守护进程,要注意,init并不是一个内核级的守护进程,他是用户级的守护进程的父进程

我们如何判定是否使用内核守护进程:我们使用一个内核组件,却不被用户进程的上下文所调用,并且是常驻内存,我们就应该使用内核守护进程

使用守护进程的大体原则:

如果一个进程永远都是以后台方式启动,并且不能受到Shell退出影响而退出,一个正统的做法是将其创建为守护进程(daemon)。守护进程值得是系统长期运行的后台进程,类似Windows服务。守护进程信息通过ps –a无法查看到,需要用到–x参数,当使

用这条命令的时候,往往还附上-j参数以查看作业控制信息,其中TPGID一栏为-1就是守护进程。

2对于守护进程的编程规则

  1. #include "apue.h"
  2. #include <syslog.h>
  3. #include <fcntl.h>
  4. #include <sys/resource.h>
  5. void daemonize(const char *cmd)
  6. {
  7. int i,fd0,fd1,fd2;
  8. pid_t pid;
  9. struct rlimit r1;
  10. struct sigaction sa;
  11. umask(0);
  12. if(getrlimit(RLIMIT_NOFILE,&r1) < 0)
  13.  err_quit("%s:can't get file limit",cmd);
  14. if((pid = fork())<0)
  15.  err_quit("%s:can't fork",cmd);
  16. else if(pid != 0)
  17.  exit(0);
  18. setsid();
  19. sa.sa_handler = SIG_IGN;
  20. sigemptyset(&sa.sa_mask);
  21. sa.sa_flags = 0;
  22. if(sigaction(SIGHUP,&sa,NULL) < 0)
  23.  err_quit("%s:can't ignore SIGHUP",cmd);
  24. if((pid = fork()) < 0)
  25.  err_quit("%s:can't fork",cmd);
  26. else if(pid != 0)
  27. {
  28. exit(0);
  29. }
  30. if(chdir("/") < 0)
  31.  err_quit("%s:can't change directory to /",cmd);
  32. if(r1.rlim_max == RLIM_INFINITY)
  33. r1.rlim_max = 1024;
  34. for(i = 0; i < r1.rlim_max;i++)
  35.  close(i);
  36. fd0 = open("/dev/null",O_RDWR);
  37. fd1 = dup(0);
  38. fd2 = dup(0);
  39. openlog(cmd,LOG_CONS,LOG_DAEMON);
  40. if(fd0 != 0|| fd1 != 1||fd2 != 2)
  41. {
  42. syslog(LOG_ERR,"unexpected file descriptors %d %d %d",fd0,fd1,fd2);
  43. exit(0);
  44. }
  45. }
  46. int main()
  47. {
  48. daemonize("my ");
  49. sleep(10);
  50. return 0;
  51. }

将一个进程变成一个守护进程的方法

(1)首先应该改变的是文件模式屏蔽字,因为在原本的进程上下文中,文件屏蔽字是不一定符合规范的,而且守护进程一般需要创建一个具有特定要求的屏蔽字,所以应该自己设置一个合理的屏蔽字,如果有更加严格的要求一般将文件模式设置为007

(2)调用fork,进行创建一个新的子进程,为什么要这么做第一,如果在shell中这样启动一个程序,当父进程exit的时候,会让shell认为此程序已经执行完毕第二,这样保证了这个进程不是一个子进程,这是使用setsid的先决条件

(3)调用setsid来进行设置一个新的会话,在创建会话的时候为了保证不分配一个控制终端,通常会在fork一次,这样就一定这个进程不是控制终端进程(其实在第一次fork的时候,并且使用了setsid的时候就确定了一定不是控制终端)这样做其实是为了更加确定(可以这样理解)

(4)更改当前的工作目录成为根目录,因为如果不更改为根目录,本来工作的目录其实就要一直被挂载,这在某些系统中,显然是这样做到,因为有可能这个操作系统会被删除

(5)进行所有的文件描述符的关闭

(6)某些守护进程打开/dev/null等,使其映射到0,1,2

关于程序的运行的结果显示

3守护进程出错记录

首先我们进行syslog的介绍

 syslog是一种工业标准的协议,可用来记录设备的日志。在UNIX系统,路由器、交换机等网络设备中,系统日志(System Log)记录系统中任何时间发生的大小事件。管理者可以通过查看系统记录,随时掌握系统状况。UNIX的系统日志是通过syslogd这个进程记录系统有关事件记录,也可以记录应用程序运作事件。通过适当的配置,我们还可以实现运行syslog协议的机器间通信,通过分析这些网络行为日志,藉以追踪掌握与设备和网络有关的状况。

因为守护进程是没有控制终端的,所以一般不能要求守护进程将出错记录写到控制台上,当然也不能写到一个文件中,如果每个守护进程都写到一个文件中,则无疑再用的时候是很麻烦的


这是日志系统的主要设施,这个设施被称为syslog设施,与用户进程调用syslog不是一个东西,三种可以产生日志的方法

(1)用户守护进程调用syslog函数产生日志消息,这些消息被送往UNIX域数据报套接字(这是一个设备,目前先了解为就是一个设施)然后接下来syslog是不产生UDP数据报的,要注意

(2)通过tcp/ip网络在或者不在此系统上的进程发送消息到UDP端口54

(3)内核通过log发送到klog

其中syslogd这个守护进程读取这三种格式的日志消息,并且根据/etc/syslog.conf发送,该文件决定了不同种类的消息应该送向何处,列如紧急消息可送往系统管理员,而警告信息可以送往一个文件

该syslog整个设备的接口(这里的接口指的是操作整个syslog的接口,可以往/dev/log或者UDP或者klog里面写,总之就是整个syslog的写入接口,但是可以根据选项选择怎样写,写什么样的小设备

主要有四个接口

#include<syslog.h>

void openlog(const char* ident,int option,int facility);

ident:一般是程序的名字,自动追加到每则日志消息中

option:



打开日志系统,其中这个是可选的,如果没有调用openlog,则在第一次调用syslog的时候,自动调用openlog。    

facility:

facility可以让配置文件说明,来自不同设施的消息将以不同的方式进行处理

注意:如果不调用openlog,或者以facility为0来调用,那么在调用syslog,可将facility作为priority参数的一个不分进行说明

void syslog(int priority,const char *format,....);

priority:是facility和level的组合,其中level可以是

format:以及其他的所有参数传至vsprintf函数进行格式话,在format中,每个出现的%m字符都被替换成和errno对应的字符串

void closelog(void );

用来关闭被用于与syslogd函数通信的描述符

int setlogmask(int makdpri);

设置屏蔽字,如果被设置,则不进行屏蔽,优先级屏蔽字

列如,我们提供一个范列

4单实例守护进程

什么叫做单实例守护进程

因为出于需要,系统在某个时间只需要一个守护进程运行,列如有的守护进程需要排他性的访问一个设备;比如说,cron进程在具体 的时间内运行某一个进程,此进程是守护进程,如果运行多个这样子的守护进程,则会在相同的时间内运行多个一样的程序,造成混轮,所以这个cron进程需要字任意一个时间内运行一个守护进程!!

守护进程采用文件-和记录锁实现了单实例守护进程,此方法假设,守护进程都需要创建一个文件,并且在第一个守护进程创建文件之后就对整个文件进行加锁,使其他的相同的守护进程不能自进行此文件的加锁,这样就实现了守护进程的单实例运行

  1. #include "unistd.h"
  2. #include "stdlib.h"
  3. #include "fcntl.h"
  4. #include "syslog.h"
  5. #include "string.h"
  6. #include "errno.h"
  7. #include "stdio.h"
  8. #include <sys/stat.h>
  9. #define LOCKFILE "var/run/daemon.pid"
  10. #define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
  11. extern int lockfile(int);
  12. int already_running(void)
  13. {
  14. int fd;
  15. char buf[16];
  16. fd = open(LOCKFILE,O_RDWR|O_CREAT,LOCKMODE);
  17. if(fd < 0)
  18. {
  19. syslog(LOG_ERR,"can't open :%s %s",LOCKFILE,strerror(errno));
  20. exit(1);
  21. }
  22. if(lockfile(fd) < 0)
  23. {
  24. if(errno == EACCES || errno ==EAGAIN)
  25. {
  26. close(fd);
  27. return(1);
  28. }
  29. syslog(LOG_ERR,"can't lock %s:%s",LOCKFILE,strerror(errno));
  30. exit(1);
  31. }
  32. ftruncate(fd,0);
  33. sprintf(buf,"%ld",(long)getpid());
  34. write(fd,buf,strlen(buf)+1);
  35. return 0;
  36. }
  37. int main()
  38. {
  39. already_running();
  40. return 0;
  41. }

如以上的程序,先创建一个文件夹,并且在运用此函数时候锁住该文件,如果再有守护进程调用的时候,则进行锁住,并且将其写入文件,strerror是将错误标志转换成字符串

5守护进程的惯例

在unix系统中,通常的守护进程设计通常是如此设计

(1)如果守护进程使用锁文件,则该锁文件通常位于/var/run目录中。然而需要主义的是,在该目录创建文件通常需要超级用户的权限,锁文件的名字通常是name.pid,例如cron守护进程锁文件的名字是/var/run/crond.pid

(2)如果守护进程支持配置选项,那么该配置文件一般是放在/etc上的,文件名字一般是name.cond,name是该守护进程或服务的名字,

(3)守护进程可以使用命令启动,但是该启动命令一班是在系统初始化 的脚本中进行的,如果守护进程终止,应当自动进行重启,并且如果一个守护进程更新了配置文件,则应该重新启动

(4)为了避免当更改配置文件,需要停止在启动进程的时候,某些进程将捕捉SIGHUP信号,当接受到此信号的时候,重新读取配置文件。因为守护进程不与终端结合,他们或者是没有控制终端的会话首进程,或者是孤儿进程组的成员,所以守护进程没有里有期望接受SIGHUP,因此,守护进程可以重复使用SIGHUP








posted @ 2016-09-12 09:14  SmileLion_LY  阅读(3926)  评论(0编辑  收藏  举报