PHP事件

时间:2016-11-30 07:27:38

标签: php libevent pecl-event pecl-dio

我正在使用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();




?>

1 个答案:

答案 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流。没有干净的方法来获取封装在DIO资源中的文件描述符。但有一个解决方法:

  • 使用poll;
  • 打开端口流
  • 使用[kqueue] [3];
  • 使流无阻塞
  • 使用[fopen()] [3];
  • 从流中获取数字文件描述符
  • 将数字文件描述符传递给stream_set_blocking()(当前未记录)并获取DIO资源;
  • 使用回调添加EventUtil::getSocketFd()以侦听文件描述符上的读取事件;
  • 回调中的
  • 会耗尽可用数据并根据应用程序的逻辑对其进行处理。

替代方案:修补/贡献DIO

当然,您可以添加一个将基础文件描述符导出为整数的函数。这很简单。签出项目:

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);