博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
创建守护进程以及为什么fork两次
阅读量:4166 次
发布时间:2019-05-26

本文共 2405 字,大约阅读时间需要 8 分钟。

守护进程

守护进程也叫精灵进程(Daemon),它在后台运行,独立于控制终端并周期性地执行某种任务或等待某些事情发生,一般用于C/S 服务中,在服务端监听端口,等待客户端的连接,如果有客户端发出连接,则fork出一个子进程去响应,而守护进程继续监听。

步骤

创建一个守护进程一般有如下几步:

  1. 调用umask(0),将文件模式创建屏蔽字改为0
  2. 调用fork(),父进程退出,目的是: 
    • 父进程终止可以让shell切换到前台继续等待用户输入命令
    • 保证子进程不是一个进程组的组长进程
  3. 在子进程中调用 ssetsid(),会导致如下结果: 
    • 调用进程成为新会话的首进程
    • 调用进程成为一个进程组的组长进程
    • 调用进程脱离终端,即没有控制终端
  4. 调用chdir("/")将当前工作目录更改为根目录,目的是防止用户改动目录,从而影响进程的运行
  5. 关闭相应的文件描述符,原因是,当前进程已经脱离终端,防止产生并不需要的交互作用,还有一个原因,用fork新建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读或写,但它们一样消耗系统资源,所以需要关闭
  6. 忽略SIGCHLD信号,该动作是为了,子进程退出的时候资源能够被系统所回收,防止出现僵尸进程 
    关于忽略SIGCHLD信号:父进程fork出一个进程,然后父进程立马退出, 在子进程的执行流中, 设置 signal(SIGCHLD, SIG_IGN)。 此时原来的父进程已经不存在,而子进程脱离终端,自成会话, 相当于 子进程就变成了新会话的父进程, 又因为此时它是守护进程, 代码已经写好了, 而在它的执行流中很有可能会再次 fork 产生新的子进程, 此时如果它fork出的子进程退出的话,必须要回收资源,可以使用wait 或者 waitpid, 但是这样会导致守护进程阻塞, 所以 在守护进程中捕捉SICCHLD信号, 并忽略, 可以让守护进程不阻塞,并回收子进程资源。
#include 
#include
#include
#include
#include
#include
#include
void create_daemon(){ //1. 将文件模式创建屏蔽字设置为0 umask(0); //2. 调用fork 父进程退出 // 作用:确保子进程不是一个进程组的组长 pid_t id = 0; if ( (id = fork()) < 0) { perror("fork"); exit(2); } if(id > 0 ) { // father exit(3); } // 3.调用setsid 创建一个新的会话 setsid(); //4. 将当前工作目录改为更目录 chdir("/"); //5. 关闭不需要的文件描述符 close(0); close(1); close(2); //6. 忽略SIGCHLD 信号 signal(SIGCHLD, SIG_IGN); // 守护进程逻辑 while(1) { // Do something }}int main(){ create_deamon(); return 0;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
最重要的一步就是 调用setsid
#include 
pid_t setsid(void);成功返回新创建的会话 ID ,出错返回-1
  • 1
  • 2
  • 3
  • 4

调用此函数之前,当前进程不允许是进程组的Leader,如果是则出错返回-1。 

所以可以先fork创建子进程,然后在调用setsid,fork出的子进程和父进程在同一个进程组中,进程组的Leader必然是父进程,所以可以保证该函数会执行成功。

该函数成功的表现: 

- 创建一个新的会话,当前进程成为会话Leader,SID = PID 
- 创建一个新的进程组,当前进程称为进程组的Leader,GID = PID 
- 当前进程与控制终端脱离关系

下面内容于修改:2017年6月19日19:00:10

有时候创建守护进程时会fork 两次,原因如下:

  1. 彻底和控制终端脱离关系。
  2. 防止父进程运行很长时间,而第一次fork出的子进程成为僵尸进程(少见)。

对于原因1的解释:

APUE 第13 章中关于守护进程的介绍,作者在小字部分提到,在System V 环境下,有人建议 fork 两次,为了保证守护进程不是会话首进程,(因为会话首进程可以获取到控制终端,而非会话收进程无法打开控制终端),避免它取得控制终端。所以说第二次fork 可以在你确保不获取控制终端的情况下略之。

对于原因2的解释:

这里有一个假定,父进程生成守护进程后,还有自己的事要做,它的人生意义并不只是为了生成守护进程。这样,如果父进程fork一次创建了一个守护进程,然后继续做其它事时阻塞了,这时守护进程一直在运行,父进程却没有正常退出。如果守护进程因为正常或非正常原因退出了,就会变成ZOMBIE进程。 

如果fork两次呢?父进程先fork出一个儿子进程,儿子进程再fork出孙子进程做为守护进程,然后儿子进程立刻退出,守护进程被init进程接管,这样无论父进程做什么事,无论怎么被阻塞,都与守护进程无关了。所以,fork两次的守护进程很安全,避免了僵尸进程出现的可能性。

转载地址:http://xzexi.baihongyu.com/

你可能感兴趣的文章
C++程序员技术需求规划(发展方向)
查看>>
TinyXml2解析xml用法例子
查看>>
Linux 虚拟内存和物理内存
查看>>
产品和技术,你选对了吗?
查看>>
哈希表(Hash Table)-哈希概述
查看>>
Filebench的安装及使用
查看>>
Ubuntu下 E: Could not get lock /var/lib/apt/lists/lock - open (11: Recource temporarily unavailable)
查看>>
Linux-mmap映射物理内存到用户空间
查看>>
Ext4文件系统三种日志模式——journal、ordered、writeback
查看>>
Linux挂载ext4根文件系统为journal模式
查看>>
linux内核引导参数解析及添加
查看>>
长短期记忆人工神经网络(LSTM)及其tensorflow代码应用
查看>>
长短期记忆人工神经网络(LSTM)网络学习资料
查看>>
运行网络中搜寻到的python程序代码——以长短期记忆人工神经网络(lstm)python代码为例
查看>>
闪存文件系统(Flash File System)
查看>>
WinMIPS64工具进行MIPS指令集实验(一)
查看>>
WinMIPS64工具进行MIPS指令集实验(二)
查看>>
Linux上快速入门英特尔Optane DC Persistent Memory Module的配置与使用
查看>>
Intel Optane DC Persistent Memory Module (PMM)详解
查看>>
Ubuntu 18.04安装英特尔Optane DC Persistent Memory Module配置工具ipmctl
查看>>