溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

php命名管道怎么實現(xiàn)

發(fā)布時間:2022-08-13 14:50:38 來源:億速云 閱讀:168 作者:iii 欄目:開發(fā)技術

這篇文章主要介紹了php命名管道怎么實現(xiàn)的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇php命名管道怎么實現(xiàn)文章都會有所收獲,下面我們一起來看看吧。

進程間為什么要通信

進程間通信的目的:

  • 數(shù)據(jù)傳輸:一個 進程需要將它的數(shù)據(jù) 發(fā)送給另一個進程。

  • 通知事件:一個進程需要向另一個或一組進程 發(fā)送消息,通知它(它們)發(fā)生了 某種事件(如進程終止時要通知父進程)。

  • 資源共享:多個進程之間 共享同樣的資源 。為了做到這一點,需要內核提供互斥和同步機制。

  • 進程控制:有些進程 希望完全控制另一個進程的執(zhí)行 (如 Debug 進程),此時控制進程希望能夠攔截另一個進程的所有狀態(tài)信息

進程不是孤立的,一個足夠大的項目絕對不是單一的進程可以支撐的起的。所以我們需要進程間通信,來滿足不同進程間信息交互與傳遞的需求。

進程間通信本質上是進程與進程之間交換數(shù)據(jù)的手段。

進程如何實現(xiàn)通信

每個進程的用戶地址空間都是獨立的(進程通過虛擬內存地址達到進程相互隔離),一般而言是不能互相訪問的,進程之間要通信必須通過內核,也就是說操作內核提供一個緩沖區(qū),用戶進程操作這個緩沖區(qū)【讀寫數(shù)據(jù)】來實現(xiàn)通信。

php命名管道怎么實現(xiàn)

常見進程通信方式

php命名管道怎么實現(xiàn)

今天我們要學習的是管道中的命名管道

管道概念

其實管道一共有兩種,一種是匿名管道,它的特點是只能用于具有共同祖先的進程(具有親緣關系的進程)之間進行通信;比如父子進程,通常,一個匿名管道由一個進程創(chuàng)建,然后該進程調用fork,此后父、子進程之間就可應用該管道。

對于匿名管道,它的通信范圍是存在父子關系的進程。因為管道沒有實體,也就是沒有管道文件,只能通過 fork 來復制父進程 fd 文件描述符,來達到通信的目的。

第二種是命名管道可以實現(xiàn)兩個不相關進程之間進行數(shù)據(jù)交互,也能實現(xiàn)父子進程之間數(shù)據(jù)交互。命名管道是一種特殊類型的文件,有實體

對于命名管道,它可以在不相關的進程間也能相互通信。因為命名管道,提前創(chuàng)建了一個類型為管道的設備文件,在進程里只要使用這個設備文件,就可以相互通信。

由于php擴展并沒有提供匿名管道的封裝,只提供了命名管道的,所有我們先不講匿名管道,有興趣的自行了解

命名管道實現(xiàn)

不管是匿名管道還是命名管道,進程寫入的數(shù)據(jù)都是緩存在內核中,另一個進程讀取數(shù)據(jù)時候自然也是從內核中獲取,同時通信數(shù)據(jù)都遵循先進先出原則,類似于隊列

posix_mkfifo函數(shù)

php 通過 posix_mkfifo 函數(shù)創(chuàng)建命名管道文件

<?php
//定義管道文件
$file = 'fifo_dalei';
//posix_access() 函數(shù)檢測管道文件是否存在,如果不存在使用posix_mkfifo() 函數(shù)創(chuàng)建一個命名管道文件
if(!posix_access($file, POSIX_F_OK)){
    if(posix_mkfifo($file,0666)){
        echo "命名管道創(chuàng)建成功\n";
    }
}

php命名管道怎么實現(xiàn)

執(zhí)行代碼我們就得到了創(chuàng)建出來的管道文件 fifo_dalei ,我們查看文件權限位以 p 開頭就表示這個文件是一個管道文件,當然我們也可以使用 file 命令查看fifo_dalei 文件類型是管道類型

php命名管道怎么實現(xiàn)

我們可以直接在終端操作讀寫這個命名管道文件

比如:我們打開兩個終端,A終端與B終端,A終端負責讀命名管道中的數(shù)據(jù),B終端負責往命名管道寫入數(shù)據(jù)

php命名管道怎么實現(xiàn)

我們通過 cat 命令 讀取命名管道,如果命名管道沒有數(shù)據(jù) cat fifo_dalei 命令會阻塞住,直到有數(shù)據(jù)寫入命名管道,cat fifo_dalei 才會輸出數(shù)據(jù)內容并結束,反過來如果先將數(shù)據(jù)寫入命名管道,卻沒有另一個進程讀取命名管道的內容,寫入命令依然會阻塞。

由此可以發(fā)現(xiàn),管道這種通信方式效率低,不適合進程間頻繁地交換數(shù)據(jù)。當然,它的好處,自然就是簡單,同時也我們很容易得知管道里的數(shù)據(jù)已經被另一個進程讀取了。

那么在php中我們如何使用命名管道文件呢,下面我們以父子進程通信為例

<?php
// 定義命名管道文件
$file = 'fifo_dalei';
// posix_access 函數(shù)檢測當前文件是否存在
if(!posix_access($file, POSIX_F_OK)){
//posix_mkfifo 函數(shù)創(chuàng)建命名管道
    if(posix_mkfifo($file,0666)){
        echo "命名管道創(chuàng)建成功\n";
    }
}
// fork 創(chuàng)建子進程
$pid = pcntl_fork();
if($pid == 0){//子進程執(zhí)行邏輯
	// 用讀方式打開命名管道,需要注意的是如果命名管道內沒有數(shù)據(jù),fopen 函數(shù)會阻塞
    $fd = fopen($file, 'r');
    // fread 函數(shù) 讀取命名管道,10個字節(jié)長度數(shù)據(jù)
    $data = fread($fd,10);
    if($data){
            fprintf(STDOUT,"read process pid = %d, recv:%s\n",getmypid(),$data);
    }
    exit(0);
}
// 用寫方式打開命名管道
$fd = fopen($file, 'w');
// 往命名管道寫入數(shù)據(jù) 'dalei' 寫入的長度為5
$len = fwrite($fd,'dalei',5);
fprintf(STDOUT,"write process pid = %d, write len=%d\n",posix_getpid(),$len);
// 關閉管道文件
fclose($fd);
//回收執(zhí)行完畢退出子進程,防止僵尸進程
$pid = pcntl_wait($status);
if($pid > 0){
    fprintf(STDOUT,"exit process pid = %d\n",$pid);
}

php命名管道怎么實現(xiàn)

分析上面代碼,首先父進程32023 寫入數(shù)據(jù)dalei到命名管道,子進程 32024 讀取命名管道中父進程寫入的數(shù)據(jù),然后 32024 子進程退出。

需要注意的是 fopen() 函數(shù)打開命名管道必須讀端寫端都打開,不然 fopen() 函數(shù)會阻塞

上面我們通過命令操作管道發(fā)現(xiàn),管道內必須有內容,才能讀取管道,不然會阻塞,php操作管道也同樣有這個問題,看下面代碼,我們通過 while 不停循環(huán)讀取管道內容,但是寫數(shù)據(jù)端寫入一次會發(fā)生什么事情呢?

<?php
// 定義命名管道文件
$file = 'fifo_dalei';
// posix_access 函數(shù)檢測當前文件是否存在
if(!posix_access($file, POSIX_F_OK)){
//posix_mkfifo 函數(shù)創(chuàng)建命名管道
    if(posix_mkfifo($file,0666)){
        echo "命名管道創(chuàng)建成功\n";
    }
}
// fork 創(chuàng)建子進程
$pid = pcntl_fork();
if($pid == 0){//子進程執(zhí)行邏輯
	// 用讀方式打開命名管道,需要注意的是如果命名管道內沒有數(shù)據(jù),fopen 函數(shù)會阻塞
    $fd = fopen($file, 'r');
    while(1){
    // fread 函數(shù) 讀取命名管道,10個字節(jié)長度數(shù)據(jù)
    $data = fread($fd,10);
    if($data){
            fprintf(STDOUT,"read process pid = %d, recv:%s\n",getmypid(),$data);
    }
    echo "hello\n";
     sleep(2);
    }
    exit(0);
}
// 用寫方式打開命名管道
$fd = fopen($file, 'w');
// 往命名管道寫入數(shù)據(jù) 'dalei' 寫入的長度為5
$len = fwrite($fd,'dalei',5);
fprintf(STDOUT,"write process pid = %d, write len=%d\n",posix_getpid(),$len);
// 關閉管道文件
#fclose($fd);
//回收執(zhí)行完畢退出子進程,防止僵尸進程
$pid = pcntl_wait($status);
if($pid > 0){
    fprintf(STDOUT,"exit process pid = %d\n",$pid);
}

通過運行上面代碼,當父進程只寫入一次,子進程循環(huán)讀,由于命名管道內無數(shù)據(jù)會造成 fread() 函數(shù)阻塞,無法往下執(zhí)行也就無法打印出 hello,如何以非阻塞的方式讀取數(shù)據(jù)呢?

下面我們就來介紹 stream_set_blocking() 函數(shù)實現(xiàn)非阻塞讀命名管道,即命名管道無數(shù)據(jù)立即返回,并不會阻塞在fread() 函數(shù)

<?php
// 定義命名管道文件
$file = 'fifo_dalei';
// posix_access 函數(shù)檢測當前文件是否存在
if(!posix_access($file, POSIX_F_OK)){
//posix_mkfifo 函數(shù)創(chuàng)建命名管道
    if(posix_mkfifo($file,0666)){
        echo "命名管道創(chuàng)建成果\n";
    }
}
// fork 創(chuàng)建子進程
$pid = pcntl_fork();
if($pid == 0){//子進程執(zhí)行邏輯
	// 用讀方式打開命名管道,需要注意的是如果命名管道內沒有數(shù)據(jù),fopen 函數(shù)會阻塞
    $fd = fopen($file, 'r');
    //設置非阻塞讀取命名管道
    stream_set_blocking($fd,0);
    while(1){
    // fread 函數(shù) 讀取命名管道,10個字節(jié)長度數(shù)據(jù)
    $data = fread($fd,10);
    if($data){
            fprintf(STDOUT,"read process pid = %d, recv:%s\n",getmypid(),$data);
    }
    echo "hello\n";
    sleep(2);
    }
    exit(0);
}
// 用寫方式打開命名管道
$fd = fopen($file, 'w');
// 往命名管道寫入數(shù)據(jù) 'dalei' 寫入的長度為5
$len = fwrite($fd,'dalei',5);
fprintf(STDOUT,"write process pid = %d, write len=%d\n",posix_getpid(),$len);
// 關閉管道文件
#fclose($fd);
//回收執(zhí)行完畢退出子進程,防止僵尸進程
$pid = pcntl_wait($status);

if($pid > 0){
    fprintf(STDOUT,"exit process pid = %d\n",$pid);
}

php命名管道怎么實現(xiàn)

還有個需要注意的知識點,當寫端寫入數(shù)據(jù)的過程中,如果讀端退出,寫入數(shù)據(jù)將失敗,并且產生中斷信號 SIGPIPE, 下面是實驗代碼

<?php
// 定義命名管道文件
$file = 'fifo_dalei';
// posix_access 函數(shù)檢測當前文件是否存在
if(!posix_access($file, POSIX_F_OK)){
//posix_mkfifo 函數(shù)創(chuàng)建命名管道
    if(posix_mkfifo($file,0666)){
        echo "命名管道創(chuàng)建成果\n";
    }
}
// 安裝信號處理器,處理捕獲信號
pcntl_signal(SIGPIPE,function($signo){
fprintf(STDOUT,"signo=%d\n",$signo);
});
// fork 創(chuàng)建子進程
$pid = pcntl_fork();
if($pid == 0){//子進程執(zhí)行邏輯
	// 用讀方式打開命名管道,需要注意的是如果命名管道內沒有數(shù)據(jù),fopen 函數(shù)會阻塞
    $fd = fopen($file, 'r');
    //設置非阻塞讀取命名管道
    stream_set_blocking($fd,0);
    $i = 0;
    while(1){
    // fread 函數(shù) 讀取命名管道,10個字節(jié)長度數(shù)據(jù)
    $data = fread($fd,10);
    if($data){
    	$i++;
    	if($i > 2){
    		fclose($fd);
    		break;
    	}
            fprintf(STDOUT,"read process pid = %d, recv:%s\n",getmypid(),$data);
    }
    sleep(2);
    }
    exit(0);
}
// 用寫方式打開命名管道
$fd = fopen($file, 'w');
stream_set_blocking($fd,0);
while(1){
// 信號分發(fā)(沒有這個函數(shù),信號無法被捕獲)
pcntl_signal_dispatch();
// 往命名管道寫入數(shù)據(jù) 'dalei' 寫入的長度為5
$len = fwrite($fd,'dalei',5);
fprintf(STDOUT,"write process pid = %d, write len=%d\n",posix_getpid(),$len);
sleep(2);
}
// 關閉管道文件
fclose($fd);
//回收執(zhí)行完畢退出子進程,防止僵尸進程
$pid = pcntl_wait($status);
if($pid > 0){
    fprintf(STDOUT,"exit process pid = %d\n",$pid);
}

php命名管道怎么實現(xiàn)

通過分析代碼我們得知,我們定義了一個 $i 變量 用于累加,當累加數(shù)大于2,也就是read執(zhí)行超過兩次后讀進程退出,寫進程write將無法再向命名管道寫入數(shù)據(jù),并且產生信號數(shù)為13的中斷信號

使用 kill -l 查看linux 中所有信號

php命名管道怎么實現(xiàn)

無血緣進程間通信

寫端

<?php
$file = 'fifo_dalei';
if(!posix_access($file,POSIX_F_OK)){
    if(!posix_mkfifo($file,0666)){
    }
}
$fd = fopen($file,'w');
while(1){
	//獲取終端輸入數(shù)據(jù),大小限制1280字節(jié)
    $data = fgets(STDIN,1280);
    // 寫入數(shù)據(jù)到命名管道,數(shù)據(jù)長度限制為10個字節(jié)
    $len = fwrite($fd,$data,10);
    fprintf(STDOUT,"pid=%d, write len = %d\n",posix_getpid(),$len);
}
fclose($fd);

讀端 (一定要設置非阻塞讀,不然寫端,寫入數(shù)據(jù)讀端無法讀取,只有寫端進程退出才,讀端才能全部讀取出來)

<?php
$file = 'fifo_dalei';
if(!posix_access($file,POSIX_F_OK)){
    if(posix_mkfifo($file,0666)){

    }
}
$fd = fopen($file,'r');
//設置非阻塞讀
stream_set_blocking($fd,0);
while(1){
//讀取命名管道內容,讀取長度限制為128字節(jié)
$data = fread($fd,128);
if($data){
    fprintf(STDOUT,"pid=%d,data=%s\n",posix_getpid(),$data);
}
}
fclose($fd);

php命名管道怎么實現(xiàn)

關于“php命名管道怎么實現(xiàn)”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“php命名管道怎么實現(xiàn)”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。

php
AI