PHP进程通信-管道的使用

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);
写进程每次写3个字节,需要写4次才能 被读进程以10个字节的方式读取,之后读进程睡眠10秒,写进程就会阻塞

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);
如无特殊说明,文章均为本站原创,转载请注明出处。如发现有什么不对的地方,希望得到您的指点。

发表评论

电子邮件地址不会被公开。 必填项已用*标注