作为程序员一定要保持良好的睡眠,才能好编程

RPC 第五课 有名管道与无名管道的定义及区别

发布时间:2018-11-21

进程之间的通讯,作为老牌的通讯方式: 管道


管道分为: 无名管道和 有名管道


1、无名管道

 popen 打开进程文件指针(单向管道)

pclose 关闭用popen打开的指向管道的指针


proc_open 打开一个用来输入和输出的文件指针(双向管道)

proc_close 管子资源文件


$desc=[
  ['pipe','r'],
  ['pipe','w'],
  ['file','/tmp/error.txt','a']
];


$cmd='cat';

$process=proc_open($cmd,$desc,$pipes);


if(!is_resource($process)){
  exit('proc_open failure.');
}

fwrite($pipes[0],date('Y-m-d H:i:s'));


sleep(1);

echo 'daa';

$str=fread($pipes[1],100);

echo $str.PHP_EOL;

fclose($pipes[1]);
fclose($pipes[0]);

proc_close($process);


说明:$cmd =cat  直接打印数据


$desc=[
  ['pipe','r'],
  ['pipe','w'],
  ['file','/tmp/error.txt','a']
];


//$cmd='cat';
$cmd='php';

$process=proc_open($cmd,$desc,$pipes);


if(!is_resource($process)){
  exit('proc_open failure.');
}

//fwrite($pipes[0],date('Y-m-d H:i:s'));

fwrite($pipes[0],'<?php print_r($_SERVER); ?>');

/**fwrite($pipes[0],'<?php echo time(); ?>');**/

fclose($pipes[0]);
//sleep(1);

//echo 'daa';

$str=stream_get_contents($pipes[1]);

echo $str.PHP_EOL;

fclose($pipes[1]);

proc_close($process);


这里的$cmd =php ,则说明要执行php文件

Array
(
    [HOSTNAME] => localhost.localdomain
    [SELINUX_ROLE_REQUESTED] => 
    [SHELL] => /bin/bash
    [TERM] => xterm
    [HISTSIZE] => 1000
    [SSH_CLIENT] => 192.168.61.102 49385 22
    [SELINUX_USE_CURRENT_RANGE] => 
    [SSH_TTY] => /dev/pts/0
    [USER] => root
    [LS_COLORS] => rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.tbz=01;31:*.tbz2=01;31:*.bz=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
    [PATH] => /usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/mysql5.6/bin:/usr/local/php/php7/bin:/root/bin
    [MAIL] => /var/spool/mail/root
    [_] => /usr/local/php/php7/bin/php
    [PWD] => /home/song/php
    [LANG] => en_US.UTF-8
    [SELINUX_LEVEL_REQUESTED] => 
    [HISTCONTROL] => ignoredups
    [SSH_ASKPASS] => /usr/libexec/openssh/gnome-ssh-askpass
    [HOME] => /root
    [SHLVL] => 2
    [LOGNAME] => root
    [CVS_RSH] => ssh
    [SSH_CONNECTION] => 192.168.61.102 49385 192.168.61.103 22
    [LESSOPEN] => |/usr/bin/lesspipe.sh %s
    [G_BROKEN_FILENAMES] => 1
    [PHP_SELF] => Standard input code
    [SCRIPT_NAME] => Standard input code
    [SCRIPT_FILENAME] => 
    [PATH_TRANSLATED] => 
    [DOCUMENT_ROOT] => 
    [REQUEST_TIME_FLOAT] => 1542807455.4841
    [REQUEST_TIME] => 1542807455
    [argv] => Array
        (
            [0] => Standard input code
        )

    [argc] => 1
)





2、有名管道

 可以在两个独立的进程中独立通讯 。不再受制于父子进程,有名管道是一个文件,使用posix_mkfifo() 函数创建。这个函数有两个参数 第一个参数 文件路径(我们一般放在/tmp/目录下)第二个参数是maskmode 比如说 0644 、0755 这样的值。写数据、读数据  和我们操作普通文件是一样的。  fread($handle,100)  或 fgets($handle) 读取一行   fwrite($handle,$data)  使用fclose() 关闭资源


posix_mkfifo() 



下面我们举个例子:

写入有名管道:pipe.php

$file='/tmp/pipe';

if(!file_exists($file)){
  posix_mkfifo($file,0644);
}


$handle=fopen($file,'w');


if(!$handle){
  exit('create or open file failure.');
}



$count=0;
while($count<10){

fwrite($handle,$count."\n");

echo 'write '.$count.PHP_EOL;


sleep(1);
$count++;
}


fclose($handle);
unlink($file);


读有名管道:readpipe.php

$file='/tmp/pipe';
if(!file_exists($file)){
  exit("file not exists.\n");
}

$handle=fopen($file,'r');
while(true && !feof($handle)){
  $str=trim(fgets($handle),PHP_EOL);
  echo $str.PHP_EOL;
}

/*
while(true){
  
  $str=trim(fgets($handle),PHP_EOL);  
  
  if(!empty($str)){
    echo $str.PHP_EOL;
    if($str=='exit'){
      break;
    } 
  }

}
*/

fclose($handle);

/**

在这里做一个说明,如果我想把有名管道的读取,做一个守护进程,应该怎么做?

我这里想在最外层 做一个 while(true)的循环,

判断文件是否存在,如果存在就执行任务,如果文件不存在,就在这里while循环着。

也不会退出程序。
**/


也可以直接使用linux的命令行:  cat /tmp/pipe  来读取。


做个提醒:

场景1

我们在命令行中 执行  php pipe.php   ,如果没有打开客户端读取,则写操作是一直处于阻塞的状态,直到有进程读取数据,才进行

 

场景2

我们在命令行中 执行  php pipe.php   ,打开的客户端正在读取,突然间中断,则 发送方继续发送,直到发送完毕结束

会造成数据丢失,就不能收到全部数据,这个怎么容错?




正常通讯的方式,是可以收取到所有的数据,但这程序也不排除有其他问题吧。



3、无名管道与有名管道的区别

 

1、无名管道:无文件,只能在两个相关的两个进程之间使用,且有相同的祖先进程才可通讯。

2、有名管道:系统中有可见的管道文件,不想管的进程也能交换数据。