Linux快速入门之 守护进程 (14)

守护进程


守护进程(精灵进程), 为Linux系统中的后台服务进程,其生存周期较长 。通常独立于控制终端并周期性执行某种任务或等待某些事件, 一般以d为结尾

1.1 进程组

多进程的集合就是进程组,该进程组 组长就是进程组中的第一个进程,组长以外的都是普通的成员,每个进程组都有唯一的组ID,进程组的ID和组长的PID是一样的;

进程组中的成员是可以转移的,如果当前进程组的成员被转移到其他组或者该进程组中所有进程都退出了,那么这个进程组就不存在了,如果进程组中组长死了,但是还存在其他进程,该进程组还是继续存在。

//得到当前进程所在的进程组 ID
pid_t getpgrp(void);

//获取指定的进程所在的进程组的组ID ,参数 pid 就是指定的进程
pid_t getpgid(pid_t pid , pid_t pgid);

参数:

pid: 某个进程的进程 ID;

pgid: 某个进程组的组 ID;

  1. 如果pgid 对应的进程组存在 ,pid对应的进程会移动到这个组中,pid != pgid;
  2. 如果 pgid 对应的进程组不存在 ,会创建一个新的进程组,因此要求 pid == pgid ,当前进程就是组长了;

返回值: 函数调用成功返回 0 ; 失败返回 -1;

1.2 会话

会话(session)是由一个或多个进程组成的,一个会话可以对应一个控制终端,也可以没有。一个普通进程可以调用 setsid() 函数使自己成为新 session 的领头进程(会长)同时这个session领头进程会被放入一个新的进程组中。

#include <unistd.h>

//获取某个进程所属的会话ID
pid_t getsid(pid_t pid);

//将某个进程变成会话 , 得到一个守护进程
//使用那个进程调用这个函数,该进程变成一个会话
pid_t setsid(void);

该函数使用注意事项:

  • 调用这个函数的进程不能是组长进程,如果保证这个函数能调用成功?

​ —先fork()创建子进程 , 终止父进程,让子进程调用这个函数;

  • 如果调用这个函数的进程不是进程组长,会话创建成功

    — 这个进程会变成当前会话中的第一个进程 , 同时也会变成新的进程组的组长;

    — 该函数调用成功 , 当前进程就脱离了控制终端 ,因此不会阻塞终端;

1.3 创建守护进程

创建一个守护进程标准步骤:

  • 创建子进程,让父进程退出

    1. 父进程可能不是组长进程,不符合条件也没什么价值,退出即可;
    2. 子进程没有任何职务,目的是让子进程最终变成一个会话,最终变成守护进程;
  • 通过子进程创建新的会话,调用函数 setsid(),脱离控制终端 , 变成守护进程;

  • 改变当前进程的工作目录(可选项,非必须)

    1. 某些文件系统可以被卸载(U盘、移动硬盘),进程如果在这些目录中运行,运行期间这些设备被卸载了,运行的进程就不能工作了;

    2. 修改当前进程的工作目录需要调用函数 chdir()

      int chdir(const char *path);
      
  • 重新设置文件的掩码(可选项,非必须)

    1. 掩码: umask , 在创建新文件的时候需要和这个掩码进行运算,去掉文件的某些权限;

    2. 设置掩码需要使用函数 umask()

      mode_t umask(mode_t mask);
      
  • 关闭/重定向文件描述符

    1. 启动一个进程,文件描述符中默认有三个被打开,对应当前的终端文件;

    2. 因为进程通过调用 setsid()已经脱离了当前终端,因此关联的文件描述符也就没用了,可以关闭;

      close(STDIN_FILENO);
      close(STDOUT_FILENO);
      close(STDERR_FILENO);
      
    3. 重定向文件描述符(和关闭二选一);改变文件描述符关联的默认文件,让他们指向特殊的文件 /dev/null ,将数据甩到该设备文件中,数据就会被销毁;

      int fd = open("/dev/null", O_RDWR);
      // 重定向之后, 这三个文件描述符就和当前终端没有任何关系了
      dup2(fd, STDIN_FILENO);
      dup2(fd, STDOUT_FILENO);
      dup2(fd, STDERR_FILENO);
      
  • 根据实际需求在守护进程中执行某些特定的操作;

1.4 守护进程的应用

写一个系统进程,每隔2s获取一次系统文件,并将得到的时间写入到磁盘文件中;

//守护进程 :每隔2s 获取系统时间并写入磁盘文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include  <unistd.h>
#include  <fcntl.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <time.h>
#include <signal.h>

//信号处理动作
void writeFile(int num)
{
    // 得到系统时间
    time_t seconds =time(NULL);
    //时间转换  ,总秒数 -> 可以识别的时间字符串
    struct tm *loc = localtime(&seconds);
    char *curtime =asctime(loc);   //自带换行
    //打开一个文件, 如果文件不存在就创建 , 文件需要有追加属性
    int fd =open("./time.log",O_WRONLY|O_RDONLY|O_APPEND,0644);
    if (fd ==-1)
    {
        perror("open error");
    }
    write(fd,curtime ,strlen(curtime));
    close(fd);
}


int main()
{
    //1. 创建子进程 ,杀死父进程
    pid_t pid = fork();
    if (pid >0)
    {
        //kill(getpid(),9);      raise(9);   abort();
        exit(0);
    }

    //2 .子进程,变成会话 ,脱离当前终端
    setsid();

    //3. 修改进程目录,修改到不能被修改和删除的目录中 /home/liu
    chdir("/home/liu");

    //4. 设置掩码 ,在进程中创建文件的时候这个掩码就起作用
    umask(022);

    //5. 重定向和终端关联的文件描述符 -> /dev/null
    int fd = open("/dev/null",O_RDWR);
    dup2(fd ,STDIN_FILENO);
    dup2(fd ,STDOUT_FILENO);
    dup2(fd ,STDERR_FILENO);

    //6. 委托内核捕捉并处理将来发生的信号 -SIGALRM(14)
   struct sigaction act;
    act.sa_flags = 0;
    act.sa_handler = writeFile;
    sigemptyset(&act.sa_mask);
    sigaction(SIGALRM, &act, NULL);

    // 6. 设置定时器
    struct itimerval val;
    val.it_value.tv_sec = 2;
    val.it_value.tv_usec = 0;
    val.it_interval.tv_sec = 2;
    val.it_interval.tv_usec = 0;
    setitimer(ITIMER_REAL, &val, NULL);

    while(1)
    {
        sleep(100);
    }

    return 0;

}



  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值