1.管道的介绍
Linux中,管道是一直特殊的内核文件,提供给进程读写操作。按进程亲缘性划分,管道分为:管道和命名管道(FIFO)两种。前者只能在父子兄弟兄妹进程中操作管道,而后者可以跨非亲缘进程的操作管道。管道的操作每一次打开只能是只读或者只写状态。
管道的读写规则
1.当没有数据可读时,read操作将阻塞(默认阻塞模式)
2.当管道满的时候,write操作将阻塞,直到read操作读取数据
3.当write操作后,再次write操作将阻塞,直到read操作读取数据
4.当所有管道写端的对应文件描述符被关闭则read操作返回0
5.当所有管道读端对应的文件描述符被关闭,则write操作会产生SIGPIP的信号
2.管道在PHP中的实现及表现行为
在PHP中,仅支持FIFO命名管道的方式, 通过文件的读写操作来实现管道文件的读写。通过posix_mkfifo函数 创建命名管道。
PHP命名管道的表现行为
1.当没有数据可读时,read操作将阻塞(和Linux标准一样,前提是write操作以后不能关闭管道,否则还是会非阻塞返回)
2.由于管道的大小,在创建时没办法指定,所以管道满的情况没办法验证。(也许可以设置linux内核关于默认管道大小的配置来进行验证)
3.读操作之后,会清空管道内容,写操作会追加内容到管道内。
3.管道读写阻塞,非亲缘进程的演示
<?php
$pipePath = '/tmp/test.pipe';
if (!file_exists($pipePath) && !posix_mkfifo($pipePath, 0666)) {
exit('create named pipe failed');
}
$pid = pcntl_fork();
if ($pid == -1) {
exit;
} else if ($pid) {
} else {
$i = 0;
while (1) {
$file = fopen($pipePath, 'w');
if ($size = fwrite($file, 'd' . $i++)) { //why not block until read
printf("time:%d PID:%d, 写入成功:大小为:%d字节" . PHP_EOL, date('s', time()), posix_getpid(), $size);
} else {
printf("PID:%d, 写入失败:" . PHP_EOL, posix_getpid());
}
sleep(1);
}
fclose($file);
}
pcntl_wait($status);
unlink($pipePath);
<?php
$pipePath = '/tmp/test.pipe';
if (!file_exists($pipePath) && !posix_mkfifo($pipePath, 0666)) {
exit('create named pipe failed');
}
while (1) {
$file = fopen($pipePath, 'r');
$data = fread($file, 10);
printf("time:%d PID:%d, 读取数据%s:" . PHP_EOL, date('s', time()), posix_getpid(), $data);
fclose($file);
sleep(10);
}
unlink($pipePath);

4.FIFO管道读写的演示
<?php
$pipePath = '/tmp/test.pipe';
if (!file_exists($pipePath) && !posix_mkfifo($pipePath, 0666)) {
exit('create named pipe failed');
}
$pid = pcntl_fork();
if ($pid == -1) {
exit;
} else if ($pid) {
} else {
$i = 0;
while (1) {
$file = fopen($pipePath, 'w');
printf("PID:%d, 开始写操作:" . PHP_EOL, posix_getpid());
if ($size = fwrite($file, 'd' . $i++)) { //why not block until read
printf("PID:%d, 写入成功:大小为:%d字节" . PHP_EOL, posix_getpid(), $size);
} else {
printf("PID:%d, 写入失败:" . PHP_EOL, posix_getpid());
}
sleep(1);
}
fclose($file);
}
while (1) {
printf("PID:%d, 开始读操作:" . PHP_EOL, posix_getpid());
$file = fopen($pipePath, 'r');
//stream_set_blocking($file, false);
// 管道的写操作不能关闭管道,否则会是非阻塞读取
//非阻塞状态下会立即返回,否则fread会阻塞等待直到读取length,或遇到eof才会返回
$data = fread($file, 10);
printf("PID:%d, 读取数据%s:" . PHP_EOL, posix_getpid(), $data);
fclose($file);
sleep(1);
}
unlink($pipePath);
pcntl_wait($status);
