管道-无名管道

管道(pipe)概述:(怎样使用管道实现父子进程间通信)不相关的两个进程无法通过无名管道进行进程间通信。

管道又称无名管道(没有名字),无名管道是一种特殊类型的文件,在应用层体现为两个打开的文件描述符

管道是最古老的UNIX IPC方式,其特点是:

1>   半双工,数据在同一时刻只能在一个方向上流动,读写不能同时进行(单工-收音机)

2>   由两个文件描述符引用,一个表示读端fd【0】,一个表示写端fd【1】;

       数据只能从管道的一端写入,从另一端读出。

3>   写入管道中的数据遵循先入先出的规则。

4>   管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据的格式,如多少字节算一个消息等。

5>   管道不是普通的文件,不属于某个文件系统,只存在于内存中。

6>   管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。

7>   从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写更多的数据。即数据一旦被读走,便不在管道中存在,不可反复读。

8>   管道没有名字,只能在具有公共祖先的进程之间使用。

Linux shell允许重定向,而重定向使用的就是管道。例如:ls | more

创建管道的那个进程(父进程)具有管道的两个文件描述符,继承这个进程(子进程)也具有这两个文件描述符,所以就可以对管道进行操作。父进程创建管道—>得到管道的两个读写端->创建子进程,子进程继承文件描述符(管道的两个读写端)->父子进程进行进程间通信。

实际上,管道是一个固定大小的缓冲区,在linux中,该缓冲区的大小是4k(8×512Bytes),即一页。

创建管道:

#include<unistd.h>
int pipe(int filedes[2]);

功能:经由参数filedes返回两个文件描述符

参数:

>>filedes为int型数组的首地址,其存放了管道的文件描述符filedes[0],filedes[1].

>>filedes[0]为读而打开,filedes[1]为写而打开管道,filedes[0]的输出是filedes[1]的输入。

返回值:成功返回0,失败返回-1.

从管道中读数据的特点:

1) 默认用read函数从管道中读数据是阻塞的。

2) 调用write函数向管道里写数据,当缓冲区已满时write也会阻塞

3) 通信过程中,读端口全部关闭后,写进程向管道内写数据时,写进程会(收到SIGPIPE信号)退出。

#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc,char * argv[])
{
        int fd_pipe[2];
        char buf1[] = "hello world";
        char buf2[15];
        pid_t pid;
        if(pipe(fd_pipe) < 0)
                perror("pipe");
        pid = fork();
        if(pid < 0){
                perror("fork");
                exit(-1);
        }
        if(pid == 0){
                write(fd_pipe[1],buf1,strlen(buf1));
                _exit(0);
        }else{
                wait(NULL);//parent process,wait any child process's termination
                memset(buf2,0,sizeof(buf2));
                read(fd_pipe[0],buf2,sizeof(buf1));
                printf("buf2 = [%s]\n",buf2);
        }
        return 0;
}

运行结果:

#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc,char * argv[])
{
        int fd_pipe[2];
        char buf[] = "hello world";
        pid_t pid;
        if(pipe(fd_pipe) < 0)
                perror("pipe");
        pid = fork();
        if(pid < 0){
                perror("fork");
                exit(-1);
        }
        if(pid == 0){
                int i = 0;
                close(fd_pipe[0]);
                while(1){
                        write(fd_pipe[1],buf,strlen(buf));
                        i++;
                        printf("i = %d\n",i);
                        sleep(1);
                }
                _exit(0);
        }else{
                int i;
                for(i = 0;i < 5;i++){
                        memset(buf,0,sizeof(buf));
                        read(fd_pipe[0],buf,sizeof(buf));
                        printf("read: buf = %s\n",buf);
                }
                close(fd_pipe[0]);
                wait(NULL);//parent process,wait any child process's termination
        }
        return 0;
}

父子进程都关闭读端之后,写端也将自动关闭,运行结果如下:

读写行为总结

1. 如果所有指向管道写端的文件描述符都关闭了(管道写端引用计数为0),而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。

2. 如果有指向管道写端的文件描述符没关闭(管道写端引用计数大于0),而持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。

3. 如果所有指向管道读端的文件描述符都关闭了(管道读端引用计数为0),这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。当然也可以对SIGPIPE信号实施捕捉,不终止进程。具体方法信号章节详细介绍。

4. 如果有指向管道读端的文件描述符没关闭(管道读端引用计数大于0),而持有管道读端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再次write会阻塞,直到管道中有空位置了才写入数据并返回。

读管道:

1. 管道中有数据,read返回实际读到的字节数。

2. 管道中无数据:

(1) 管道写端被全部关闭,read返回0 (好像读到文件结尾) 

(2) 写端没有全部被关闭,read阻塞等待(不久的将来可能有数据递达,此时会让出cpu)

写管道:

1. 管道读端全部被关闭, 进程异常终止(也可使用捕捉SIGPIPE信号,使进程不终止)

2. 管道读端没有全部关闭:

(1) 管道已满,write阻塞。

(2) 管道未满,write将数据写入,并返回实际写入的字节数

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值