首页 » SEO优化 » linuxphppcntl技巧_PHP编程PHP是若何实现守护进程的

linuxphppcntl技巧_PHP编程PHP是若何实现守护进程的

访客 2024-11-07 0

扫一扫用手机浏览

文章目录 [+]

下面我们如何利用PHP从零开始实现一个大略的守护进程。

守护进程的定义

在开始代码实现之前,我们先熟习一下守护进程的定义。
在Wikipedia上是这样说的:在一个多任务的电脑操作系统中,守护进程是一种在后台实行的电脑程序。
此类程序会被以进程的形式初始化。
守护进程程序的名称常日以字母“d”结尾:例如,syslogd便是指管理系统日志的守护进程。

linuxphppcntl技巧_PHP编程PHP是若何实现守护进程的

常日,守护进程没有任何存在的父进程(即PPID=1),且在UNIX系统进程层级中直接位于init之下。
守护进程程序常日通过如下方法使自己成为守护进程:对一个子进程运行fork,然后使其父进程立即终止,使得这个子进程能在init下运行。
这种方法常日被称为“脱壳”。

linuxphppcntl技巧_PHP编程PHP是若何实现守护进程的
(图片来自网络侵删)

这里把稳到,守护进程有如下特色:

没有终端后台运行父进程 pid 为1

想要查看运行中的守护进程可以通过 ps -ax 或者 ps -ef 查看,个中 -x 表示会列出没有掌握终真个进程。

守护进程的实现

PHP实现守护进程须要进程2次fork操作和一次setsid操作,fork操作须要借助于pcntl扩展,setsid操作须要借助于posix扩展。

在开始之前我们须要把稳:

通过二次 pcntl_fork() 以及 posix_setsid 让主进程分开终端通过 pcntl_signal() 忽略或者处理 SIGHUP 旗子暗记多进程程序须要通过二次 pcntl_fork() 或者 pcntl_signal() 忽略 SIGCHLD 旗子暗记防止子进程变成 Zombie 进程通过 umask() 设定文件权限掩码,防止继续文件权限而来的权限影响功能将运行进程的 STDIN/STDOUT/STDERR 重定向到 /dev/null 或者其他流上

如果要做的更好,还须要把稳:

如果通过 root 启动,运行时改换到低权限用户身份及时 chdir() 防止操作缺点路径多进程程序考虑定时重启,防止内存透露

关注点

1、fork操作

fork操作即是调用fork系统,fork 系统调用用于复制一个与父进程险些完备相同的进程,新天生的子进程不同的地方在于与父进程有着不同的 pid 以及有不同的内存空间,根据代码逻辑实现,父子进程可以完成一样的事情,也可以不同。
子进程会从父进程中继续比如文件描述符一类的资源。

PHP 中的 pcntl 扩展中实现了 pcntl_fork() 函数,用于在 PHP 中 fork 新的进程。

2、setsid操作

setsid操作即是setsid 系统调,setsid 系统调用则用于创建一个新的会话并设定进程组 id。

这里有几个观点:会话,进程组。

在 Linux 中,用户登录产生一个会话(Session),一个会话中包含一个或者多个进程组,一个进程组又包含多个进程。
每个进程组有一个组长(Session Leader),它的 pid 便是进程组的组 id。
进程组长一旦打开一个终端,这一个终端就被称为掌握终端。
一旦掌握终端发生非常(断开、硬件缺点等),会发出旗子暗记到进程组组长。

后台运行程序(如 shell 中以&结尾实行指令)在终端关闭之后也会被杀去世,便是没有处理好掌握终端断开时发出的SIGHUP旗子暗记,而SIGHUP旗子暗记对付进程的默认行为则是退出进程。

调用 setsid 系统调用之后,会让当前的进程新建一个进程组,如果在当提高程中不打开终真个话,那么这一个进程组就不会存在掌握终端,也就不会涌现由于关闭终端而杀去世进程的问题。

PHP 中的 posix 扩展中实现了 posix_setsid() 函数,用于在 PHP 中设定新的进程组。

3、孤儿进程

父进程比子进程先退出,子进程就会变成孤儿进程。
init 进程会收养孤儿进程,即孤儿进程的 ppid 变为 1。

二次 fork 的浸染

首先,setsid 系统调用不能由进程组组长调用,会返回-1。

二次 fork 操作的样例代码如下:

假定我们在终端中实行运用程序,进程为 a,第一次 fork 会天生子进程 b,如果 fork 成功,父进程 a 退出。
b 作为孤儿进程,被 init 进程托管。

此时,进程 b 处于进程组 a 中,进程 b 调用 posix_setsid 哀求天生新的进程组,调用成功后当提高程组变为 b。

此时进程 b 事实上已经分开任何的掌握终端,例程:

实行程序之后:

从 ps 的结果来看,process_b 的 TTY 已经变成了 ?,即没有对应的掌握终端。

代码走到这里,彷佛已经完成了功能,关闭终端之后 process_b 也没有被杀去世,但是为什么还要进行第二次 fork 操作呢?

这是为了防止实际的事情的进程主动关联或者意外关联掌握终端,再次 fork 之后天生的新进程由于不是进程组组长,是不能申请关联掌握终真个。

综上,二次 fork 与 setsid 的浸染是天生新的进程组,防止事情进程关联掌握终端。

SIGHUP 旗子暗记处理

一个进程收到 SIGHUP 旗子暗记的默认动作是结束进程。

而 SIGHUP 会在如下情形下发出:

掌握终端断开,SIGHUP 发送到进程组组长进程组组长退出,SIGHUP 会发送到进程组中的前台进程SIGHUP 常被用于关照进程重载配置文件(APUE 中提及,daemon 由于没有掌握终端,被认为不可能会收到这一个旗子暗记,以是选择复用)

由于实际的事情进程不在前台进程组中,而且进程组的组长已经退出并且没有掌握终端,不处理正常情形下当然也没有问题,然而为了防止有时的收到 SIGHUP 导致进程退出,也为了遵照守护进程程序设计的老例,还是应该处理这一旗子暗记。

Zombie 进程

子进程先于父进程退出,父进程没有调用 wait 系统调用处理,进程变为 Zombie 进程。

子进程先于父进程退出时,会向父进程发送 SIGCHLD 旗子暗记,如果父进程没有处理,子进程也会变为 Zombie 进程。

Zombie 进程会占用可 fork 的进程数,Zombie 进程过多会导致无法 fork 新的进程。

此外,Linux 系统中 ppid 为 init 进程的进程,变为 Zombie 后会由 init 进程回收管理。

Zombie 进程的处理

从 Zombie 进程的特点,对付多进程的daemon,可以通过两个路子办理这一问题:

父进程处理 SIGCHLD 旗子暗记让子进程被 init 接管

父进程处理旗子暗记无需多说,注册旗子暗记处理回调函数,调用回收方法即可。

对付让子进程被 init 接管,则可以通过2次 fork 的方法,让第一次 fork 出的子进程 a 再 fork 呈现实的事情进程 b,让 a 先行退出,使得 b 成为孤儿进程,这样就能被 init 进程托管了。

umask

umask 会从父进程中继续,影响创建文件的权限。

PHP 手册上提到:

umask() 将 PHP 的 umask 设定为 mask & 0777 并返回原来的 umask。
当 PHP 被作为做事器模块利用时,在每个要求结束后 umask 会被规复。

如果父进程的 umask 没有设定好,那么在实行一些文件操作时,会涌现意想不到的效果:

以是,为了担保每一次都能按照预期的权限操作文件,须要置0 umask 值。

重定向0/1/2

这里的0/1/2分别指的是 STDIN/STDOUT/STDERR,即标准输入/输出/缺点三个流。

首先来看一个示例:

上述代码险些完成了文章最开始部分提及的各个方面,唯一不同的是没有对标准流做处理。
通过 php not_redirect_std_stream_daemon.php 指令也能让程序在后台进行。

在 sleep 的间隙,关闭终端,会创造进程退出。

通过 strace 不雅观察系统调用的情形:

创造发生了 EIO 缺点,导致进程退出。

缘故原由很大略,即我们编写的 daemon 程序利用了当时启动时终端供应的标准流,当终端关闭时,标准流变得不可读不可写,一旦考试测验读写,会导致进程退出。

办理方案

APUE 13.3中提到过一条编程规则(第6条):

某些守护进程打开 /dev/null 期间具有文件描述符0、1和2,这样,任何一个视图读标准输入、写标准输出或者标准缺点的库例程都不会产生任何效果。
由于守护进程并不与终端设备干系联,以是不能在终端设备上显示器输出,也无从从交互式用户那里接管输入。
及时守护进程是从交互式会话启动的,但由于守护进程是在后台运行的,以是登录会话的终止并不影响守护进程。
如果其他用户在同一终端设备上登录,我们也不会在该终端上见到守护进程的输出,用户也不可期望他们在终端上的输入会由守护进程读取。

大略来说:

daemon 不应利用标准流0/1/2 要设定成 /dev/null

如下图:

实现了这一个功能。
dup() 系统调用会复制输入参数中的文件描述符,并复制到最小的未分配文件描述符上。
以是上述例程可以理解为:

关闭所有可以打开的文件描述符,包括标准输入输出错误;打开/dev/null并赋值给变量fd0,由于标准输入已经关闭了,以是/dev/null会绑定到0,即标准输入;由于最小未分配文件描述符为1,复制文件描述符0到文件描述符1,即标准输出也绑定到/dev/null;由于最小未分配文件描述符为2,复制文件描述符0到文件描述符2,即标准缺点也绑定到/dev/null;

开源项目实现:Workerman

Workerman 中的 Worker.php 中的 resetStd() 方法实现了类似的操作。

Workerman 中如此实现,结合博文,可能与 PHP 的 GC 机制有关,对付 fd 0 1 2来说,PHP 会坚持对这三个资源的引用计数,在直接 fclose 之后,会使得这几个 fd 对应的资源类型的变量引用计数为0,导致触发回收。
所须要做的便是将这些变量变为全局变量,担保引用的存在。

标签:

相关文章

我国土地利用分类代码的构建与应用

土地利用分类代码是我国土地管理的重要组成部分,是土地资源调查、规划、利用和保护的依据。土地利用分类代码的构建与应用显得尤为重要。本...

SEO优化 2025-02-18 阅读1 评论0

微信跳转微信支付便捷支付体验的秘密武器

移动支付已成为人们日常生活中不可或缺的一部分。作为我国领先的社交平台,微信支付凭借其便捷、安全的支付方式,深受广大用户的喜爱。而微...

SEO优化 2025-02-18 阅读0 评论0

探寻会计科目代码背后的奥秘分类与

会计科目代码是会计信息系统中不可或缺的组成部分,它将企业的经济活动进行分类和归纳,为会计核算、财务分析和决策提供重要依据。本文将从...

SEO优化 2025-02-18 阅读1 评论0