进程间通信方式之管道--无名管道

紧接昨晚的利用消息队列进行进程间通信,今天讲讲利用管道进行进程间通信。

在Linux中,管道通信是一种使用非常频繁的通信机制。管道在本质上也是一种文件,但是又有区别于一般的文件,因为管道可以克服使用文件会出现的问题。

管道实际上是一个固定大小的缓冲区,该缓冲区的大小为1页,即4K字节,使得它的大小不像文件那样不加检验地增长。同时使用单个固定缓冲区会带来问题,比如在写管道时可能变满,当这种情况发生时,随后对管道的write()调用将默认地被阻塞,等待某些数据被读取,以便腾出足够的空间供write()调用写。读取进程也可能工作得比写进程快。当所有当前进程数据已被读取时,管道变空。当这种情况发生时,一个随后的read()调用将默认地被阻塞,等待某些数据被写入,这解决了read()调用返回文件结束的问题。从管道读数据是一次性操作,数据一旦被读,它就从管道中被抛弃,释放空间以便写更多的数据。

首先阐述一下管道的特点:

1、管道是半双工的,即数据只能向一个方向流动,如果需要实现双方通信时,就需要建立两个管道;

2、只能用于父子进程之间或者兄弟进程间这些有亲缘关系的进程;

3、单独构成一种独立的文件系统。因为管道对于管道两端的进程而言,就是一个文件,但是又区别于普通文件,不属于某种文件系统,并且只存于内存中;

4、数据的读写:一个进程向管道中写的内容被管道另一端的进程读取。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读取数据。

管道用于不同进程间的通信,分为无名管道和有名管道。利用管道通信,通常首先创建一个管道,再通过fork()函数创建一个子进程,该子进程会继承父进程所创建的管道。

无名管道的建立:

pipe函数

功能:建立无名管道

头文件:#include <unistd.h>

原型:int pipe(int fileds[2])

说明:参数fileds返回文件描述符,fileds[0]是管道的读取端,fileds[1]是管道的写入端

返回值:成功,返回0,否则返回-1,并将错误原因存于errno中。

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

int main()
{
        int pipe_fd[2];
        if(pipe(pipe_fd)<0)
        {
        printf("pipe create error\n");
        return -1;
        }
        else 
                printf("pipe create success\n");
        close(pipe_fd[0]);
        close(pipe_fd[1]);
}

调试结果:

pipe create success

注意:必须在调用fork之前调用pipe(),否则子进程不能继承文件描述符

读写无名管道:

管道的两端只能固定地读写,如果试图从管道写端读写数据,或者向管道读端写入数据都会导致错误发生。同时,一般的文件的I/O函数都可以用于管道,例如close、read、write等。

从管道中读取数据:

1、如果写端不存在,则认为已经读到了数据的末尾,读函数返回的读出字节数为0.

2、当管道的写端存在时,如果请求的字节数目大于PIPE_BUF,则返回管道中现有的字节数;如果请求的字节数目不大于PIPE_BUF,则返回管道中现有数据字节数,返回请求的字节数。


#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
        int pipe_fd[2];
        pid_t pid;
        char buf_r[100];
        char* p_wbuf;
        int r_num;

        memset(buf_r,0,sizeof(buf_r));

        if(pipe(pipe_fd)<0)
        {
                printf("pipe create error\n");
                return -1;
        }

        if((pid=fork())==0)  
        {
                printf("\n");
                close(pipe_fd[1]);
                sleep(2); 
                if((r_num=read(pipe_fd[0],buf_r,100))>0)
                {
                        printf(   "%d numbers read from the pipe is %s\n",r_num,buf_r);
                }
                close(pipe_fd[0]);
                exit(0);
        }
        else if(pid>0)
        {
                close(pipe_fd[0]);
                if(write(pipe_fd[1],"Hello",5)!=-1)
                        printf("parent write1 Hello!\n");
                if(write(pipe_fd[1]," Pipe",5)!=-1)
                        printf("parent write2 Pipe!\n");
                close(pipe_fd[1]);
                sleep(3);
                waitpid(pid,NULL,0); 
                exit(0);
        }
        return 0;
}

调试结果:

parent write1 Hello!
parent write2 Pipe!
10 numbers read from the pipe is Hello Pipe

注意:管道写端关闭后,写入的数据将一直都存在,直到全部读出为止。

向管道中写入数据时,Linux不会保证写入的原子性,管道的缓冲区一有空闲区域,写进程就会试图向管道写入数据,如果读进程不读走管道缓冲区的数据,那么写操作将一直处于阻塞。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值