Linux系统编程——进程间的通信(二)无名管道和命名管道

一、无名管道

管道通常是指无名管道,(因为没有文件名,所以叫无名管道)是 UNIX 系统IPC最古老的形式。

特点:
1、它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
2、它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
3、它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
4、管道中的数据被读走了,管道中就没有数据了。

原理
当建立一个管道时,由参数fd返回两个文件描述符:fd[0]为读而打开,fd[1]为写而打开,fd[1]的输出是fd[0]的输入,管道是建立在内核之中的

如:
在这里插入图片描述
如果要关闭管道,只需要将fd[0]和fd[1]的文件描述符关闭即可
创建
单个进程中的管道几乎没有任何用处。所以,调用pipe的进程接着调用fork,这样就创建了从父进程到子进程的IPC通道。 父子进程都有读端和写端,子进程的内容都是从父进程复制过来的 。
对于从父进程到子进程的管道父进程关闭管道的读端(fd[0])子进程则关闭写端(fd[1]),反之也是一样

在这里插入图片描述
头文件

#include <unistd.h>

函数原型

int pipe(int pipefd[2]);

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

例如

创建由父进程到子进程的管道,父进程写入hello world,子进程读取数据到buf,然后打印输出

#include <stdio.h>
#include <unistd.h>
#include <string.h>
//int pipe(int pipefd[2]);
int main()
{
    int fd[2];
    int pid;
    char buf[128] = {0};
    if(pipe(fd) == -1){            //等于-1表示失败
        printf("creat pipe fail\n");
    }
    pid = fork();                  //创建进程
    if(pid < 0){                   //创建进程失败
        printf("creat pid fail\n");
    }
    else if(pid > 0){             //进入父进程
        printf("this is father\n");
        close(fd[0]);             //关闭读
        write(fd[1],"hello world",strlen("hello world")); //往fd[1]写字符串hello world
    }
    else{                        //进入子进程
        printf("this is child\n"); 
        close(fd[1]);           //关闭写
        read(fd[0],buf,128);    //将父进程中写入的字符串从fd[0]中读出来
        printf("read father %s\n",buf); //打印
    }
    return 0;
}

在这里插入图片描述

二、有名管道(FIFO)

FIFO可以在无关的进程之间交换数据,与无名管道不同

有名管道也叫命名管道,是在文件系统目录中存在一个管道文件中

管道文件仅仅是文件系统中的标示,并不在磁盘上占据空间。在使用时,在内存上开辟空间,作为两个进程数据交互的通道。

管道文件的创建:

#include <sys/types.h>
#include <sys/stat.h>   //头文件
int mkfifo(const char *pathname, mode_t mode); //函数原型

mode和open函数中的相似(0600)可读可写open
pathname:路径名字
当创建了一个FIFO,就可以用一般的文件操作它,当打开一个FIFO时,要看是否设置了非阻塞标志(O_NONBLOCK)

如果没有指定O_NONBLOCK(默认没有设置),只读open要阻塞到某个其他进程为写而打开此FIFO(文件),类似的,只写open要阻塞到某个其他进程为读而打开它。

如果指定了O_NONBLOCK,则只读open立即返回,而只写open将出错返回-1,如果已经没有进程为读而打开该FIFO,其errno置ENXIO。(如果管道存在,也会出错)

创建一个管道

	#include <sys/types.h>
    #include <sys/stat.h> 
    #include <stdio.h> 
    if((mkfifo("./file",0600) == -1) && errno != EEXIST){ //创建管道
        printf("mkfifom fail\n");
        perror("why");
    }

在这里插入图片描述
可以看到生成了一个可读可写的文件,这个文件就是创建的管道

示例代码
一个文件是将数据写入管道,另一个文件是将第一个写入管道中的数据读出来,然后输出

将管道中的文件读到buf中,再输出
read.c

#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
//int mkfifo(const char *pathname, mode_t mode);
int main()
{
    char buf[30] = {0};
    int nread = 0;
    int cnt = 0;
    if((mkfifo("./file",0600) == -1) && errno != EEXIST){ //创建管道
        printf("mkfifom fail\n");
        perror("why");
    }
    int fd = open("./file",O_RDONLY);  //以只读方式打开管道
    printf("open success\n");
    while(1){
        nread = read(fd,buf,30);      //将管道中的数据读到buf中
        printf("read %d byte  read file:%s\n",nread,buf); //打印字节数和读出的内容
        cnt++;
        if(cnt == 5){
                break;
        }
    }
    close(fd);  //关闭管道
    return 0;
}

write.c
向管道里面写数据

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
int main()
{
    int cnt = 0;
    char *str = "hello world";   //要向管道写入的字符串
    int fd = open("./file",O_WRONLY); //以只写的方式打开管道
    printf("write open success\n");
    while(1){                       //连续输入5个
        write(fd,str,strlen(str));  //将字符串str写入管道fd
        sleep(1);                   //延时一秒
        cnt++;
        if(cnt == 5){
              break;
        }
    }
    close(fd);                   //关闭管道
    return 0;
}

这里如果运行read.c的话会造成read堵塞,因为只读open要阻塞到某个其他进程为写而打开此管道,也就是说这里要运行write时,read才不会堵塞,
点击运行read.c
在这里插入图片描述
可以看到发生了阻塞,然后再运行write.c
在这里插入图片描述
read成功的读到数据
在这里插入图片描述
如果想开始不堵塞的话

 int fd = open("./file",O_RDONLY | O_NONBLOCK);  
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值