zl程序教程

您现在的位置是:首页 >  系统

当前栏目

Linux系统编程-进程(三):守护进程【Daemon Process(精灵进程)】【Linux 中的后台服务进程,独立于控制终端】

2023-09-27 14:20:40 时间

一、守护进程介绍

守护进程(Daemon Process),也就是通常说的 Daemon 进程(精灵进程),是 Linux 中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。一般采用以d结尾的名字。

守护进程是个特殊的孤儿进程,这种进程脱离终端,为什么要脱离终端呢?之所以脱离于终端是为了避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示。

由于在 Linux 中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。

Linux 的大多数服务器就是用守护进程实现的。比如,Internet 服务器 inetd,Web 服务器 httpd 等。

二、守护进程模型

1) 创建子进程,父进程退出(必须)

  • 所有工作在子进程中进行形式上脱离了控制终端

2) 在子进程中创建新会话(必须)

  • setsid()函数
  • 使子进程完全独立出来,脱离控制

3) 改变当前目录为根目录(不是必须)

  • chdir()函数
  • 防止占用可卸载的文件系统
  • 也可以换成其它路径

4) 重设文件权限掩码(不是必须)

  • umask()函数
  • 防止继承的文件创建屏蔽字拒绝某些权限
  • 增加守护进程灵活性

5) 关闭文件描述符(不是必须)

  • 继承的打开文件不会用到,浪费系统资源,无法卸载

6) 开始执行守护进程核心工作(必须)

守护进程退出处理程序模型

三、守护进程参考代码

写一个守护进程, 每隔2s获取一次系统时间, 将这个时间写入到磁盘文件:

/*
* time_t rawtime;
* time ( &rawtime  ); --- 获取时间,以秒计,从1970年1月一日起算,存于rawtime
* localtime ( &rawtime  ); //转为当地时间,tm 时间结构
* asctime() // 转为标准ASCII时间格式:
*/
void write_time(int num)
{
    time_t rawtime;
    struct tm * timeinfo;
    // 获取时间
    time(&rawtime);
#if 0
    // 转为本地时间
    timeinfo = localtime(&rawtime);
    // 转为标准ASCII时间格式
    char *cur = asctime(timeinfo);
#else
    char* cur = ctime(&rawtime);
#endif
​
    // 将得到的时间写入文件中
    int fd = open("/home/edu/timelog.txt", O_RDWR | O_CREAT | O_APPEND, 0664);
    if (fd == -1)
    {
        perror("open error");
        exit(1);
    }
    // 写文件
    int ret = write(fd, cur, strlen(cur) + 1);
    if (ret == -1)
    {
        perror("write error");
        exit(1);
    }
    // 关闭文件
    close(fd);
}
​
int main(int argc, const char* argv[])
{
    pid_t pid = fork();
    if (pid == -1)
    {
        perror("fork error");
        exit(1);
    }
​
    if (pid > 0)
    {
        // 父进程退出
        exit(1);
    }
    else if (pid == 0)
    {
        // 子进程
        // 提升为会长,同时也是新进程组的组长
        setsid();
​
        // 更改进程的执行目录
        chdir("/home/edu");
​
        // 更改掩码
        umask(0022);
​
        // 关闭文件描述符
        close(STDIN_FILENO);
        close(STDOUT_FILENO);
        close(STDERR_FILENO);
​
        // 注册信号捕捉函数
        //先注册,再定时
        struct sigaction sigact;
        sigact.sa_flags = 0;
        sigemptyset(&sigact.sa_mask);
        sigact.sa_handler = write_time;
        sigaction(SIGALRM, &sigact, NULL);
​
        // 设置定时器
        struct itimerval act;
        // 定时周期
        act.it_interval.tv_sec = 2;
        act.it_interval.tv_usec = 0;
        // 设置第一次触发定时器时间
        act.it_value.tv_sec = 2;
        act.it_value.tv_usec = 0;
        // 开始计时
        setitimer(ITIMER_REAL, &act, NULL);
​
        // 防止子进程退出
        while (1);
    }
​
    return 0;
}