守护进程

本文详细介绍了守护进程(Daemon)的概念,特点及创建方法。守护进程是独立于控制终端且周期性执行任务的特殊进程,常见于Linux服务器。文章阐述了守护进程与普通进程的区别,如何通过fork和setsid函数创建守护进程,以及使用daemon函数简化创建过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1)守护进程基本概念

守护进程也称精灵进程(Daemon),是运⾏在后台的⼀种特殊进程。它独⽴于控制终端并且周期性地执⾏某种任务或等待处理某些发⽣的事件。守护进程是⼀种很有⽤的进程。Linux的⼤多数服务器就是⽤守护进程实现的。⽐如淘宝啦,百度,支付宝这些等等。
Linux系统启动时会启动很多系统服务进程,这些系统服务进程没有控制终端,不能直接和⽤户交互。其它进程都是在⽤户登录或运⾏程序时创建,在运⾏结束或⽤户注销时终⽌,但系统服务进程(守护进程)不受⽤户登录注销的影响,它们⼀直在运⾏着。这种进程有⼀个名称叫守护进程(Daemon)。

2)守护进程的特点

  • 自成进程组、会话、作业与控制终端脱离关联
  • 守护进程的父进程是1号init进程
  • 守护进程的命令一般是以字符d结尾的
  • 守护进程不可以存在内存泄漏或者文件描述符等泄漏

3)创建

1.使用命令:ps axj | more 查看系统中的所有进程
在这里插入图片描述

凡是TPGID⼀栏写着-1的都是没有控制终端的进程,也就是守护进程。
在COMMAND⼀列⽤[]括起来的名字表⽰内核线程,这些线程在内核⾥创建,没有⽤户空间代码,因此没有程序⽂件名和命令⾏, 通常采⽤以k开头的名字,表⽰Kernel。
init进程我们已经很熟悉了,udevd负责维护/dev目录下的 设备⽂件,acpid负责电源管理,syslogd负责维护/var/log下的⽇志⽂件可以看出,守护进程通常采⽤以d结尾的名字,表⽰Daemon。

2.setsid函数
setsid函数创建⼀个新的Session,并成为Session Leade。

#include <unistd.h>
pid_t setsid(void);
该函数调用成功返回新创建的Session的id(其实也就是当前进程的id),出错返回-1.

调⽤这个函数之前,当前进程不允许是进程组的Leader,否则该函数返回-1。要保证当前进程不是进 程组的Leader也很容易,只要先fork再调⽤setsid就⾏了。fork创建的⼦进程和⽗进程在同⼀个进程组中,进程组的
Leader必然是该组的第⼀个进程,所以⼦进程不可能是该组的第⼀个进程,在⼦进程中调⽤setsid就不会有问题了。(所以所有的守护进程父进程都是init进程,都是孤儿进程)。

成功调⽤该函数的结果是:

创建⼀个新的Session,当前进程成为Session Leader,当前进程的id就是Session的id。
创建⼀个新的进程组,当前进程成为进程组的Leader,当前进程的id就是进程组的id。
如果当前进程原本有⼀个控制终端,则它失去这个控制终端,成为⼀个没有控制终端的进程。所谓失去控制终端是指,原来的控制终端仍然是打开的,仍然可以读写,但只是⼀个普通的打开⽂件⽽不是控制终端了。

3.创建守护进程的步骤

1)调用umask将文件模式创建屏蔽字设置为0;//umask必须清0,否则创建文件受系统默认权限的影响

  • 文件权限掩码是屏蔽掉文件权限中的对应位。由于使用fork()函数新创建的子进程继承了父进程的文件权限掩码,在子进程权限与父进程权限不同时,就带来了麻烦,因此在子进程中要把文件的权限掩码设置为0,即在此时有最大的权限,这样可以大大增强守护进程的灵活性。

2)调用fork,父进程退出(exit)。
如果该守护进程是作为一个简单的shell命令启动的,那么该父进程终止使得shell认为该命令已经执行完毕。保证子进程不是一个进程组的组长。
3)调用setsid创建一个新会话。
4)在子进程中调用chdir函数,让根目录“/”成为子进程的工作目录。
5)在子进程中关闭任何不需要的文件描述符。
6)忽略SIGCHLD信号:signal(SIGCHLD,SIG_IGN)

代码如下:

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>

int Creat_Daemon()
{
  //1.调用umask将文件模式创建屏蔽字设置为0
  umask(0);
  //2.调用fork,父进程退出(exit)
  pid_t id =fork();
  if(id > 0)
  {
    exit(1);
  }
  else if(id == 0)
  {
    //3.创建一个新的会话
    setsid();
  //4.将新建会话的工作目录改为根目录
    if(chdir("/") < 0){
      perror("chdir");
      return;
    }
     //5.关闭从父进程继承来的文件
     close(0);
     close(1);
     close(2);
     //忽略SIGSHLD信号
     signal(SIGCHLD,SIG_IGN);
  }

  
}


  int main()
  {
    Creat_Daemon();
    while(1);
    return 0;
  }

4.使用daemon函数创建守护进程

int daemon(int nochdir,int noclose)
参数:
1.nochdir:为0时,当前目录变为根目录,否则不变。
2.noclose为0时,表示将标准输入,标准输出,错误输出都重定向为/dev/null,即不输出信息,否则不变。

返回值:daemon()调用了fork(),如果fork成功,那么父进程就调用_exit(2)退出,所以看到的错误信息全部都是子进程产生的,如果成功函数就返回0,否则返回-1并设置为errno。


#include<stdio.h>
#include<unistd.h>

int main()
{
   daemon(0,0);
   while(1);
}

上述代码运行后便产生了一个守护进程。
在这里插入图片描述

5.有些人使用fork两次创建守护进程的原因

  • 第一次fork:这里第一次fork的作用就是让shell认为这条命令已经终止,不用挂在终端输入上;也为了后面使用setsid,因为要调用setsid函数的进程不能是进程组组长,如果不fork子进程,那么此时的父进程是进程组的组长,无法调用setsid。所以到这里子进程便成为了一个新会话组的组长。
  • 第二次fork:第二次fork不是必须的。而fork两次进程是为了防止进程再次打开一个控制终端。因为打开一个控制终端的前台条件是该进程必须是会话组长。再fork一次,子进程ID!=sid(sid是父进程的sid).所以也无法打开新的控制终端。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值