进程间通信IPC
- 管道:同一机器两个进程通信
- 套接字:不同机器两个进程通信
- System V IPC:同一机器上多个进程间通信。包括:消息队列(频繁传递的多个消息)、信号量(进程间同步)、共享内存(内容较多的通信)。每个IPC都有一个标识符(创建返回时,内核分配)和键值(程序员选择)。
共享内存
共享内存实际上是一段特殊的内存区域,这一段区域可以被多个进程映射到自身的地址空间中。多个进程就可以操作自身的这块区域来共同操作共享内存。每块共享内存在内核中实际上是以一个虚拟的文件存在的,这个文件代表一块内存区域。如果有进程连接共享内存,实际上是映射这一个虚拟的文件。
Linux创建进程时,有很大的虚拟地址空间,一小部分存放代码、数据、堆和栈,剩余的初始化为空闲。一块共享内存一旦被连接,即被映射到空闲虚拟地址空间。随后进程就可以像对待普通内存区那样读写共享内存。
共享内存的系统调用
- 头文件在linux/include/linux/下
- 源文件在linux/ipc/下
#include <sys/type.h>
#include <sys/ipc.h>
#include <sys/shm.h>
//创建一块共享内存,或返回一个已存在共享内存id
//返回的id是个数组下标,这个数组管理着所有共享内存
int shmget(key_t key, int size, int shmflg);
//将一块共享内存映射到一个进程的地址空间
void *shmat(int shmid, char *shmaddr, int shmflg);
//取消一个进程共享内存的映射
void *shmdt(int *shmaddr);
//对共享内存的各种操作,如设置所有者,读写权限,或者销毁其中一块共享内存
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
键值
键值的作用如同文件的文件名,每个IPC对象并不存在进程本身使用的内存中,创建IPC时根据键值返回其ID。shmget函数的第一个参数key可以是一块已经存在共享内存的键值、0、或IPC_PRIVATE。内存分配以页为单位,如果进程申请一块只有一个字节的内存,内存管理器也会分配一页4KB。
数据结构
- linux/include/linux/shm.h
- Linux内核中,每个共享内存用shmid_ds结构表示
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
__kernel_time_t shm_atime; /* last attach time */
__kernel_time_t shm_dtime; /* last detach time */
__kernel_time_t shm_ctime; /* last change time */
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of current attaches */
unsigned short shm_unused; /* compatibility */
void *shm_unused2; /* ditto - used by DIPC */
void *shm_unused3; /* unused */
};
- 每个IPC对象用一个ipc_perm结构存放权限信息
- linux/include/linux/ipc.h
struct ipc_perm
{
__kernel_key_t key;
__kernel_uid_t uid;
__kernel_gid_t gid;
__kernel_uid_t cuid;
__kernel_gid_t cgid;
__kernel_mode_t mode;
unsigned short seq;
};
和系统调用fork、exec、exit之间的关系
- fork之后,子进程继承父进程已连接的共享内存
- exec之后,所有已连接的共享内存被分离,但不被销毁
- exit,所有已连接的共享内存被分离,但不被销毁
- 目前没有方法来确保进程互斥访问共享内存,因此使用共享内存机制进行通信时的互斥问题,必须由用户自己解决。