我正在使用PHP Event类包装器来解压缩读取串口。我使用它来避免缓冲区超过运行 - 想法是使用Event定期检查端口,以便没有数据丢失。
我希望这些事件在设置时才会触发,但得出的结论是事件只会在调用EventBase::loop()
后触发。在这种情况下,当我调用loop()
时,控制流从我的代码发送到libevent中的调度程序。最终控制流返回到调用循环后位置的代码。
我从这种行为中假设我基本上安排了事件的分派,并应定期调用loop()以避免我的事件被CPU匮乏。
但是在这种情况下,我应该永远无法在之前的loop()
调用运行时调用loop()
,因为通过上面的解释,控制流可以在我的代码中,也可以在libevent中,并且可以&# 39;两者兼而有之。
所以我通过我的代码调用了loop()
(总共四个 - 我感觉很好),其中两个产生了可重复的警告。
我显然不理解这一点。有人可以帮忙吗?
干杯保罗
<?php
// serial comms defines
define("PORT", "/dev/serial0");
const PORTSETTINGS = array(
'baud' => 9600,
'bits' => 8,
'stop' => 1,
'parity' => 0
);
define("SYN", 170);
define("MSB", 127);
const POLL = 0.1;
/*
** Class Scanner
**
** Manages low level serial comms with the vbus master
**
*/
class Scanner {
private $fd;
private $pkt;
private $state;
private $base;
private $timer;
/*
** __construct()
**
** setup the serial port for reading using dio
** setup a timer to read anything on the serial port frequently
**
*/
function __construct() {
// set up port and state machine
$this->fd = dio_open(PORT, O_RDONLY | O_NOCTTY | O_NONBLOCK);
dio_tcsetattr($this->fd, PORTSETTINGS);
$this->pkt = array();
$this->state = "discard";
// set up timer handler
$this->base = new EventBase();
$this->timer = new Event($this->base, -1, Event::TIMEOUT | Event::PERSIST, array($this, "Tickle"));
$this->timer->addTimer(POLL);
$this->base->loop(EventBase::LOOP_NONBLOCK);
}
function PrintPkt($pkt) {
echo "\n\n".date("H:i:s");
foreach($pkt as $i)
echo " ".dechex($i);
}
/*
** Tickle()
**
** read the serial port, if MSB set discard the packet, else save the packet and then pass for processing
** called by the event timer on a regular basis ie POLL seconds
*/
function Tickle() {
do {
// read the next one and convert to int
$ch = dio_read($this->fd, 1);
$i = ord($ch);
// check for MSB, if set discard to the next packet
if (($i > MSB) && ($i != SYN))
$state="discard";
// if there is nothing on the port it returns 0x0 ie null/false
if ($i) {
if ($i == SYN) {
// we are at the start of a new packet
if (count($this->pkt) > 0) {
if ($this->state === "save")
// this is where we would save the packet but for now we are printing it.
$this->PrintPkt($this->pkt);
// reset for the next packet
$this->pkt = array();
$this->state = "save";
}
}
// save this number
$this->pkt[] = $i;
}
} while ($ch);
// restart the timer
$this->timer->addTimer(POLL);
}
/*
** spin()
**
** call the base loop so that the timer event is serviced
*/
function spin() {
$this->base->loop(EventBase::LOOP_NONBLOCK);
}
}
$c = new Scanner();
echo "setup";
while(1);
// $c->spin();
?>
答案 0 :(得分:2)
我希望事件在设置时才会触发,但得出的结论是事件只在调用EventBase :: loop()后触发。
Event::__construct()
注册一个事件并将其与EventBase
相关联。此时,Event
对象表示特定事件的一组条件和回调。在此状态下,事件未已触发。
Event::add()
使事件待定。当事件处于挂起状态时,可以在满足相应条件时触发它。
EventBase::loop()
运行EventBase
,直到其中没有待处理的事件。只有在相应的基数运行时才能触发事件。
触发事件后,它将变为活动,并且会运行其回调。如果事件配置为persistent,则在回调运行后它将保持挂起状态。否则,它将停止等待。考虑一下:
$base = new EventBase();
$e = new Event($base, -1, Event::TIMEOUT, function() {
// The event is not pending, since it is not persistent:
printf("1 sec elapsed\n");
});
printf("1) Event is pending: %d\n", $e->pending);
// Make $e pending
$e->add(1);
printf("2) Event is pending: %d\n", $e->pending);
// Dispatch all pending events
$base->loop();
printf("3) Event is pending: %d\n", $e->pending);
输出
1) Event is pending: 0
2) Event is pending: 1
1 sec elapsed
3) Event is pending: 0
使用Event::PERSIST
标志:
$e = new Event($base, -1, Event::TIMEOUT | Event::PERSIST, function() {
每秒都会调用回调,因为事件仍处于待定状态。
最终控制流返回到调用循环后位置的代码。
该过程被阻止,直到循环结束。我们需要等待处理事件。否则,流程可能会在处理完所有事件之前到达程序的末尾。这就是所有异步程序实际工作的方式。
我从这种行为中假设我基本上安排了事件的分派,并应定期调用loop()以避免我的事件被CPU匮乏。
是的,您在运行基础之前安排事件。不,您不应该定期致电EventBase::loop()
,也不需要考虑CPU“饥饿”,因为底层实施基于有效的平台特定后端,例如epoll
,{{1}在空闲状态下(当运行基地只等待事件发生时),该过程消耗的系统资源可以忽略不计。
例如,您可以通过计时器事件,添加/删除事件或修改其回调中的事件属性来控制流量。
事件扩展程序当前无法识别DIO流。没有干净的方法来获取封装在DIO资源中的文件描述符。但有一个解决方法:
poll
; kqueue
] [3]; fopen()
] [3]; stream_set_blocking()
(当前未记录)并获取DIO资源; EventUtil::getSocketFd()
以侦听文件描述符上的读取事件; 当然,您可以添加一个将基础文件描述符导出为整数的函数。这很简单。签出项目:
dio_fdopen()
将新功能添加到Event
:
svn checkout https://svn.php.net/repository/pecl/dio/trunk dio
cd dio
其原型为php7/dio.c
:
/* {{{ proto int dio_get_fd(resource fd)
Returns numeric file descriptor for the given DIO resource */
PHP_FUNCTION(dio_get_fd)
{
zval *r_fd;
php_fd_t *f;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &r_fd) == FAILURE) {
return;
}
if ((f = (php_fd_t *) zend_fetch_resource(Z_RES_P(r_fd), le_fd_name, le_fd)) == NULL) {
RETURN_FALSE;
}
RETURN_LONG(f->fd);
}
/* }}} */
/* ... */
PHP_FE(dio_get_fd, dio_close_args)
重建扩展程序,您就可以使用php7/php_dio.h
:
PHP_FUNCTION(dio_get_fd);