管道

一、什么是管道

    Linux下一切皆文件,我们可以创建一个管道文件进行通信,实际上是调用pipe函数在内核中开辟一块缓冲区(称为管道)用于通信,管道是一种最基本的IPC机制,由pipe函数创建 。

    它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端(很好记,就像0是标准输入1是标准输出一样)。所以管道在用户程序看起来就像一个打开的文件,通过read(filedes[0]);或者write(filedes[1]);向这个文件读写数据其实是在读写内核缓冲区。pipe函数调⽤用成功返回0,调⽤用失败返回-1。 

管道的种类 :
1、普通管道:pipe也叫匿名管道
a.单向通信
b.只有在具有亲缘关系的进程间通信
c.具有同步机制
d.它是一种面向字节流的通信服务
e.生命周期随进程

2、流管道:s_pipe
a.进行双向传输,其他同上
b.只有在具有亲缘关系的进程间通信
c.具有同步机制
d.它是一种面向字节流的通信服务
e.生命周期随进程
3、命名管道:name_pipe
可以使毫不相干的进程进行通信,其他同匿名管道

二、使用管道进行通信

单向通信:

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


int main()
{
    int _pipe[2];
    int ret = pipe(_pipe);
    if(ret == -1){
        printf("creat pipe error!errno code is:%d\n",errno);
        return 1;
    }
    pid_t id = fork();
    if(id < 0)
    {
        printf("fork error!");
        return 2;
    }else if(id==0){//child
    close(_pipe[0]);//关掉读
    int i = 0;
    char* _mesg_c = NULL;
    while(i<20){
        if(i<10){  
    _mesg_c = " i am child!";
    write(_pipe[1], _mesg_c,strlen(_mesg_c)+1);//写入
        }
    sleep(1);
    i++;
    }
   // close(_pipe[1]);
 }else{//father
    close(_pipe[1]);//关掉写
    char _mesg[100];
    int j = 0;
      while(j<3) 
       {
    memset(_mesg,'\0',sizeof(_mesg));
    int ret = read(_pipe[0],_mesg,sizeof(_mesg));//读出
    printf("%s:code is %d\n",_mesg,ret);
           j++;
       }
       close(_pipe[0]);
       sleep(10);
     if(waitpid(id,NULL,0)< 0)
       {
           return 3;
       }
      }
    return 0;
}

分析:父子进程进行单向通信,子进程写,父进程读

使用管道需要注意以下四种特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标志)
1、如果所有指向管道写端的文件描述符都关闭了,(管道写端的引用计数为0),而仍然有进程从管道的读端读取数据,那么管道中剩余的数据都被读取之后,再次read将会返回0,就像读到文件结尾一样。也就是说,写端不会写,读端读完之后就会再等着写端去写,但是写端关闭了啊,不会写了,所以就出现上面说的情况。这就体现出了管道的同步机制。

2、如果有指向管道写端的文件描述符没有关闭,(管道写端的引用计数大于0)而持有管道写端的进程也没有向管道中写数据,这时有进程管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。通俗讲就是,读端读数据,一直读,但是写端不写了,而且写端并没有关闭,所以这时读端就会一直等着写端去写。这就造成了阻塞式等待。

3、如果所有指向管道读端的文件描述符都关闭了(管道读端的引用计数为0),这时有进程向管道的写端写数据,那么该进程会收到SIGPIPE,通常会导致进程异常终止。所以进程就会异常退出了。
4、如果有指向管道读端的文件描述符没关闭(管道读端的引用计数大于0)而持有管道读端的进程也没有从管道中读取数据,这时有进程向管道写端写数据,那么在管道写满时再写将会阻塞,直到管道中有了空位置才写入并返回,也就是管道的同步机制。
双向通信
命名管道:
命名管道可以在不相关的进程之间和不同计算机之间使用,服务器建立命名管道时给它指定一个名字,任何进程都可以通过该名字打开管道的另一端,根据给定的权限和服务器进程通信。而且,FIFO总是按照先进先出的原则工作,第一个被写入的数据首先从管道中读出。

创建:

#include<sys/types.h>
#include<sys/stat.h>
int mknod(const char*path,mode_t mod,dev_t dev);
int mkfifo(const char*path,mode_t mode);
参数:
path为创建的命名管道的路径名,mod为创建命名管道的模式,指明其存取权限,dev为设备值,该值文件创建的种类,它只在创建设备文件时才会用到。这两个函数掉用成功返回0,失败都返回-1.

命名管道实现通信的代码:

这里写代码片.PHONY:all
all:server client
client:client.c
    gcc -o $@ $^
server:server.c
    gcc -o $@ $^
.PHONY:clean
clean:
    rm -f server client

common.h

#ifndef __COMM_H_
#define __COMM_H_

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


#define  ONEPATH "./fifo_one.c"
#define  TWOPATH "./fifo_two.c"
#define  SIZE  1000

#endif

client.c

#include "comm.h"

int main()
{
    int _ret = mkfifo(ONEPATH,S_IFIFO|0666);
    if(_ret == -1)
    {
        printf("mkfifo ONEPATH error\n");
        return 1;
    }
    int fdone = open(ONEPATH,O_WRONLY);
    int fdtwo = open(TWOPATH,O_RDONLY);
    if((fdone<0)||(fdtwo<0))
      {
      printf("open file error\n");
          return 1;
      }
       char buf[SIZE];
       memset(buf,'\0',sizeof(buf));
       while(1)
       {
        printf("guangyuan->>>");
        scanf("%s,buf");
        int retone = write(fdone,buf,strlen(buf)+1);
           if(retone<0)
           {
               printf("write error\n");
               break;
           }

        printf("guangyuan<<<-");
        int rettwo = read(fdtwo,buf,sizeof(buf));
        if(rettwo<0)
           {
            printf("write error\n");
            break;
           }
        printf("%s\n",buf);
       }
       close(fdone);
       close(fdtwo);
    return 0;
}

server.c

#include "comm.h"

int main()
{
    int ret = mkfifo(TWOPATH,S_IFIFO|0666);
    if(ret == -1)
    {
        printf("mkfifo TWOPATH error\n");
        return 1;
    }
    int fdone = open(ONEPATH,O_RDONLY);
    int fdtwo = open(TWOPATH,O_WRONLY);
    if((fdone<0)||(fdtwo<0))
      {
      printf("open file error\n");
          return 1;
      }
       char buf[SIZE];
       memset(buf,'\0',sizeof(buf));
       while(1)
       {
        printf("mengnan<<<-");
        int retone = read(fdone,buf,sizeof(buf));
        if(retone<0)
        {
            printf("read error\n");
            break;
        }
         printf("%s\n",buf);


        printf("megnnan->>>");
        scanf("%s",buf);
        int rettwo = write(fdtwo,buf,strlen(buf)+1);

        if(rettwo<0)
           {
            printf("write error\n");
           break;
           }
       }
       close(fdone);
       close(fdtwo);
    return 0;
}

注意 :
命名管道的使用和匿名管道基本相同,只是在使用命名管道之前首先要使用open函数打开,因为命名管道是存在于硬盘上的文件,而管道是存在于内存中的特殊文件。
需要注意,使用open的几点:
1、调用open()打开命名管道可能会被阻塞,但是如果同时用读写方式(O_RDWR)打开,则一定不会造成阻塞。
2、 如果以只读方式(O_RDONLY)打开,则调用open()函数的进程将会被阻塞直到有写才能打开管道。
/3、 同样,以写方式(O_WRONLY)打开也会阻塞直到有读方式打开管道。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值