前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >守护进程(Daemon)

守护进程(Daemon)

作者头像
饶文津
发布2020-05-31 15:50:29
7.3K1
发布2020-05-31 15:50:29
举报
文章被收录于专栏:饶文津的专栏饶文津的专栏

守护进程的概念

守护进程(Daemon)一般是为了保护我们的程序/服务的正常运行,当程序被关闭、异常退出等时再次启动程序/恢复服务。 例如 http 服务的守护进程叫 httpd,mysql 服务的守护进程叫 mysqld。

或者有时候我们需要让我们的程序/服务能不中断地运行,在关闭终端后也能在后台默默运行,除了可以这样:

代码语言:javascript
复制
nohup ./xxx &

,也可以写成 Daemon 程序,例如一个服务器。

许多服务器进程就是写成了 Daemon 程序,例如 Nginx、Redis、Apache 等。

Linux 中一般把守护进程放在/etc/init.d/中,启动服务时的命令一般是 /etc/init.d/* {start|stop|status|restart}

守护进程脱离终端而存在。当你执行命令ps -ef 时,守护进程的 PPID (父进程 ID)都是 1,TTY (终端)则是???

守护进程的创建

Linix 中可以调用 int daemon(int nochdir, int noclose); 函数来使程序成为一个守护进程。具体用法可以用“男人”命令(手册命令)查询:man daemon

通过原理我们也可以手动创建一个守护进程。

相关概念:

  • 进程组(Process Group): 进程集合,每个进程组有一个组长(Leader),其进程 ID 就是该进程组 ID。
  • 会话(Session): 进程组集合,每个会话有一个组长,其进程 ID 就是该会话组 ID。
  • 控制终端(Controlling Terminal):每个会话可以有一个单独的控制终端,与控制终端连接的 Leader 就是控制进程(Controlling Process)。

fork

守护进程的父进程是 init 进程,在创建时先从父进程 fork 出来一个子进程,退出父进程,这时子进程变成孤儿,就成了 init 的子进程。

子进程会继承父进程的会话,进程组,控制终端,文件描述符等。

setid

通过setid()来创建新会话,同时也脱离了原来的进程组,会话以及控制终端,成为新的会话的组长。此时它可能会再申请一个控制终端,所以我们再 fork 一下,并只保留新的子进程,这样就不是会话组长了,就不能申请控制终端了。

close(fd)

之后再关闭从父进程继承的文件描述符。至少要关闭 0,1,2 这三个文件描述符,分别对应了 stdin, stdout, 和 stderr。不过通常用 sysconf(_SC_OPEN_MAX) 获取系统允许的最大文件描述符个数,然后全部 close 掉。 关闭之后我们要将文件描述符 0,1,2 重新定向到 "/dev/null",防止新打开的文件的文件描述符为 0,1,2。

umask(0)

设置文件掩码是为了不受父进程的 umask 的影响,能自由创建读写文件和目录。

chdir("/")

守护进程一般是一直执行到系统关机,在它运行过程中,它所在的目录就不能卸载(unmounted)。通过将它的工作目录转移到根目录,用来的目录就允许卸载了。也不一定要根目录(这种情况,运行需要超级权限),可以选择一个不需要卸载的路径。

代码语言:javascript
复制
void daemon() {
    // fork 出一个子进程。
    pid_t pid = fork();

    // fork 失败。
    if( pid == -1) {
        perror("fork");
        exit(1);
    }

    // 退出父进程。
    if(pid) {
        exit(0);
    }

    // 创建新会话。
    // 该子进程会成为新的会话和进程组的组长。
    if(setsid() == -1) {
        perror("setsid");
        exit(1);
    }

    // 再 fork 一次,新的子进程不再是会话组长。
    pid = fork();

    if( pid == -1) {
        perror("fork");
        exit(1);
    }

    if(pid) {
        exit(0);
    }
    
    // 关掉从父进程继承的文件描述符。
    int max_fd = sysconf(_SC_OPEN_MAX);
    for(int i = 0; i < max_fd; ++i) {
        close(i);
    }

    // 重定向文件描述符 0, 1, 2 到 /dev/null
    open("/dev/null", O_RDWR);
    dup(0);
    dup(0);

    // 设置文件创建权限掩码,不希望被父进程的掩码限制。
    umask(0);

    // 将当前工作目录设置为系统根目录。
    chdir("/");
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-04-15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 守护进程的概念
  • 守护进程的创建
    • fork
      • setid
        • close(fd)
          • umask(0)
            • chdir("/")
            相关产品与服务
            云数据库 Redis
            腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档