守护进程

守护进程是什么

守护进程是一类运行在系统后台,不受任何终端、上下文影响的特殊后台进程。从运行开始,直到系统关闭时才会结束。

通常写一个服务程序,或者程序从设备开机起到设备关机一直都在运行,就适合做成守护进程形式。

为什么要守护进程

简单来说,就是为了使程序运行起来后,不受一些外部因素的干扰而异常退出。

linux系统运行起来后,pid为1的进程是init进程,负责启动linux系统,其他的进程都是在他创建出来的。我们在某个终端上通过命令./a.out的形式来运行一个程序,这种终端程序就是我们所运行程序的父进程。那么程序中相关的文件描述符、跟终端相关的信号等资源还是会和运行的进程产生联系。为了后台进程拜托终端的影响,必须让进程脱离父进程,成为孤儿进程,从而能够被init接管。同时关闭不必要的文件描述符,释放不必要的资源。

如何实现一个守护进程

  1. 调用umask将文件模式创建屏蔽字设置为0,这样可以不受集成而来的屏蔽字的影响。
  2. 调用fork,并且在子进程中调用setsid。
    要创建一个新的session,摆脱原来的session,切断与原来终端的联系,做法是调用setsid。但是调用者必须不是进程组组长。我们通常执行程序,例如./a.out,此时a.out就是session leader(进程组ID(PGID)与pid相同)。所以要创建守护进程,第一步要先fork一次,这样fork出来的子进程,就不是session leader。这里需要说明一下,一个session,是一个或多个进程组的集合。session从登录进入,到登出为一个session的生命周期结束。在这个过程中,会创建多个进程组。
  3. 再次调用fork。这一步不是必须的,之所以调用是为了防止进程再次打开控制终端。因为只有session leader才能打开进程终端,再fork一次,子进程就不是session leader了。也就无法取得控制终端。当然为防止进程取得会话终端,也可以换一种方式:就是无论何时打开一个终端设备都要指定O_NOCTTY参数。
  4. 将当前工作目录改为根目录。这一步也是为了不受从父进程继承而来的工作目录的 影响。
  5. 关闭不必要的文件描述符。可以使用getrlimit函数来获取当前打开的文件描述符的最大值,然后关闭全部。
  6. 为了使标准输入、标准输出、标准错误输出的相关调用不报异常。可以将0,1, 2 三个文件描述符绑定到/dev/null下。

代码:


#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/resource.h>

int main(int argc, char* argv[])
{
    int i = 0;
    int fd = 0;
    int fd0, fd1, fd2;
    int fork_num = 5;
    pid_t   pid;
    struct rlimit   fd_limit;

    getrlimit(RLIMIT_NOFILE, &fd_limit);

    //设置文件模式创建屏蔽字
    umask(0);

    //第一次fork,使得子进程可以调用setsid来创建新的会话
    pid = fork();
    if (pid > 0) {
        //父进程退出
        exit(0);
    }
    //创建新的会话,切段与之前终端的联系
    setsid();

    //再次fork,防止进程再次打开控制终端
    pid = fork();
    if (pid > 0) {
        //退出
        exit(0);                                                                                                                                                                                                                                                  
    }

    //设定工作目录为根目录
    chdir("/");

    //关闭所有打开的文件描述符
    for (i = 0; i < fd_limit.rlim_max; ++i)
        close(i);

    //将标准输入输出、标准错误重定向到/dev/null
    fd0 = open("/dev/null", O_RDWR);
    fd1 = dup(0);
    fd2 = dup(0);

    //运行程序
    //TODO...
    while (1) {
        sleep(1);
    }
}

运行程序后通过ps命令来查看:

可以看出。a.out的进程组ID是9026,但是没有一个活动进程的id是9026。a.out现在是一个孤儿进程在运行着。同时它也不是session leader,不会获取到控制终端。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值