前言
认识进程间通信方式也是十分重要的一部分。因为我们在实现数据传输,资源共享,通知事件和控制进程等方面,其实都是两个进程之间在互相通信。学习进程间通信的方式,主要是为了进程与进程间交换数据时使用的。常用的进程间通信方式有四种:管道,共享内存,消息队列,信号量。本篇文章只对管道和共享内存做详细介绍。
一、管道
1.匿名管道
1.管道符号
之前命令 ps aux | grep “进程号” 在看进程信息的时候经常使用,其中 | 就是管道的符号。ps和grep本质上都是可执行程序。
2.管道的本质
管道本质在内核当中就是一块供不同进程之间进行读写的缓冲区。
本质上是ps程序将数据放入缓冲区中,grep程序再将数据从缓冲区中读走。
3.管道的接口
pipe函数
参数分析:
pipefd:pipefd是一个数组,参数是输出型参数。pipefd中用pipefd[0]和pipefd[1]进行填充,当中保存的是2个文件描述符,分别对应管道的读写两端。pipefd[0]对应读端,pipefd[1]对应写端。
返回值:
0:创建匿名管道成功
-1:创建失败
4.从PCB的角度理解管道
因为匿名管道的特性,导致匿名管道在内存当中创建出来的缓冲区没有标识符,因此其他进程没办法直接找到这个缓冲区,但是创建的进程可以通过管道的读写两端对文件描述符进行操作。
原理:
当父进程创建一个子进程,子进程是拷贝父进程的PCB,因此子进程中也会有fd[1]和fd[1],因此也有了对管道的读写权限,父子进程就可以互相通信了。
5.匿名管道的特性:
①管道是半双工通信的,数据只能从读端流向写端
②匿名管道在内存当中创建出来的缓冲区没有标识符,因此其他进程没办法直接找到这个缓冲区,但是创建的进程可以通过管道的读写两端对文件描述符进行操作
③匿名管道只支持有亲缘关系的进程间进行通信
④当文件描述符保持基础属性(阻塞)时,调用read读空管道,read函数会阻塞
⑤管道大小为64k(4096字节)
⑥当文件描述符保持基础属性(阻塞)时,一直调用write将管道写满后,write函数会阻塞
⑦管道的生命周期跟随进程,
⑧管道提供字节流服务(描述符的前后两个数据之间没有明显边界)
⑨使用fd[1]从管道读数据时时直接将数据从管道中读走,不是拷贝
⑩对管道的读写操作,当读写大小没有超过pipe_size时,管道保持原子性(不存在中间状态,要么还没开始读,要么已经读完了)
6.如何将文件描述符设置成非阻塞属性:O_NONBLOCK
fcntl函数
参数分析:
fd:文件描述符
cmd:设置的命令(告知fcntl函数做什么)
…arg:表示可变参数列表
当cmd传递F_GRTFL:表示获取文件描述符属性,arg就不用设置了。
当cmd传递F_SETFL:表示设置文件描述符属性,设置的属性用第三个参数arg设置。在对第三个参数设置阻塞属性时,需要将文件描述符原有的属性加上,并使用按位或来设置。
返回值:
当传递F_GRTFL则返回文件描述符属性
当传递F_SETFL返回0或-1
7.匿名管道的非阻塞属性
①将读设置成非阻塞属性
1.写不关闭,一直读:读端调用read函数之后,返回值为-1,出错吗为EAGAIN
2.写关闭,一直写:读端read函数返回0, 表示什么都没有读到
②将写设置成非阻塞属性:
1.读不关闭,一直写:当把管道写满之后,则再调用write,会返回-1
2.读关闭,一直写:写端调用write函数会崩溃。本质上时写端进程收到了SIGPIPE信号,导致写端进程崩溃。
2.命名管道
区别于匿名管道,命名管道是有标识符的管道,可以被其他进程找到。命名管道支持不同进程间的通信。
1.命名管道的命令创建
使用mkfifo命令+[文件名]:就创建了一个命名管道文件
这个管道文件可以被不同的进程访问。
2.命名管道的函数创建
使用mkfifo函数
参数分析:
pathname:要创建命名管道文件的路径
mode:命名管道文件的权限,用八进制数字表示
二、共享内存
1.共享内存原理
再物理内存中开辟一段空间,不同进程通过页表将该空间映射到自己的进程虚拟地址空间中,不同进程通过操作自己进程虚拟地址空间来操作共享内存。如下图:
2.共享内存接口
对共享内存的操作有三步:创建(创建共享内存),附加(将进程附加到该共享内存上),分离(将虚拟地址和物理地址之间的映射关系删除)
①创建:shmget函数
参数分析:
key:共享内存标识符
size:共享内存大小
shmflg:共享内存的属性信息,需要传递参数如下
(IPC_CREAT:如果共享内存不存在则创建。
IPC_EXCL:需要搭配IPC_CREAT按位或使用。IPC_EXCL | IPC_CREAT:表示如果共享内存存在则报错,不存在则创建。也就是一定要获取到自建创建的共享内存。
使用时还要按位或上共享内存的权限,使用八进制数字表示。)
返回值:成功创建返回共享内存操作句柄
②附加:shmat函数
参数分析:
shmid:共享内存操作句柄
shmaddr:想要将共享内存附加到共享区中哪一个地址,一般传递NULL,让操作系统进行分配。
shmflg:共享内存的属性信息,SHM_RDONLY:只读方式,0:可读可写
返回值:
成功附加:返回附加的虚拟地址
失败:NULL
③分离:shmdt函数
shmaddr:shmat函数的返回值
④shmctl函数:
功能:用于控制共享内存
参数分析:
shmid:共享内存操作句柄
cmd:告诉shmctl函数完成什么事情(有三个可取值)
IPC_SET:设置共享内存属性信息
IPC_STAT:获取共享内存属性信息
IPC_RMID:删除共享内存,第三个参数传递NULL
3.共享内存特性:
①共享内存是覆盖写的方式,读的时候是访问地址
②共享内存的生命周期跟随操作系统内核,可以使用ipcs命令查看
4.共享内存的删除特性
①使用ipcrm -m [共享内存标识符]可以删除共享内存。一旦共享内存被删除之后,本质在物理内存中的共享内存空间也就被删除了。
②如果删除共享内存时,共享内存的附加进程数量为0,则内核当中描述共享内存的结构体就被释放;如果删除共享内存时,共享内存的附加进程数量不为0,则操作系统回将共享内存的key编程0x00000000,表示当前共享内存不能被其他进程附加,且状态会被置为destory,一旦描述共享内存的就够提内部的引用计数变为0,该结构体就会被释放。