Linux进程间通信,IPC-PIPE管道

一、管道

管道是IPC通信中的一种最基本的通信机制,所有UNIX系统都提供此种通信机制。

管道有两种局限性:

  1. 是半双工的通信方式,同一时刻仅支持数据单向流动
  2. 只能在具有亲缘关系的两个进程间使用

二、管道函数

2.1 pipe函数

/*
功能:创建管道
参数:
     pipefd[2],用于存储返回的两个文件描述符

返回值:成功返回 0  并由参数fd返回两个文件描述符:fd[0]为读描述符,fd[1]为写描述符
       失败返回 -1

*/
#include <unistd.h>
int pipe(int pipefd[2]);

2.2 管道的读写:

父进程到子进程:父进程开启写端fd[1],子进程开启读端fd[0];

子进程到父进程:子进程开启读端fd[1],父进程开启读端fd[0];

注意:

  • 管道是一种半双工的通信方式,所以在同一时刻只能有一种数据流向。
  • 管道中的数据只能读一次,数据一旦被读端读取就从管道中删除。
  • 管道的读写操作默认都是阻塞的。
  • 当写入管道的数据大于PIPE_BUF,将不再保证写入的原子性,写满空闲缓存区后,写操作返回。
  • 当多个进程(父进程可以有多个子进程)同时进行写操作,并且要求写的字节数超过PIPE_BUF,那么所写的数据就有可能会相互交叉。

2.3 读写状态分析

读端开启:

1.写管道未关闭:管道内有数据 > 不阻塞。读取数据,如果管道内数据被读完了,再继续读,阻塞。

                            管道内无数据 > 阻塞。直到写端写入数据,阻塞解除,开始读。

2.写管道已关闭:管道内有数据  > 不阻塞。读取数据,如果管道内数据被读完了,read返回0。

                            管道内无数据 > 不阻塞。read返回0。

写端开启:

1.读管道未关闭:管道未写满 > 写入数据。如果缓冲区已满,写操作返回,阻塞。

                            管道已写满 > 阻塞。直到读端读取数据后,阻塞解除,缓冲区有空闲区域,写端写入。

2.读管道已关闭:产生信号SIGPIPE,如果捕捉该信号并从其处理函数返回,则write返回-1,error设置为EPIPE。

2.4 编程示例:

通常我们会先调用pipe,接着调用fork,从而创建从父进程到子进程的IPC通道

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

#define  MSG_STR_C "child transferring information to parent"
#define  MSG_STR_P "parent transferring information to child"

int main(int argc,char **argv)
{
        pid_t   pid;
        int     fd[2];
        int     rv=0;
        char    buf[1024];

        memset(fd,0,sizeof(fd));
        memset(buf,0,sizeof(buf));
        pipe(fd);   //创建管道,生成两个文件描述符fd[0],fd[1]
        pid=fork();   //创建子进程
        if(pid < 0)
        {
                printf("fork failure:%s\n",strerror(errno));
                return 0;
        }
        if(pid == 0)
        {
                sleep(2);
                printf("child start running\n");

                //子进程打开写端,向缓存区内写入数据:MSG_STR_C
                rv=write(fd[1],MSG_STR_C,strlen(MSG_STR_C));  
                if(rv <= 0)
                {
                        printf("child failure:%s\n",strerror(errno));
                        goto cleanup;
                }
                sleep(1);
           
                //父进程向缓存区写入数据,子进程则打开读端,读取缓存区内父进程写入的数据
                rv=read(fd[0],buf,sizeof(buf));  
                if(rv <= 0)
                {
                        printf("child failure:%s\n",strerror(errno));
                        goto cleanup;
                }
                printf("read %d char:%s\n",rv,buf);
        }
        if(pid > 0)
        {
                sleep(2);
                printf("parent start running\n");

                //子进程向缓存区写入数据,父进程则打开读端,读取缓存区内子进程写入的数据
                rv=read(fd[0],buf,sizeof(buf));  
                if(rv <= 0)
                {
                        printf("parent failure:%s\n",strerror(errno));
                        goto cleanup;
                }
                printf("read %d char:%s\n",rv,buf);
                sleep(1);

                //父进程打开写端,向缓存区内写入数据:MSG_STR_P
                rv=write(fd[1],MSG_STR_P,strlen(MSG_STR_P));  
                if(rv <= 0)
                {
                        printf("parent failure:%s\n",strerror(errno));
                        goto cleanup;
                }

        }
cleanup:
        close(fd[0]);
        close(fd[1]);
        return 0;
}

注意:使用sleep函数并不能完全保证父子进程间读写端是否对应,可以使用信号signal或semaphore PV操作等来提升程序的可靠性。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值