进程间通信
通信是需要成本的
本质:让不同进程看到同一个”资源“(特定形式的内存空间)
这份”资源“一般是由操作系统提供。
为什么不是其中一个进程所提供?
因为进程具有独立性,不能让其他的进程访问当前进程的内容
进程访问这个空间本质就是访问操作系统
管道
管道是UNIX进程间通信最古老的通信方式
基于文件级别的通信
把从一个进程连接到另一个进程的数据流叫做管道
匿名管道
pipe函数
创建一个无名管道
int pipe(int fd[2]);
fd:文件描述符 fd[0]表示读端,f[1]表示写端
返回值:成功返回0,错误返回-1
1、匿名管道是有固定的读端和写端
2、进程必须要有血缘关系(父子进程、兄弟进程等)
单向通信
所以fork()创建出子进程之后就需要,关闭父进程的写端和子进程的读端,使得父进程只读子进程只写
3、可以看作一个特殊的文件,可以使用read,write,存在内存中
为什么子进程要对struct_file内容做修改时,没有写时拷贝?
因为父子进程指向的是文件,不是代码段,文件是属于操作系统,并不属于进程
命名管道
使没有血缘关系的进程通信
int mkfifo(const char *pathname, mode_t mode);
pathname:指定管道在文件系统中路径
mode:设置文件管道权限
可以使不同进程之间双向通信
注意:要实现双向通信需要创建两个命名管道,因为一个管道中,一个进程只读打开文件时就会阻塞,直到其他进程写入。
管道通信实质:在内核中申请一个缓存区(内存),命名管道的文件只是一个标识符。所以并不是写入管道中数据越多,管道文件就会越大
多个进程通过管道通信时,删除管道文件之后,还能通信吗?
管道文件只是标识符,所以在删除之后管道缓冲区依然存在,可以通信
共享内存
原理:给两个或多个进程共享一个给定的存储区,让他们映射到相同的物理地址
共享内存是通信最快的访问方式,直接将物理地址映射到了虚拟地址,所以可以直接访问虚拟地址就可以实现通信,相比其他方式少了内核态到用户态之间的转换
删除共享内存时,本质就是无法映射到地址中,只要进程链数不为0,进程就可以访问,直到为0时无法实现通信
但是当所有进程与共享内存断开映射之后,共享内存不会直接释放,其生命周期是随内核的
调用shmdt函数释放共享内存
shmget函数
ftok函数
pathname
:是一个指向路径名的字符串,用于生成唯一的键值。proj_id
:是一个整数值,通常是一个字符,用于增加键值的独特性。
shmat函数
shmdt函数
shmaddr
:指定要分离的共享内存段连接到进程地址空间的起始地址
shmctl函数
用于对共享内存段进行控制操作
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid
:是共享内存段的标识符,由shmget
函数返回。cmd
:是要执行的操作命令,可以是下列值之一:IPC_STAT
:获取共享内存的状态信息,并将其存储在buf
参数指向的结构体中。IPC_SET
:设置共享内存的状态信息,buf
参数指向的结构体中包含了要设置的新值。IPC_RMID
:删除共享内存段。
buf
:是一个指向shmid_ds
结构体的指针,用于存储获取或设置的共享内存的状态信息。
进程互斥
信号量本质类似于(≠)计数器,进行PV操作,原子的
申请信号量--P操作
释放资源,删除信号量--V操作
要么执行,要么不执行,没有“正在做”--原子的
信号量是1,0两态的,互斥功能
申请信号量本质:对临时资源的预定机制!
注:信号量不是信号