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 相关文章推荐
用PHP写的MySQL数据库用户认证系统代码
Mar 22 PHP
php开启安全模式后禁用的函数集合
Jun 26 PHP
使用php 获取时间今天明天昨天时间戳的详解
Jun 20 PHP
php用正则表达式匹配URL的简单方法
Nov 12 PHP
PHP将回调函数作用到给定数组单元的方法
Aug 19 PHP
php构造函数的继承方法
Feb 09 PHP
php代码架构的八点注意事项
Jan 25 PHP
postfixadmin忘记密码后的修改密码方法详解
Jul 20 PHP
php 获取文件行数的方法总结
Oct 11 PHP
PHP使用Redis实现防止大并发下二次写入的方法
Oct 09 PHP
PHP使用HTML5 FormData对象提交表单操作示例
Jul 02 PHP
PHP+iframe模拟Ajax上传文件功能示例
Jul 02 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 COOKIE设置为浏览器进程
2009/06/21 PHP
谨慎使用PHP的引用原因分析
2012/09/06 PHP
PHP Class&amp;Object -- 解析PHP实现二叉树
2013/06/25 PHP
使用Thinkphp框架开发移动端接口
2015/08/05 PHP
全面了解PHP中的全局变量
2016/06/17 PHP
php 在字符串指定位置插入新字符的简单实现
2016/06/28 PHP
使用PHP免费发送定时短信的实例
2016/10/24 PHP
php编程实现简单的网页版计算器功能示例
2017/04/26 PHP
thinkphp5实现微信扫码支付
2019/12/23 PHP
laravel框架数据库操作、查询构建器、Eloquent ORM操作实例分析
2019/12/20 PHP
Jquery Ajax学习实例3 向WebService发出请求,调用方法返回数据
2010/03/16 Javascript
UserData用法总结 lanyu出品
2010/07/01 Javascript
深入解析JavaScript中的变量作用域
2013/12/06 Javascript
js实现带圆角的两级导航菜单效果代码
2015/08/24 Javascript
JavaScript中数组的合并以及排序实现示例
2015/10/24 Javascript
Bootstrap每天必学之级联下拉菜单
2016/03/27 Javascript
javascript容错处理代码(屏蔽js错误)
2017/01/20 Javascript
Vue.js实现输入框绑定的实例代码
2017/08/24 Javascript
微信小程序实现折叠面板
2018/01/31 Javascript
VUE在for循环里面根据内容值动态的加入class值的方法
2018/08/12 Javascript
Vue中使用sass实现换肤功能
2018/09/07 Javascript
原生js拖拽实现图形伸缩效果
2020/02/10 Javascript
JS实现表单中点击小眼睛显示隐藏密码框中的密码
2020/04/13 Javascript
[52:37]完美世界DOTA2联赛循环赛 Forest vs DM BO2第一场 10.29
2020/10/29 DOTA
在Django中进行用户注册和邮箱验证的方法
2016/05/09 Python
python+django+sql学生信息管理后台开发
2018/01/11 Python
对pytorch网络层结构的数组化详解
2018/12/08 Python
python 实现图片旋转 上下左右 180度旋转的示例
2019/01/24 Python
基于Django静态资源部署404的解决方法
2019/07/28 Python
Python 通过微信控制实现app定位发送到个人服务器再转发微信服务器接收位置信息
2019/08/05 Python
scrapy爬虫:scrapy.FormRequest中formdata参数详解
2020/04/30 Python
详解用Python调用百度地图正/逆地理编码API
2020/07/02 Python
Darphin迪梵官网: 来自巴黎,植物和精油调制的护肤品牌
2016/10/11 全球购物
长青弘远的面试题
2012/06/09 面试题
求职信的正确写法
2014/07/10 职场文书
python中sqllite插入numpy数组到数据库的实现方法
2021/06/21 Python