进程间通信之管道

目录

一、为什么需要进行进程通信呢?

1.1进程具有独立性

1.2进程独立性的优缺点:

1.3进程通信的手段:

二、匿名管道

1.1管道符号

 1.2管道的本质

 1.3管道的接口

1.4从PCB角度理解管道

 1.5代码验证

三、匿名管道的特性

1.半双工

 2.只能支持有亲缘关系的进程通信

3、管道的生命周期是跟随进程的

4、管道的大小是64K

 5、管道提供字节流服务

6、pipe_size

7、阻塞属性

8.、非阻塞属性

 9、文件描述符数值的含义

10、为什么文件描述符的属性信息需要用按位或的方式进行设置?(*****)

11、代码验证非阻塞属性

四、命名管道

创建命名管道

命名管道的特性:

代码验证:


一、为什么需要进行进程通信呢?

1.1进程具有独立性

每一个进程的数据都是存储在物理内存当中的,进程通过自己的虚拟地址空间去进行访问,访问的时候通过各自的页表的映射关系,访问到物理内存当中去

从进程的角度看,每个进程都认为首己拥有4G(32位下)的空间,至 于物理内存当中属于如何存储,页 表如何映射,进程是不清楚。 这 也造就了进程的独立行。

1.2进程独立性的优缺点:

优点:让每个进程运行的时候都是独立进行的,数据不会窜

缺点:如果两个进程想要进行数据交换时,由于进程的独立性,就不是那么的方便了

所以:   进程问通信本质上是进程和进程之间交换数据的手段

1.3进程通信的手段:

                            管道,共享内存,消息队列,信号量,网络

其中网络是最大的进程间通信方式

二、匿名管道

1.1管道符号

ps aux |  grep   文件名

 1.2管道的本质

管道在内核里是一块缓冲区,供进程进行读写、数据交换

 1.3管道的接口

  • 1.3.1理解参数的含义         程序猿通过该函数创建一个匿名管道
  • int  pipe ( int  pipefd [2] );

  • 参数:
    • 参数为出参,也就是pipe fd【0】【1】的值是p ipe函数进行填充的,调用者进行使用
    • pipefd是数组,有两个元素
    • pipefd[0] :管道的读端
    • pipefd[1] :管道的写端

为什么pipefd[0]和pipefd[1]能称为 管道的读写两端?

因为这两个元素保存的内容是文件描述符

  • 返回值:
    • 0  :  创建成功
    • -1 :  创建失败

1.4从PCB角度理解管道

 1.5代码验证

1.5.1验证pipe函数

1、验证pipe函数是出参

 

 可以发现,调用pipe函数之前,数组元素的值是随机的,调用之后,值就变位了3和4

2、验证fd[0],fd[1]保存的是文件描述符、

我们可以让程序一直不退出,在去查看/proc/【pid】/fd下面去查看是不算多了对应的文件描述符

 可以发现,验证成功

3、利用write和read来进行验证读端和写端

 

 可以发现,验证成功

4、父子进程进行通信

 父子进程要通过匿名管道进行通信,核心点在于父子进程都要能够读写通道,即父子进程都要有管道读写两端的文件描述符

因为子进程是要拷贝父进程的PCB的,所以父子之间进行通信,一定是先创建管道再创建子进程的,否则子进程就没有管道的读写两端的文件描述符

 代码实现:

目标:
           实现父子进程的通信
    做法:
       1、父进程创建管道                                                         
       2、父进程创建子进程
    
       3、父进程在他的代码逻辑中写
       4、子进程在他的代码逻辑中读

代码如下:

 

可以发现,每次运行,子进程都能读出来父进程写入的内容 

那么就要疑问了:

原因:

当管道当中没有数据的时候,子进程调用的read函数去管道当中进行读的时候,会阻塞,直到管道当中有内容,read函数读回来之后,read才返回

怎么验证:

pstack  子进程进程号,查看子进程在干什么

我们让一进入父进程就sleep(1000),再去看看子进程调用read函数的状态是不是阻塞

 我们可以看到确实是阻塞的状态

三、匿名管道的特性

1.半双工

数据只能从写端流向读端,单向通信

 2.只能支持有亲缘关系的进程通信

没有标识符,其他进程是没有办法通过标识符找到这个匿名管道对应的缓冲区,只能具有亲缘性关系的进程进行进程间通信:因为其他进程没有管道的读写两端的文件描述符,所以这就要求父进程先创建管道再创建子进程

3、管道的生命周期是跟随进程的

进程退出了,管道在内核当中就被销毁了

4、管道的大小是64K

怎么验证:

如果没有人读,疯狂往管道里面写,会发生什么呢?

 运行一下:

 1、我们可以看到最终写65536字节写满了也就是64kb

2、write往管道中写的时候阻塞掉了

这时候使用pastack去查看

 结论:当管道写满之后,再调用write函数往管道去写的时候,write会阻塞

 5、管道提供字节流服务

1、先后两次写入管道的数据之间没有间隔

 2、数据是被读走的,而并非拷贝走的 

6、pipe_size

pipe_size 的大小是4096,并非是管道的大小(pipe_buffer : 64k)

 什么是原子性?

原子性这个词是诞生于1980年代,那个时候世界上最小的物质是原子,所以起名为原子性

原子性:意味着不可以分割,已经是最小的单位了

原子性:在计算机里面表示非黑即白,不存在中间状态,一个操作要么执行到底,要么就不执行

7、阻塞属性

读写两端的文件描述符初始的属性为阻塞属性

  • 当write一直调用写,读端不去读, 则写满之 后write会阻寒

同理,当read一直进行读,当管道的内 部被读完之后,则read会阻寨

8.、非阻塞属性

第一件事情:设置非阻塞属性

第二件事情:写代码验证非阻塞属性,给管道读写两端带来的特性

  • int fcntl(int fd,int cmd,  ···  /*arg*/ );
  • 参数: 
  •   fd:待要操作的文件描述符  (fd[0]  ,  fd[1])
  •  cmd:告知fcntl函数做什么操作
    •    F_GETFL:获取文件描述符的属性信息
    •         通过返回值获取管道读端的属性信息:

                        int   flag  =fcntl(fd[0],F_GETFL)

    •  F_SETFL:设置文件描述符的属性信息,设置新的属性放到可变参数列表当中

  • 返回值:
    • F_GETFL:返回文件描述符的属性信息
    • F_SETFL:
      • 0:设置成功
      • -1: 设置失败

代码:

我们先来获取文件描述符的属性信息

我们分别获取fd [0]和fd[1]的文件描述符的属性,发现值分别是0和1

 

我们再来看看设置非阻塞属性后的文件描述符的属性,和之前的进行对比一下

 可以发现,设置了非阻塞属性以后,文件描述符的属性信息变为了2048

 9、文件描述符数值的含义

1、O_NONBLOCK的值是多少

 我们可以去操作系统中查找O_NONBLOCK的定义

 我们可以看到,O_NONBLOCK的定义转化为10进制数的值正好是2048

2、fd[0]的属性信息为啥是0呢?

 3、fd[1]的属性信息为啥是1呢?

fd[1]这个文件描述符的属性为

10、为什么文件描述符的属性信息需要用按位或的方式进行设置?(*****)

结论: 文件描述符的属性信息,再操作系统内核当中用比特位来进行表示

 系统接口中,文件打开方式的宏,在内核当中的使用方式是位图

                O_RDONLY    O_CREAT    O_NONBLOCK

为什么操作系统中大量使用位图?

1、位操作起来快速

2、节省空间

11、代码验证非阻塞属性

一、读设置成为非阻塞属性     fd[0]为非阻塞

1、写不关闭,一直读,读端调用read函数之后,返回值为-1,errno设置为EAGATN

                进程可正常操作fd[1],可以正常操作写端

这时候执行一下,发现资源不可用

 资源不可用是是因为管道当中没有内容可读

2、写关闭,一直读,读端read函数返回0,表示什么也没有读到

 这时候彻底关闭写端,再执行一下代码

、写设置成为非阻塞属性     fd[1]为非阻塞

1、读不关闭,一只写,当把管道写满之后,在调用write,就会返回-1

 

 2、读关闭

四、命名管道

创建命名管道

1、mkfifo命令:

 

 结论:

1、数据还是存储在内核的缓冲区当中的

2、管道文件的作用是为了让不同的进程可以找到这块缓冲区

2.函数创建

mkfifo函数:(first inpit first output)  先进先出

 

  • int mkfifo(const char *pathname, mode_ t mode ) ;
  • 参数:
    • pathname ;要创建的命名管道文件的路径,以及文件名
    • mode_ t :命名管道文件的权限,八进制数组 (0664)
  • 返回值:
    • 成功: 0
    • 失败: -1

命名管道的特性:

支持不同的进程进行通信,不依赖亲缘性

因为不同的进程可以通过管道文件去找到管道(操作系统内核的缓冲区)

代码验证:

我们分别写两个进程 一个write,一个read,让这两个进程进行通信

写好代码,运行一下:

 

 我们可以看到,read确实是在一直读取管道的的数据

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值