php守护进程 加linux命令nohup实现任务每秒执行一次


Posted in PHP onJuly 04, 2011

Unix中 nohup 命令功能就是不挂断地运行命令,同时 nohup 把程序的所有输出到放到当前目录 nohup.out 文件中,如果文件不可写,则放到 <用户主目录>/nohup.out 文件中。那么有了这个命令以后我们php就写成shell 脚本使用循环来让我们脚本一直运行下去,不管我们终端窗口是否关闭都能够让我们php 脚本一直运行下去。
马上动手写个 PHP 小程序,功能为每30秒记录时间,写入到文件

# vi for_ever.php 
#! /usr/local/php/bin/php 
define('ROOT', dirname(__FILE__).'/'); 
set_time_limit(0); 
while (true) { 
file_put_contents(ROOT.'for_ever.txt', date('Y-m-d H:i:s')."\n", FILE_APPEND); 
echo date('Y-m-d H:i:s'), ' OK!'; 
sleep(30); 
} 
?>

保存退出,然后赋予 for_ever.php 文件可执行权限:
# chmod +x for_ever.php
让它在再后台执行:
# nohup /home/andy/for_ever.php.php &
记得最后加上 & 符号,这样才能够跑到后台去运行
执行上述命令后出现如下提示:
[1] 5157
nohup: appending output to 'nohup.out'
所有命令执行输出信息都会放到 nohup.out 文件中
这时你可以打开 for_ever.php 同目录下的 for_ever.txt 和 nohup.out 看看效果!
好了,它会永远运行下去了,怎么结束它呢?
# ps
PID TTY TIME CMD
4247 pts/1 00:00:00 bash
5157 pts/1 00:00:00 for_ever.php
5265 pts/1 00:00:00 ps
# kill -9 5157
找到进程号 5157 杀之,你将看到
[1]+ Killed nohup /home/andy/for_ever.php
OK!
====================
在很多项目中,或许有很多类似的后端脚本需要通过crontab定时执行。比如每10秒检查一下用户状态。脚本如下:
@file: /php_scripts/scan_userstatus.php
#!/usr/bin/env php -q 
$status = has_goaway(); 
if ($status) { 
//done 
} 
?>

通过crontab定时执行脚本scan_userstatus.php
#echo “*:*/10 * * * * /php_scripts/scan_userstatus.php”
这样,每隔10秒钟,就会执行该脚本。
我们发现,在短时间内,该脚本的内存资源还没有释放完,又启用了新的脚本。也就是说:新脚本启动了,旧脚本占用的资源还没有如愿释放。如此,日积月累,浪费了很多内存资源。我们对这个脚本进行了一下改进,改进后如下:
@file: /php_scripts/scan_userstatus.php
#/usr/bin/env php -q 
while (1) { 
$status = has_goaway(); 
if ($status) { 
//done 
} 
usleep(10000000); 
} 
?>

这样,不需要crontab了。可以通过以下命令执行脚本,达到相同的功能效果
#chmod +x /php_scripts/scan_userstatus.php
#nohup /php_scripts/scan_userstatus.php &
在这里,我们通过&将脚本放到后台运行,为了防止随着终端会话窗口关闭进程被杀,我们使用了nohup命令。那么有没有办法,不使nohup命令,也能够运行呢,就像Unin/Linux Daemon一样。接下来,就是我们要讲的守护进程函数。
什么是守护进程?一个守护进程通常补认为是一个不对终端进行控制的后台任务。它有三个很显著的特征:在后台运行,与启动他的进程脱离,无须控制终端。常用的实现方式是fork() -> setsid() -> fork() 详细如下:
@file: /php_scripts/scan_userstatus.php
#/usr/bin/env php -q 
daemonize(); 
while (1) { 
$status = has_goaway(); 
if ($status) { 
//done 
} 
usleep(10000000); 
} 
function daemonize() { 
$pid = pcntl_fork(); 
if ($pid === -1 ) { 
return FALSE; 
} else if ($pid) { 
usleep(500); 
exit(); //exit parent 
} 
chdir("/"); 
umask(0); 
$sid = posix_setsid(); 
if (!$sid) { 
return FALSE; 
} 
$pid = pcntl_fork(); 
if ($pid === -1) { 
return FALSE; 
} else if ($pid) { 
usleep(500); 
exit(0); 
} 
if (defined('STDIN')) { 
fclose(STDIN); 
} 
if (defined('STDOUT')){ 
fclose(STDOUT); 
} 
if (defined('STDERR')) { 
fclose(STDERR); 
} 
} 
?>

实现了守护进程函数以后,则可以建立一个常驻进程,所以只需要执行一次:
#/php_scripts/scan_userstatus.php
这里较为关键的二个php函数是pcntl_fork()和posix_setsid()。fork()一个进程,则表示创建了一个运行进程的副本,副本被认为是子进程,而原始进程被认为是父进程。当fork()运行之后,则可以脱离启动他的进程与终端控制等,也意味着父进程可以自由退出。 pcntl_fork()返回值,-1表示执行失败,0表示在子进程中,而返进程ID号,则表示在父进程中。在这里,退出父进程。setsid(),它首先使新进程成为一个新会话的“领导者”,最后使该进程不再控制终端,这也是成为守护进程最关键的一步,这意味着,不会随着终端关闭而强制退出进程。对于一个不会被中断的常驻进程来说,这是很关键的一步。进行最后一次fork(),这一步不是必须的,但通常都这么做,它最大的意义是防止获得控制终端。(在直接打开一个终端设备,而且没有使用O_NOCTTY标志的情况下, 会获得控制终端).
其它事项说明:
1) chdir() 将守护进程放到总是存在的目录中,另外一个好处是,你的常驻进程不会限制你umount一个文件系统。
2)umask() 设置文件模式,创建掩码到最大的允许限度。如果一个守护进程需要创建具有可读,可写权限的文件,一个被继承的具有更严格权限的掩码会有反作用。
3)fclose(STDIN), fclose(STDOUT), fclose(STDERR) 关闭标准I/O流。注意,如果有输出(echo),则守护进程会失败。所以通常将STDIN, STDOUT, STDERR重定向某个指定文件.
PHP 相关文章推荐
Extended CHM PHP 语法手册之 DIY
Oct 09 PHP
国外PHP程序员的13个好习惯小结
Feb 20 PHP
php读取文件内容至字符串中,同时去除换行、空行、行首行尾空格(Zjmainstay原创)
Jul 31 PHP
基于flush()不能按顺序输出时的解决办法
Jun 29 PHP
php查找字符串出现次数的方法
Dec 01 PHP
浅谈PHP中output_buffering
Jul 13 PHP
php分页查询的简单实现代码
Mar 14 PHP
php对xml文件的增删改查操作实现方法分析
May 19 PHP
centos7上编译安装php7以php-fpm方式连接apache
Nov 08 PHP
Yii2.0建立公共方法简单示例
Jan 29 PHP
PHP的图像处理实例小结【文字水印、图片水印、压缩图像等】
Dec 20 PHP
PHP优化之批量操作MySQL实例分析
Apr 23 PHP
ajax 的post方法实例(带循环)
Jul 04 #PHP
php高级编程-函数-郑阿奇
Jul 04 #PHP
php 日期和时间的处理-郑阿奇(续)
Jul 04 #PHP
php 目录与文件处理-郑阿奇(续)
Jul 04 #PHP
第4章 数据处理-php正则表达式-郑阿奇(续)
Jul 04 #PHP
第4章 数据处理-php字符串的处理-郑阿奇(续)
Jul 04 #PHP
第4章 数据处理-php数组的处理-郑阿奇
Jul 04 #PHP
You might like
PHP反射类ReflectionClass和ReflectionObject的使用方法
2013/11/13 PHP
JavaScript 对象模型 执行模型
2009/12/06 Javascript
读jQuery之八 包装事件对象
2011/06/21 Javascript
基于jquery的不规则矩形的排列实现代码
2012/04/16 Javascript
JavaScript高级程序设计 阅读笔记(十二) js内置对象Math
2012/08/14 Javascript
Google Maps API地图应用示例分享
2014/10/23 Javascript
全面解析JavaScript中的valueOf与toString方法(推荐)
2016/06/14 Javascript
AngularJS基础 ng-readonly 指令简单示例
2016/08/02 Javascript
利用jquery给指定的table动态添加一行、删除一行的方法
2016/10/12 Javascript
详解在vue-cli项目中安装node-sass
2017/06/21 Javascript
Vue.js中的图片引用路径的方式
2017/07/28 Javascript
JavaScript 自定义事件之我见
2017/09/25 Javascript
微信小程序踩坑记录之解决tabBar.list[3].selectedIconPath大小超过40kb
2018/07/04 Javascript
JavaScript错误处理操作实例详解
2019/01/04 Javascript
vscode配置vue下的es6规范自动格式化详解
2019/03/20 Javascript
解决mui框架中switch开关通过js控制开或者关状态时小圆点不动的问题
2019/09/03 Javascript
layui原生表单验证的实例
2019/09/09 Javascript
Vue路由管理器Vue-router的使用方法详解
2020/02/05 Javascript
基于javascript实现碰撞检测
2020/03/12 Javascript
jQuery实现点击滚动到指定元素上的方法分析
2020/03/19 jQuery
如何用JS模拟实现数组的map方法
2020/07/30 Javascript
[01:30]2016国际邀请赛中国区预选赛神秘商店火爆开启
2016/06/26 DOTA
用Python编写一个基于终端的实现翻译的脚本
2015/04/24 Python
numpy.random.seed()的使用实例解析
2018/02/03 Python
matplotlib实现区域颜色填充
2019/03/18 Python
Python TCP通信客户端服务端代码实例
2019/11/21 Python
python 还原梯度下降算法实现一维线性回归
2020/10/22 Python
NBA德国官方网上商店:NBA Store德国
2018/04/13 全球购物
一百多行代码实现react拖拽hooks
2021/03/23 Javascript
法院个人总结
2015/03/03 职场文书
初婚初育证明范本
2015/06/18 职场文书
捐书仪式主持词
2015/07/04 职场文书
简短的人生哲理(38句)
2019/08/13 职场文书
python 实现mysql自动增删分区的方法
2021/04/01 Python
python析构函数用法及注意事项
2021/06/22 Python
Python采集股票数据并制作可视化柱状图
2022/04/04 Python