• 守护进程


    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








  • 相关阅读:
    Completely disable mousewheel on a WinForm
    C# Form内存回收
    解锁用户
    CentOS7+JDK8编译Hadoop2.6.4
    网址收藏
    pvresize
    Linux crontab 命令格式与详细例子
    在CentOS 7中安装与配置JDK8
    Disable SELinux CentOS 7
    Python3标准库:math数学函数
  • 原文地址:https://www.cnblogs.com/SmileLion/p/5863593.html
Copyright © 2020-2023  润新知