【操作系统】管道以及相关知识

管道是一种基本的进程间通信机制,用于有血缘关系的进程间单向数据流传输。它由内核缓冲区和两个文件描述符组成,读写端默认阻塞。创建管道使用pipe函数,父子进程通过fork后关闭不必要的端口进行通信。管道的读端在无数据时会阻塞,写端在缓冲区满时阻塞。可以通过fcntl设置非阻塞模式,并使用ulimit-a查看管道缓冲区大小。
摘要由CSDN通过智能技术生成

目录

管道

管道的概念

管道的原理

管道的局限性

创建管道-pipe函数

父子进程使用管道通信

管道练习

管道的读写行为

如何设置管道为非阻塞

如何查看管道缓冲区大小


管道

管道的概念

管道是一种最基本的IPC机制,也称匿名管道,应用于有血缘关系的进程之间,完成数据传递。调用pipe函数即可创建一个管道。

有如下特质:

  1. 管道的本质是一块内核缓冲区

  2. 由两个文件描述符引用,一个表示读端,一个表示写端。

  3. 规定数据从管道的写端流入管道,从读端流出。

  4. 当两个进程都终结的时候,管道也自动消失。

  5. 管道的读端和写端默认都是阻塞的。

管道的原理

  • 管道的实质是内核缓冲区,内部使用环形队列实现。(缓刑管道效率高)

  • 默认缓冲区大小为4K,可以使用ulimit -a命令获取大小。

  • 实际操作过程中缓冲区会根据数据压力做适当调整。

管道的局限性

  • 数据一旦被读走,便不在管道中存在,不可反复读取。

  • 数据只能在一个方向上流动,若要实现双向流动,必须使用两个管道

  • 只能在有血缘关系的进程间使用管道。

创建管道-pipe函数

函数作用:

创建一个管道

函数原型:

int pipe(int fd[2]);

函数参数:

若函数调用成功,fd[0]存放管道的读端,fd[1]存放管道的写端

返回值:

成功返回0;

失败返回-1,并设置errno值。

函数调用成功返回读端和写端的文件描述符,其中*fd[0]是读端, fd[1]是写端**向管道读写数据是通过使用这两个文件描述符进行的,读写管道的实质是操作内核缓冲区。*

管道创建成功以后,创建该管道的进程(父进程)同时掌握着管道的读端和写端。如何实现父子进程间通信呢?

父子进程使用管道通信

一个进程在由pipe()创建管道后,一般再fork一个子进程,然后通过管道实现父子进程间的通信(因此也不难推出,只要两个进程中存在血缘关系,这里的血缘关系指的是具有共同的祖先,都可以采用管道方式来进行通信)。*父子进程间具有相同的文件描述符,且指向同一个管道pipe*,其他没有关系的进程不能获得pipe()产生的两个文件描述符,也就不能利用同一个管道进行通信。

*第一步:父进程创建管道*

*第二步:父进程fork出子进程*

*第三步:父进程关闭fd[0],子进程关闭fd[1]*

*创建步骤总结:*

  • 父进程调用pipe函数创建管道,得到两个文件描述符fd[0]和fd[1],分别指向管道的读端和写端。

  • 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管。

  • 父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出,这样就实现了父子进程间通信。

管道练习

使用管道完成父子进程间通信?

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include <sys/wait.h>
 int main()
 {
         int fd[2];
         int ret = pipe(fd);
         if(ret<0)
         {       
                 perror("pipe error");
                 return -1;
         }
         //创建子进程
         pid_t pid = fork();
         if(pid<0)
         {
                  perror("fork error");
                 return -1;
 ​
         }
         else if(pid>0)
         {
                 //关闭读端
                 close(fd[0]);
                 write(fd[1],"hello world",strlen("hello world"));
                 wait(NULL);
         }
         else
         {
                 //关闭写段
                 close(fd[1]);
                 char buf[64];
                 memset(buf,0x00,sizeof(buf));
                 int n = read(fd[0],buf,sizeof(buf));
                 printf("read over, n==[%d],buf==[%s]\n",n,buf);
 }       return 0;
 }
          

管道的读写行为

  • 读操作

有数据

read正常读,返回读出的字节数

无数据

写端全部关闭

read解除阻塞,返回0, 相当于读文件读到了尾部

没有全部关闭

read阻塞

  • 写操作

读端全部关闭

管道破裂,进程终止, 内核给当前进程发SIGPIPE信号

读端没全部关闭

缓冲区写满了

write阻塞

缓冲区没有满

继续write

如何设置管道为非阻塞

默认情况下,管道的读写两端都是阻塞的,若要设置读或者写端为非阻塞,则可参

考下列三个步骤进行:

第1步: int flags = fcntl(fd[0], F_GETFL, 0);

第2步: flag |= O_NONBLOCK;

第3步: fcntl(fd[0], F_SETFL, flags);

若是读端设置为非阻塞:

Ø 写端没有关闭,管道中没有数据可读,则read返回-1;

Ø 写端没有关闭,管道中有数据可读,则read返回实际读到的字节数

Ø 写端已经关闭,管道中有数据可读,则read返回实际读到的字节数

Ø 写端已经关闭,管道中没有数据可读,则read返回0

如何查看管道缓冲区大小

  • 命令

ulimit -a

  • 函数

long fpathconf(int fd, int name);

printf("pipe size==[%ld]\n", fpathconf(fd[0], _PC_PIPE_BUF));

printf("pipe size==[%ld]\n", fpathconf(fd[1], _PC_PIPE_BUF));

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值