守护进程

一、简介

守护进程也叫做精灵进程,是一种特殊的进程。它的生命周期很长,常常在系统引导装入时启动,在系统关闭的时候才结束。由于他们没有控制终端,所以他们是在后台运行的。在Linux系统上存在很多守护进程,这些守护进程执行了日常事务活动。同时大多数服务器也是用守护进程实现的。

我们可以用命令ps axj 查看系统中的进程。

其中a表示不但列出当前用户进程,还列出所有其他用户进程,x表示列出所有有或者没有控制终端的进程,j表示与作业相关的进程。
这里写图片描述

我们可以看到其中TPGID中为-1的全部都是守护进程,也就是没有控制终端的进程。

同时我们可以发现其他内核进程的父进程都是2号进程,2号进程名字叫做kthreadd,它是一种特殊的内核进程,用来创建其他内核进程。

init进程的父进程和kthreadd进程的父进程都是0号进程,但是init是一个系统守护进程,除了其他工作外,主要负责各运行层次特定的系统服务。

二、创建守护进程

(1)setsid函数

创建守护进程的最关键的一步是调用setsid函数创建一个新的会话,并成为会话组长。

#include <unistd.h>
pid_t setsid(void);
  • 返回值
    成功返回创建会话的id(也就是当前进程的id),出错返回-1

  • 成功调用这个函数的结果是创建一个新的会话,当前进程成为会话首进程,当前进程的id就是会话的id

  • 创建一个新的进程组,当前进程成为进程组的首进程,当前进程id就是进程组的id。
  • 如果当前进程原有一个控制终端,则它会失去这个控制终端,成为一个没有控制终端的进程。所谓的失去控制终端指的是原来的控制终端仍然是打开的,可以读写,但是之时一个普通的打开文件而不是控制终端了。

总结来说就是一句话:

将当前调用进程设置为独立进程组,并且设置当前调用进程称为一个独立的会话,并于该控制终端去掉关联

(2)创建守护进程的规则

创建守护进程的方法一般就是有下面六个规则:

  1. 调用umask将文件模式创建屏蔽字设置为0.
    因为如果守护进程需要进行创建文件,那么它可能需要设置特定的权限,如果是继承的来的文件创建屏蔽字可能会被设置为拒绝某些权限。

  2. 调用fork()函数,然后父进程exit()
    首先保证当前进程不能是组长。所以要先fork让子进程执行,父进程直接退出(保证接下来执行的进程绝对不是该进程组的组长)

  3. 调用setsid创建一个新的会话。

  4. 将当前工作目录更改为根目录
    如果当前目录中的其中一个被删除掉,则程序跑不起来

  5. 关闭不再需要的文件描述符
    使守护进程不再具有从其父进程继承来的任何文件描述符。

  6. 忽略SIGCHLD信号

(3)为什么有人fork2次

1、第一次fork的作用是让shell 认为本条命令 已经终止,不用挂在终端输入上。还有一个作用是为后面setsid服务。setsid的调用者不能是进程组组长(group leader). 此时父进程是进程组组长。

2、setsid() 是本函数最重要的一个调用。它完成了daemon函数想要做的大部分事情。调用完整个函数。子进程是会话组长(sid==pid),也是进程组组长(pgid == pid),并且脱离了原来控制终端。到了这一步,基本上不管控制终端如何怎么样。新的进程都不会收到那些信号。

3、经过前面2个步骤,基本想要做的都做了。第2次fork不是必须的。也看到很多开源服务没有fork第二次。fork第二次主要目的是。防止进程再次打开一个控制终端。因为打开一个控制终端的前提条件是该进程必须是会话组长。再fork一次,子进程ID != sid(sid是进程父进程的sid)。所以也无法打开新的控制终端。

(4)deamon函数创建守护进程

其实,linux提供了daemon函数用于创建守护进程

#include
int daemon(int nochdir, int noclose);

1. daemon()函数主要用于希望脱离控制台,以守护进程形式在后台运行的程序。

2. 当nochdir为0时,daemon将更改进程的根目录为root(“/”)。

3. 当noclose为0是,daemon将进城的STDIN, STDOUT, STDERR都重定向到/dev/null。

daemon的实现大致如下:

int daemon( int nochdir,  int noclose )
{
   pid_t pid;
   if ( !nochdir && chdir("/") != 0 ) //如果nochdir=0,那么改变到"/"根目录
       return -1;

   if ( !noclose ) //如果没有noclose标志
   {
        int fd = open("/dev/null", O_RDWR); 
        if ( fd  <  0 )
            return -1;
       /* 重定向标准输入、输出、错误 到/dev/null,
键盘的输入将对进程无任何影响,进程的输出也不会输出到终端
*/

dup(fd, 0);
dup(fd, 1);
dup(fd, 2);     

close(fd);
}

   pid = fork();  //创建子进程.
   if (pid  <  0)  //失败
      return -1;
   if (pid > 0)
       _exit(0); //返回执行的是父进程,那么父进程退出,让子进程变成真正的孤儿进程.

//创建的 daemon子进程执行到这里了
   if ( setsid()  < 0 )   //创建新的会话,并使得子进程成为新会话的领头进程
      return -1;
   return 0;  //成功创建daemon子进程
}

使用实例:

int main()
{
    daemon(1, 1)//参数根据需求确定
    /*  在这里添加你需要在后台做的工作代码  */
}

如何杀死这样的进程:

通过ps+grep找到对应的后台进程,使用kill命令将进程杀死;也可创建shell脚本对进程的启动、关闭、重启进行自动管理

deamon()可以直接将程序精灵化,不一样的是他把0、1、2设置为null,这个设备是字符设备,null相当于一个无底洞,写入的任何数据都会被丢弃
受否需要更改目录,是否需要关闭文件描述符的重定向

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值