[原文地址:https://blog.ti-node.com/blog...]

其实前面是谈过一次daemon进程的,但是并涉及过多原理,但是并不影响使用。今天打算说说关于daemon进程更多的二三事,本质上说,如果你仅仅是简单实现利用一下daemon进程,这个不看也是可以的。

杠真,*NIX真是波大精深,越是深入看越是发现它的diao。原理往往都是枯燥的,大家都不爱看,但这并不影响我坚持写自己对这些东西的理解。

三个概念,理(bei)解(song)一下:

  • 进程组。一坨相关的进程可以组成一个进程组,每个进程组都会有一个组ID(正整数),每个进程组都会有一个组长进程,组长进程的ID等于进程组ID。组长进程可以创建新的进程组以及该进程组中的其他进程。一个进程组的是有生命周期的,即便是组长进程挂了,只有组里还有其他的活口,那就就算该进程组依然存活,只有到组里最后一个活口也挂了,那真的就是彻底没了。
  • 会话。一坨相关的进程组组成了一个会话。在*NIX下,是通过setsid()创建一个新的会话。但是值得注意的是,组长进程不能创建会话,简单理解就是在组长进程中,执行setsid函数会报错,这点很重要。所以一般都是组长进程执行fork,然后主进程退出,因为子进程的进程ID是新分配的,而子进程的进程组ID是继承父进程的,所以子进程就注定不可能是组长进程,从而可以确保子进程中一定可以执行setsid函数。在执行setsid函数时候,一般会发生下面三个比较重要的事情:

    • 该进程会创建一个新的进程组,该进程为进程组组长(或者你可以认为这是一种提升)
    • 该进程会创建一个会话组并成为该会话的会话首进程(会话首进程就是创建该会话的进程)
    • 该进程会失去控制终端。如果该进程本来就没有控制终端,则罢了(liao)。如果有,那么该进程也将脱离该控制终端,与之失去联系。
  • 控制终端。每个会话可能会拥有一个控制终端(看着比较玄学,你可以暂时理解为就一个那种黑乎乎的命令行窗口),建立与控制终端连接的会话首进程叫做控制进程。

结合Linux命令ps来查看一下上述几个概念的恩怨情仇,我们看下我们常用的 ps -o pid,ppid,pgid,sid,comm | less 执行结果:

第一行分别是PID,PPID,PGID,SID,COMMAND,依次分别是进程ID,该进程父进程ID,进程组ID,会话ID,命令。

通过最后一列,我们知道第二行就是bash也就是bash shell进程,其进程ID为15793,其父进程为13291,进程组ID为15793,会话ID也会15793,结合前面的概念,我们可以知道bash shell就是该进程组组长。

第三行则是ps命令的进程,其进程ID为15816,他是由于bash进程fork出来的,所以他的父进程ID为15793,然后是他所属的组ID为15816,所属的会话ID依然是15793。

最后一行是less命令的进程,其进程ID为15817,他也是由bash进程fork出来的,所以他的父进程ID也为15793,然后是他所属的组ID为15816,所属的会话ID依然是15793。

简单总结一下:

  • 上述三个进程一共形成了两个进程组,bash自己为一组,组ID为15793,组长进程为bash自己 ; ps和less为一组,组ID为15816,组长进程为ps进程
  • 上述三个进程属于同一个会话,会话ID为15793,会话首进程为bash进程(待定)
  • 控制终端则为打开的terminal窗口,与之关联的控制进程则为bash进程

通过这么一顿分析,是不是感觉可以接受点儿了?然后是,叨逼叨了半天这个,跟daemon进程有啥子关系?
啦啦啦,下面通过引入代码直接分析:

$pid = pcntl_fork();
if( $pid < 0 ){exit('fork error.');
} else if( $pid > 0 ) {// 主进程退出exit();
}
// 子进程继续执行// 最关键的一步来了,执行setsid函数!
if( !posix_setsid() ){exit('setsid error.');
}// 理论上一次fork就可以了
// 但是,二次fork,这里的历史渊源是这样的:在基于system V的系统中,通过再次fork,父进程退出,子进程继续,保证形成的daemon进程绝对不会成为会话首进程,不会拥有控制终端。$pid = pcntl_fork();
if( $pid  < 0 ){exit('fork error');
} else if( $pid > 0 ) {// 主进程退出exit;
}// 子进程继续执行// 啦啦啦,啦啦啦,啦啦啦,已经变成daemon啦,开心
cli_set_process_title('testtesttest');
// 睡眠1000000,防止进程执行完毕挂了
sleep( 1000000 );

将上述文件保存为daemon.php,然后php daemon.php执行,使用 ps -aux | grep testte ,如果没有什么大问题你应该就可以看到这个进程在后台跑了。

所以为什么第一步要先fork呢?因为调用setsid的进程不可以是组长进程(篇头的枯燥知识需要了吧?),所以必须fork一次,然后将主进程直接退出,保留子进程。因为子进程一定不会是组长进程,所以子进程可以调用setsid。调用setsid则会产生三个现象:创建一个新会话并成为会话首进程,创建一个进程组并成为组长进程,脱离控制终端。

啦啦啦,明白为啥篇头那一坨枯燥的知识是为了什么吧?

然而,实际上,上述代码仅仅完成了一个标准daemon的80%,还有20%需要我们进一步完善。那么,需要完善什么呢?我们修改一下上述代码,让程序在最终的代码段中执行一些文本输出:

$pid = pcntl_fork();
if( $pid < 0 ){exit('fork error.');
} else if( $pid > 0 ) {// 主进程退出exit();
}
// 子进程继续执行// 最关键的一步来了,执行setsid函数!
if( !posix_setsid() ){exit('setsid error.');
}// 理论上一次fork就可以了
// 但是,二次fork,这里的历史渊源是这样的:在基于system V的系统中,通过再次fork,父进程退出,子进程继续,保证形成的daemon进程绝对不会成为会话首进程,不会拥有控制终端。$pid = pcntl_fork();
if( $pid  < 0 ){exit('fork error');
} else if( $pid > 0 ) {// 主进程退出exit;
}// 子进程继续执行// 啦啦啦,啦啦啦,啦啦啦,已经变成daemon啦,开心
cli_set_process_title('testtesttest');
// 循环1000次,每次睡眠1s,输出一个字符test
for( $i = 1; $i <= 1000; $i++ ){sleep( 1 );echo "test".PHP_EOL;
}

将文件保存为daemon.php,然后php daemon.php执行文件,嗯,是不是有怪怪的现象,大概类似于下图:

即便你按Ctrl+C都没用,终端在不断输出test,唯一办法就是关闭当前终端窗口然后重新开一个,然而,这并不符合社会主义主流价值观。所以,我们还要解决标准输出和错误输出,我们的daemon程序不可以再将终端窗口当作默认的标准输出了。

其次是将当前工作目录修改更改为根目录。不然可能就会出现下面这样一个问题,就是如果父进程是的工作目录是一个挂载的目录,那么子进程会继承父进程的工作目录,当子进程已经daemon化后就会出现一个悲剧:那就是虽然原来挂载的目录已经不用了,但是却无法用umount卸载,非常悲剧。

最后一个问题是,要在第一次fork后设置umask(0),避免权限上的一些问题。所以较为完整的代码如下:
// 设置umask为0,这样,当前进程创建的文件权限则为777
umask( 0 );$pid = pcntl_fork();
if( $pid < 0 ){exit('fork error.');
} else if( $pid > 0 ) {// 主进程退出exit();
}
// 子进程继续执行// 最关键的一步来了,执行setsid函数!
if( !posix_setsid() ){exit('setsid error.');
}// 理论上一次fork就可以了
// 但是,二次fork,这里的历史渊源是这样的:在基于system V的系统中,通过再次fork,父进程退出,子进程继续,保证形成的daemon进程绝对不会成为会话首进程,不会拥有控制终端。$pid = pcntl_fork();
if( $pid  < 0 ){exit('fork error');
} else if( $pid > 0 ) {// 主进程退出exit;
}// 子进程继续执行// 啦啦啦,啦啦啦,啦啦啦,已经变成daemon啦,开心
cli_set_process_title('testtesttest');
// 一般服务器软件都有写配置项,比如以debug模式运行还是以daemon模式运行。如果以debug模式运行,那么标准输出和错误输出大多数都是直接输出到当前终端上,如果是daemon形式运行,那么错误输出和标准输出可能会被分别输出到两个不同的配置文件中去
// 连工作目录都是一个配置项目,通过php函数chdir可以修改当前工作目录
chdir( $dir );

[原文地址:https://blog.ti-node.com/blog...]

PHP多进程初探 --- 再次谈daemon进程相关推荐

  1. linux daemon 函数,Daemon 进程的创建

    原标题:Daemon 进程的创建 作者:Liao Tonglang https://quant67.com/post/linux/daemon_create.html Daemon 进程生命周期长且在 ...

  2. 什么是多进程-多线程-多协程 ----进程和多线程

    进程和多线程 进程通信 进程的组成 进程之间的通信方式 管道 匿名管道 命名管道 信号 信号量 共享内存 socket 消息队列 进程线程 系统知识 cpu时间片(抽象概念) 线程: 进程: 进程与线 ...

  3. Linux中创建Daemon进程的三种方法

    Linux中创建Daemon进程的三种方法 什么是daemon进程? Unix/Linux中的daemon进程类似于Windows中的后台服务进程,一直在后台运行运行,例如http服务进程nginx, ...

  4. daemon进程(转)

    转自:http://www.pythoner.cn/home/blog/double-fork-when-creating-daemon/Daemon进程 守护进程(daemon)是指在UNIX或其他 ...

  5. Linux系统编程——Daemon进程

    目录 Daemon进程介绍 前提知识 Daemon进程的编程规则 Daemon进程介绍 Daemon运行在后台也称作"后台服务进程". 它是没有控制终端与之相连的进程.它独立与控制 ...

  6. 什么是多进程-多线程-多协程 ----进程和多进程

    进程和多进程 进程和多进程 进程 概念: 组成: 基本状态: 创建: 如何创建子进程? OS.fork 创建子进程 返回值 os.getpid():获取进程的进程号 os.getppid():获取父进 ...

  7. 关于 PM2 Daemon 进程,内存爆涨的问题分析及解决

    PM2 Daemon 进程内存占用持续上涨,很大原因可能不是本身的原因,大概率是使用PM2启动启动的服务内存存在问题,而导致PM2 Daemon 进程内存持续上涨. 解决问题的思路:解决 Node 中 ...

  8. linux daemon 进程

    1 什么是daemon 进程 daemon 进程又名守护进程,因为没有控制终端,所以说他们运行在后台.守护进程一般在系统启动时开始运行,在系统关闭时消亡. 2 守护进程特征 守护进程的进程名一般都以d ...

  9. 初探Nginx架构之进程模型与事件处理机制

    from http://tengine.taobao.org/book/chapter_2.html#connection http://blog.csdn.net/yankai0219/articl ...

最新文章

  1. 无需「域外」文本,微软:NLP就应该针对性预训练
  2. DSP中LOG_printf()和printf()区别
  3. docker hub下载慢解决方法 使用daocloud的mirror
  4. js事件技巧方法整合
  5. 雨林木风系统md5值_微软停止Windows系列新系统开发,珍惜你的Win10吧。。。
  6. 用位操作代替求余操作
  7. 内网用户之间使用MSN Messenger快速传送文件的小窍门
  8. VC系统扫雷游戏外挂源代码程序下载(转帖
  9. 牛顿迭代法(Newton#39;s Method)
  10. xodo上的笔记不见了_一起来“终极笔记名场面批发市场”进货吗
  11. 密码学常用的算法填充模式_密码学的操作模式
  12. 风控策略分析中最重要的五步心法
  13. mfc界面的onvscroll没反应_电脑小技巧之360安全卫士卸载不掉怎么办?只因一个开关没打开...
  14. atom常用的插件整理
  15. 太强了!一个基于 Redis 的限流系统的设计!
  16. linux 卸载pip3,【python】centos7安装卸载python3,pip3
  17. 时间管理表 - 《待办清单列表》
  18. 笔记本连接android手机屏幕,实现手机、电脑屏幕共享的7个步骤
  19. 变种水仙花数 - Lily Number
  20. [转][酷酷的滕]我爱你语录

热门文章

  1. NBR100多IP出口解决方案的配置方法
  2. 系统管理-第2部分 范围,方法和元素
  3. 架构探险笔记11-与Servlet API解耦
  4. CheckStyle检查规则模板说明
  5. linux基础--awk文本分析工具详解
  6. Oracle常用诊断事件清单
  7. Windows Home Server 常见问题
  8. Unity教程之-Unity3d中针对Android Apk的签名验证(C#实现)
  9. 5 simple types and 6 false values in JavaScript
  10. SQL Server 索引基础知识(10)----Join 时的三种算法简介