2024 年 PHP Conference Japan

posix_mkfifo

(PHP 4, PHP 5, PHP 7, PHP 8)

posix_mkfifo建立 FIFO 特殊檔案(命名管道)

說明

posix_mkfifo(字串 $filename, 整數 $permissions): 布林值

posix_mkfifo() 會建立一個特殊的 FIFO 檔案,它存在於檔案系統中,並作為行程之間雙向通訊的端點。

參數

filename

FIFO 檔案的路徑。

permissions

第二個參數 permissions 必須以八進位表示法給出(例如 0644)。新建立的 FIFO 的權限也取決於目前的 umask() 設定。所建立檔案的權限為 (mode & ~umask)。

傳回值

成功時傳回 true,失敗時傳回 false

新增筆記

使用者貢獻的筆記 7 則筆記

tim
15 年前
為了對「半連接」管道(使用 /usr/bin/mkfifo、posix_mkfifo 等建立)進行非阻塞式的 fopen 讀取存取,我直接執行以下操作:
<?php
$fh
=fopen($fifo, "r+"); // 確保至少有一個寫入器(我們自己),因此將是非阻塞的
stream_set_blocking($fh, false); // 防止 fread / fwrite 阻塞
?>

「r+」允許 fopen 立即返回,無論是否有外部寫入器通道。然後您必須使用自己的慣例來追蹤 $fh 作為一個偽唯讀資源,因為技術上也允許 fwrite。我已經在 Linux 上使用 PHP 4.3.10 和 PHP 5.2.4 成功地使用了這種方法,無論是半連接(尚無寫入器)還是預連接(寫入器已在等待)的管道,都使用 stream_select 進行輪詢。
TorokAlpar at Gmail dot com
16 年前
以下是 - tech at kwur dot com- 提到的問題的可能解決方案

我面臨的問題是我有一個行程(伺服器)需要處理 Socket 連線,同時需要從資料庫獲取一些資料。我不希望客戶端等待查詢執行時間,所以我決定建立一個單獨的行程來在資料庫上執行查詢,並且兩者之間透過管道進行通訊。當然,我不希望伺服器在沒有可用資料時阻塞。所以我想出的方法是使用 stream_select(),為了克服上述問題,我會 fork 這個行程,在子行程中開啟管道進行寫入,這樣父行程在開啟管道時就不會阻塞。

以下是一些程式碼

<code>

<?php

if (pcntl_fork() == 0)
{
// 子行程
$file = fopen("JobsQueue","w");
sleep(1);
exit(
0);
}
else
{
usleep(15); // 確保子行程先執行
$file = fopen("JobsQueue","r");
}

echo
"已開啟佇列 \n";

while (
true)
{
$reders = array($file);
if (
stream_select($reders,$writers=null,$except=null,0,15) < 1)
{
continue;
}
else
{
// 從 FIFO 讀取資料
$data = fread($file,1024);
echo
$data;
}
sleep(1);
}

?>

</code>
Enric Jaen
17 年前
實現非阻塞管道讀取器的一種方法是先檢查管道是否存在。如果存在,則從管道讀取,否則執行其他操作。假設寫入器創建管道、寫入管道,然後刪除管道,這種方法將會有效。

這是一個阻塞寫入器

<?php
$pipe
="/tmp/pipe";
$mode=0600;
if(!
file_exists($pipe)) {
// 建立管道
umask(0);
posix_mkfifo($pipe,$mode);
}
$f = fopen($pipe,"w");
fwrite($f,"hello"); // 阻塞直到有讀取器
unlink($pipe); // 刪除管道

?>

這是一個非阻塞讀取器

<?php
$pipe
="/tmp/pipe";
if(
!file_exists($pipe)) {
echo
"I am not blocked!";
}
else {
// 阻塞並從管道讀取
$f = fopen($pipe,"r");
echo
fread($f,10);
}
?>
tech at kwur dot com
17 年前
這仍然不是一個解決方案:如果我監聽來自管道的指令,並在另一個管道輸出狀態,PHP 會在兩個開啟操作上阻塞,因為其他東西還沒有連接到這個管道。因為我無法使用低階的 fcntl() 來設定 O_NONBLOCK 或類似的东西,這總是會鎖死,而且真的很蠢。我能讓它工作的唯一方法是使用 system() 產生單獨的子 shell,讓它們分別執行 cat 或 echo,然後管道才能正常工作……通常是這樣?我們不能在開啟時設定非阻塞,這真是麻煩!
Uther Pendragon
17 年前
注意(引用自 Debian Linux 上的 `man 7 pipe`)

「在某些系統上(但不是 Linux),管道是雙向的:數據可以在管道兩端之間雙向傳輸。根據 POSIX.1-2001,管道只需要是單向的。可移植的應用程式應避免依賴雙向管道語義。」

Linux 管道不是雙向的。

此外,在我看來,在 PHP 中使用 FIFO(命名)管道似乎毫無意義,因為似乎沒有辦法判斷開啟(更不用說讀取)它是否會阻塞。stream_select 應該能夠做到這一點,不幸的是,你無法做到這一點,因為即使嘗試開啟管道進行讀取也會阻塞,直到有寫入器出現。

我甚至嘗試使用 popen("cat $name_of_pipe", 'r'),即使這樣也會阻塞,直到另一個進程開啟它進行寫入。
Mauro Titimoli
14 年前
物件導向的 FIFO 通訊流程

<?php
interface Communication {
public function
receive($bytes = 1024);

public function
getData();

public function
clearData();

public function
send($data);
}

class
FIFOCommunication implements Communication {
private
$fifos;

private
$data;

static public function
stream_fifo_open($fifoPath, $mode) {
if (
pcntl_fork() == 0) {
if (!
file_exists($fifoPath)) {
posix_mkfifo($fifoPath, $mode);
}

$fifo = fopen($fifoPath, 'w');
sleep(1);

exit(
0);
}
else {
usleep(15);

return
fopen($fifoPath, 'r');
}
}

static public function
stream_fifo_write($fifoPath, $mode, $data) {
if (
pcntl_fork() == 0) {
if (!
file_exists($fifoPath)) {
posix_mkfifo($fifoPath, $mode);
}

$fifo = fopen($fifoPath, 'w');

fwrite($fifo, $data);

exit(
0);
}
}

public function
__construct(
$fifoInputName, $fifoInputMode,
$fifoOutputName, $fifoOutputMode
) {
$this->fifos = array(
'input' => array(
'file' => self::stream_fifo_open($fifoInputName, $fifoInputMode),
'mode' => $fifoInputMode,
'name' => $fifoInputName,
'use' => 'r',
),
'output' => array(
'mode' => $fifoOutputMode,
'name' => $fifoOutputName,
'use' => 'w'
)
);
}
public function
remove($type = null) {
switch (
$type) {
case
'input':
@
unlink($this->fifos['input']['name']);
break;

case
'output':
@
unlink($this->fifos['output']['name']);
break;

default:
@
unlink($this->fifos['input']['name']);
@
unlink($this->fifos['output']['name']);
}
}

public function
receive($bytes = 1024) {
$readers = array($this->fifos['input']['file']);
if (
stream_select($readers, $writers = null, $except = null, 0, 15) == 1) {
$data = fread($this->fifos['input']['file'], $bytes);
}

if (! empty(
$data)) {
$this->data .= $data;
return
true;
}
else {
return
false;
}
}

public function
getData() {
return
$this->data;
}

public function
clearData() {
$this->data = null;
}

public function
send($data) {
$fifoOutput = & $this->fifos['output'];
self::stream_fifo_write($fifoOutput['name'], $fifoOutput['mode'], $data);
}
}

$fifoCommunication = new FIFOCommunication(
getmypid() . '.input', 0600,
getmypid() . '.output', 0600
);

echo
"COMMUNICATION STARTED\n";

while (
true) {
if (
$fifoCommunication->receive()) {
$data = $fifoCommunication->getData();
if (
$data == "EXIT\n") {
break;
}
else {
$fifoCommunication->send('RECEIVED: ' . $fifoCommunication->getData());
}
}

sleep(1);
}

$fifoComunication->remove();
?>
mike at easystyle dot org
16 年前
你不能在開啟讀取器之後立即開啟一個寫入器嗎?只是為了確保你不會有任何阻塞問題……
To Top