进程间通信方式
- 无名管道(
pipe
):用于具有父子关系的进程之间的通信 - 有名管道(
fifo
):可用于非父子关系之间的进程 - 信号量(
semaphore
):是一个计数器,可用于进程或线程的同步或互斥,常用作锁机制 - 消息队列(
message
):是消息的链表,允许进程将格式化的数据流以消息队列形式发送给任意进程 - 共享内存(
shared memory
):映射一段能够被其他进程所访问的内存,由一个进程创建,多个进程访问,是最快的IPC
方式,不需要进行频繁的用户态内核态切换。但需要与其他机制,如信号量配合来实现进程间的同步
区别
管道,消息队列需要在内核和用户空间进行四次内存拷贝,共享内存只需要拷贝两次。一次是输入文件到共享内存区,另一次是从共此昂内存区到输出文件。
共享内存实现机制
POSIX
共享内存XSI
共享内存mmap
共享内存
不同进程共享内存:同一块物理内存被映射到多个进程的进程地址空间。当前进程可以看到其他进程对于 shared memory
数据的更新。但由于不同进程访问同一块空间,需要进行同步或互斥,一般使用互斥锁或信号量。
特点
- 效率高,进程直接读写内存,不需要进行频繁的用户状态切换,就保证了不需要频繁地进行数据拷贝。
- 不同进程之间共享和传递数据的最有效方式
- 共享的内存安排为同一段物理内存
XSI实现机制
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
/*
@brief:1、创建共享内存
@param:
key:类似于文件名,是系统全局唯一的,方便其他进程使用同样的key打开同样一段共享内存,通过使用ftok()函数使用pathname和PROJ_ID产生。即约定一个文件名和一个项目的PROJ_ID来在同一个系统中确定一段共享内存的key
ftok()并不会创建文件,必须指定一个存在的文件名,它实际上使用的是指定文件的inode编号和文件所在设备的设备编号
也可以直接写IPC_PRIVATE,内核会在不产生冲突的共享内存段id的前提下新建一段共享内存,此时一定是新创建,shmflg=IPC_CREAT
size:共享内存大小
shmflg:创建标志
IPC_CREAT:
IPC_EXCL:
0:访问一个已存在的共享内存
SHM_HUGETLB:申请大页内存,提高内存对内存管理的处理效率
SHM_HUGE_2MB
@return:
shmid:类似于文件描述符,fork产生的子进程,可以直接使用shmid来使用相关共享内存
*/
int shmget(key_t key, size_t size, int shmflg);
/*
@brief:将共享内存映射到当前进程的内存空间,映射之后,通过访问进程的虚拟地址就可以访问到共享内存段了
@param:
shmid:上述创建的返回id值
shmaddr:指定共享内存映射到当前进程中的地址,此时shmflg=SHM_RND,大多数情况下应该置NULL,此时系统会自己选择地址
shmflg:
@return:
调用成功则返回映射地址,失败为-1
*/
void *shmat(int shmid, const void* shmaddr, int shmflg);
/*
@brief:解除映射,只能解除,不能删除
@param:
shmaddr:要解除的地址空间
*/
int shmdt(const void *shmaddr);
/*
@brief:控制共享内存
@param:
shmid:
cmd:对共享内存进行的操作
IPC_STAT:得到共享内存的状态
IPC_SET:设置共享内存的状态
IPC_RMID:删除共享内存
buf:
struct shmid_ds {
struct ipc_perm shm_perm; // 所有者与权限
size_t shm_segsz; // 共享内存段的大小
time_t shm_atime; // last attach time
time_t shm_dtime; // last detach time
time_t shm_ctime; // last change time
pid_t shm_cpid; // pid of creator
pid_t shm_lpid; // pid of last shmat/shmdt
shmatt_t shm_nattch; // number of current attaches
}
struct ipc_perm {
key_t __key; // shmget中的key
uid_t uid; // 所有者的用户id
gid_t gid; // 所有者的组id
uid_t cuid; // 创建者的用户id
gid_t cgid; // 创建者的组id
unsigned short mode; //
}
*/
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
mmap实现机制
mmap
是能够实现内存的映射,它能够将一个文件映射到内存中,在程序中就可以直接使用虚拟内存地址对文件内容进行访问,让程序对文件访问更方便。
mmap
系统调用可以用在很多场合,并不仅局限于实现共享内存。
实现在父子进程之间共享内存。
#include <sys/mman.h>
/*
@brief:映射虚拟地址为虚拟内存
@param:
flags:
MAP_SHARED:申请一段共享内存,可以映射到具体文件,也可以不,fd=-1
*/
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
// 解除映射
int munmap(void *addr, size_t length);
POSIX实现机制
本质上也是mmap
对文件的映射,映射的是tmpfs
文件系统上的文件。
其可以将共享内存当做一个文件,这样符合Linux
一切皆文件的宗旨,这样可以使用I/O
复用等相关使用文件描述符的功能。当OXI
共享内存其得到的共享内存是类文件,并不是真正的文件,所得到的shmid
也只是类文件描述符,不能向平常的文件描述符那样使用。
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
/*
@brief:创建或访问已创建的共享内存,实际上为open的封装,实际上POSIX共享的是/dev/shm目录中的一个tmpfs格式的文件
*/
shm_open(const char *name, int oflag, mode_t mode);
/*
@brief:解除映射,实际上为unlink的封装
*/
shm_unlink(const char* name);