linux高级环境编程--守护进程

基本概念
  • Linux Daemon(守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。Linux系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括系统日志进程syslogd、 web服务器httpd、邮件服务器sendmail和数据库服务器mysqld等。
  • 守护进程一般在系统启动时开始运行,除非强行终止,否则直到系统关机都保持运行。守护进程经常以超级用户(root)权限运行,因为它们要使用特殊的端口(1-1024)或访问某些特殊的资源。
  • 一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程。守护进程是非交互式程序,没有控制终端,所以任何输出,无论是向标准输出设备stdout还是标准出错设备stderr的输出都需要特殊处理。
  • 守护进程的名称通常以d结尾,比如sshd、xinetd、crond等
编程规则
  • 在父进程中执行fork并exit推出-->确保不是进程组组长进程,进程组组长,就无法调用setsid;
  • 在子进程中调用setsid函数创建新的会话-->没有控制终端;
  • 在子进程中调用chdir函数,让根目录 ”/” 成为子进程的工作目录-->若挂载在文件系统中,该文件系统不能卸载;
  • 在子进程中调用umask函数,设置进程的umask为0;
  • 在子进程中关闭任何不需要的文件描述符-->不需要父进程的相关描述符。

为什么需要设置umask?

每个文件建立时有一个权限,如755,umask命令用来设置限制新建文件权限的掩码,表示在原有权限上删除相应的权限。如文件原来的初始化权限是777,那么执行命令umask 022以后,该文件的权限将变为755:如果该文件原来的初始化权限是666,那么该文件的权限将变为644。
因为守护进程一开始是从父进程fork来,继承来的umask可能会设置为拒绝一些权限,当守护进程要创建可读可写文件时,可能会因为权限不够而无法发挥作用。

先补充相关知识点:

会话

会话是一个或多个进程组的集合,进程调用setsid()创建一个新会话,如果该进程不是一个进程组的组长,将会发生以下:

linux高级环境编程--守护进程_第1张图片
image.png

会话的作用主要由以下三个,主要用于守护进程中:

  • 使当前进程脱离原会话的控制
  • 使当前进程脱离原进程组的控制
  • 使当前进程脱离原控制终端的控制

总结说就是非进程组组长进程创建会话后,该进程称为会话首进程,并且成为新建进程组的组长进程,新进程组ID为该进程ID,重要一点就是没有控制终端。
创建会话一个条件就是非组长进程,为了保证,通常先调用fork,然后使父进程终止,而子进程继续,子进程继承了进程组ID,而进程ID又是新分配,保证了不是进程组组长。
一个会话通常开始于用户登录,终止于用户退出,期间所有的进程都属于这个会话。一个会话一般包含一个会话首进程、一个前台进程组和一个后台进程组,控制终端可有可无;此外,前台进程组只有一个,后台进程组可以有多个,这些进程组共享一个控制终端

linux高级环境编程--守护进程_第2张图片
image.png

对守护进程进行总结,守护进程是脱离终端并在后台运行的进程,需要脱离原控制终端,原进程组,原会话,因此要建立一个新的会话,由于进程组组长不能创建会话,需要先fork,确保进程ID不是原会话ID和进程组组长ID;由于fork保留了父进程的相关信息,如目录和权限,由于守护进行要更大的灵活性,需要改变目录和修改权限。同时,需要关闭原来不需要的文件描述符。过程:fork-->setsid-->chdir-->umask-->close

作业控制

Shell分前后台来控制的不是进程而是作业(Job)或者进程组(Process Group)。一个前台作业可以由多个进程组成,一个后台作业也可以由多个进程组成,Shell可以同时运行一个前台作业和任意多个后台作业,这称为作业控制(Job Control)

$ proc1 | proc2 &
$ proc3 | proc4 | proc5

其中proc1和proc2属于同一个后台进程组,proc3、proc4、proc5属于同一个前台进程组,Shell进程本身属于一个单独的进程组。这些进程组的控制终端相同,它们属于同一个Session,一个Session与一个控制终端相关。当用户在控制终端输入特殊的控制键(例如Ctrl-C)时,内核会发送相应的信号(例如SIGINT)给前台进程组的所有进程。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

// 守护进程初始化函数
void init_daemon()
{
    pid_t pid;
    int i = 0;

    if ((pid = fork()) == -1) {
        printf("Fork error !\n");
        exit(1);
    }
    if (pid != 0) {
        exit(0);        // 父进程退出
    }

    setsid();           // 子进程开启新会话,并成为会话首进程和组长进程
    if ((pid = fork()) == -1) {
        printf("Fork error !\n");
        exit(-1);
    }
    if (pid != 0) {
        exit(0);        // 结束第一子进程,第二子进程不再是会话首进程
    }
    chdir("/tmp");      // 改变工作目录
    umask(0);           // 重设文件掩码
    for (; i < getdtablesize(); ++i) {
       close(i);        // 关闭打开的文件描述符
    }

    return;
}

int main(int argc, char *argv[])
{
    int fp;
    time_t t;
    char buf[] = {"This is a daemon:  "};
    char *datetime;
    int len = 0;

    // 初始化 Daemon 进程
    init_daemon();

    // 每隔一分钟记录运行状态
    while (1) {
        if (-1 == (fp = open("/tmp/daemon.log", O_CREAT|O_WRONLY|O_APPEND, 0600))) {
          printf("Open file error !\n");
          exit(1);
        }
        len = strlen(buf);
        write(fp, buf, len);
        t = time(0);
        datetime = asctime(localtime(&t));
        len = strlen(datetime);
        write(fp, datetime, len);
        close(fp);
        sleep(60);
    }

    return 0;
}
出错记录

出错记录提出来主要是解决守护进程的错误消息记录问题,因为守护进程没有自己的终端显示,但若是每个守护进程都维护一个自己记录文件就很麻烦,需要有一个集中的守护进程出错记录设施。
在linux中使用syslog进行记录。

后台进程job与守护进程区别
后台进程一般不需要交互,启动时加一个 & 就可以,会输出相应的job ID.
对切换到后台的进程,ps可以查看到,也可以通过 jobs命令只查看所有后台进程。
而守护进程永远都以后台方式启动,ps -a无法查看到,ps xj输出中TPGID为-1就是守护进程。

你可能感兴趣的:(linux高级环境编程--守护进程)