【进程间通信】匿名管道、命名管道和共享内存相关知识点

一、进程间通信

1.什么是进程间通信?

进程间通信是指支撑进程与进程之间交换数据。

2.为什么需要进行进程间通信?

每一个进程都拥有自己独立的进程虚拟地址空间,促使了进程的独立。但是,同样就带来了进程和进程之间相互协作的问题。目前来说,最大的进程间通信是网络。

3.进程间通信的目的

数据传输:一个进程需要将它的数据发送给另一个进程。
资源共享:多个进程之间共享同样的资源。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止 时要通知父进程)。
进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另 一个进程的所有陷入和异常,并能够及时知道它的状态改变。

二、匿名管道

1.什么是管道?

管道就是内核当中的一块内存,相当于内核为进程间通信开辟的数据缓冲区。

2.匿名管道的接口

int pipe(int fd[2])

fd[2]:是一个出参,在调用pipe函数之前定义fd[2],在pipe函数内部进行赋值,pipe函数执行结束后,通过fd[2]来操作管道。
其中fd[2]包括:
fd[0]:表示读端,通过fd[0]可以对管道进行读
fd[1]:表示写段,通过fd[1]可以对管道进行写
在这里插入图片描述
返回值:成功返回0,失败返回-1。

3.匿名管道的属性

a.匿名管道就是在内核当中创建的一块没有标识符的内存。
b.pipe函数会返回两个文件描述符,供程序员对该内存进行读写。
c.管道的数据流向,只能从写段流向读端,数据流向单一,称之为半双工。
d.匿名管道只适用于具有亲缘关系的进程。
在这里插入图片描述
上图在父进程当前调用pipe创建了匿名管道,父进程的文件描述符表当中会有fd[0]和fd[1],进而在创建子进程,子进程是拷贝父进程的PCB的,所以子进程的文件描述表当中也会有刚创建出来的fd[0]和fd[1]。此时两个进程都可以对匿名管道开辟的内存进行读写。

4.匿名管道的字节流服务

数据分成了一个一个字节进行传输,在传输过程当中,接受方可以选择任意字节数量进行接受。
在这里插入图片描述
读走相当于从管道当中将数据拿走,管道就没有这个数据了。
匿名管道的大小为64K,生命周期跟随进程。

5.非阻塞属性

首先将文件描述符设置为非阻塞属性

int fcntl(int fd,int cmd,...)
//cmd:
//F_GETFL:获取文件描述符的属性
//F_SETFL:设置文件描述符的属性

然后在读写的时候有什么特性?
5.1 写端文件描述符被设置成非阻塞
a.当读端不关闭的时候,写端一直写,直到写满管道,再调用写,write就是返回-1,同时报错,资源不可用。
b.当读端关闭了,写端去写,调用write函数的进程就会收到一个SIGPIPE信号,导致该进程退出。
5.2 读端文件描述符被设置成非阻塞
a.当写端不关闭的时候,读端一直读,直到将管道当中内容读完,read就会返回-1,同时报错,资源不可用。
b.当写端关闭的时候,读端一直读,直到将管道当中的内容读完,read就会返回0,表示读到0个字节。

三、命名管道

1.什么是命名管道?

命名管道在内核当中也是一块内存,或者说是一段缓冲区,只不过这一块内存是有标识符的。

2.创建命名管道

命名管道可以从命令行上创建,命令行方法是使用下面这个命令:

mkfifo filename

命名管道也可以从程序里创建,相关函数有:

int mkfifo(const char *filename,mode_t mode); 

其实是创建了一个命名管道文件(命名管道的标识符),不同的进程打开这样的文件,往文件中读写,就是对内核当中的内存进行读写,从而实现了不同的进程之间进行通信。

3.命名管道的特性

3.1 具有标识符
3.2 命名管道的生命周期跟随进程
3.3 其它特性和匿名管道一致

4.匿名和命名管道的区别

4.1 匿名管道由pipe函数创建并打开
4.2 命名管道由mkfifo函数创建,打开用open
4.3 FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义

四、共享内存

1.共享内存的原理

在这里插入图片描述
1.共享内存是在物理内存上开辟了一段空间
2.不同的进程可以通过页表映射将开辟的物理内存映射到自己的共享区
3.不同的进程在代码当中操作共享区中的虚拟地址,以达到操作物理内存,从而实现不同的进程进行进程间通信

2.共享内存的接口

2.1 获取共享内存

int shmget(key_t key,size_t size,int shmflg)

key:共享内存标识,只要和当前系统当中的内存标识不冲突,就可以了,传参的时候,可以给一个16进制的数字
size:共享内存的大小
shmflg:IPC_CREAT,当获取的共享内存不存在则创建;IPC_CREAT | IPC_EXCL,获取自己创建的共享内存,如果在获取共享内存的时候,发现该标识符的共享内存已存在,则报错
返回值:返回共享内存的操作句柄

2.2 附件到进程

void* shmat(int shmid,const void* shmaddr,int shmflg)

shmid:共享内存操作句柄
shmaddr:要附加到共享内存的地方,传值NULL,表示操作系统选择的是共享内存。
shmflg:0表示读写,IPC_RDONLY表示只读
返回值:返回共享内存当中附加的地址。
注意
1.在操作共享内存的时候,往共享内存当中写的时候,是覆盖写的方式
2.在操作共享内存的时候,从共享内存当中读的时候是访问而不是将数据读走。

共享内存的生命周期跟随内核
命令:ipcs
在这里插入图片描述
命令:ipcrm -m [共享内存操作句柄]
如果一个共享内存被删除:
1.共享内存的标识符会被设置成为0x00000000,表示当前共享内存不能被使用,而且状态也是被置为dest
2.如果删除了之后,本身还是有进程在附加,其实已经将共享内存块释放掉了,所以如果正在附加的进程去使用了已经被删除的共享内存,有可能导致崩溃
3.如果没有进程附加,使用ipcrm -m去删除的时候,就直接给删除了

2.3 分离

int shmdt(const void* shmaddr)

shmaddr:shmat的返回值,共享区的地址

2.4 操作共享内存的接口

int shmctl(int shmid,int cmd,struct shmid_ds* buf)

shmid:共享内存操作句柄
cmd:告诉shmctl函数做什么操作。IP_STAT:获取共享内存的属性,和第三个参数buf一起使用(出参);IP_SET:设置共享内存的属性,和第三个参数buf一起使用(入参);IPC_RMID:删除共享内存,第三个参数就可以传NULL
buf:操作系统描述共享内存的一个结构体

猜你喜欢

转载自blog.csdn.net/zhao_leilei/article/details/109187114