共享内存
共享内存是进程间通信中最简单的方式之一。共享内存允许两个或更多进程访问同一块内存,当一个进程改
变了这块地址中的内容的时候,其它进程都会察觉到这个更改。
共享内存头文件
1 | #include <sys/ipc.h> |
共享内存操作函数
创建或打开一块共享内存区
1 | int shmget(key_t key, size_t size, int shmflg); |
将当前进程和共享内存关联到一起
1 | void *shmat(int shmid, const void *shmaddr, int shmflg); |
将共享内存和当前进程分离
1 | int shmdt(const void *shmaddr); |
共享内存操作 -(删除共享内存 )
1 | int shmctl(int shmid, int cmd, struct shmid_ds *buf); |
- 问题1: 是不是可以对共享内存进行多次删除
- 可以
- 共享内存被删除一次之后, 如果还有进程和共享内存关联着, 共享内存的key会发生变化变成0
- 如果共享内存key是>0的数: 共享内存状态正常, 任意进程都可以和当前共享内存进行关联
- key == 0:
- 共享内存标记为被删除, 没有被马上删除是因为还有进程没有和它解除关联
- 不相干的进程是没有权限和key为0的共享内存进行关联的

- 问题2: 如果多个进程都和同一共享内存进行关联, 其中一个进程将共享内存删除, 共享内存什么时候被删除?
- 共享内存的引用计数为0 的时候, 共享内存被删除
shm和mmap的区别
- shm不需要磁盘文件, mmap需要磁盘文件
- shm效率高
- mmap操作的数据量比shm大 4. shm内存位置在内核只有一块, mmap内存在用户区, 每个进程都有各自的内存映射区
- shm和mmap的数据谁更安全?
- mmap会通过映射的文件做备份
- 进程退出, 共享内存依然存在, 进程退出,内存映射区就不存在了
ftok函数
函数原型
1 | key_t ftok(const char *pathname, int proj_id); |
思考:
pathname 是目录还是文件的具体路径,是否可以随便设置?
可以
pathname 指定的目录或文件的权限是否有要求?
没有
proj_id 是否可以随便设定,有什么限制条件?
取值范围 0-255
陷阱:
误解:
只要文件的路径,名称和子序列号不变,那么得到的key值永远就不会变。
正解:
如果pathname指向的文件或者目录被删除而且又重新创建,那么文件系统会赋予这个同名文件新
的inode 节点信息,于是这些进程调用的 ftok() 都能正常返回,但键值key却不一定相同了。
共享内存操作命令
ipcs 用法
1 | ipcs -a // 打印当前系统中所有的进程间通信方式的信息 |
man msgget //消息队列相关函数
man semget //信号量数组相关函数
ipcrm 用法
1 | ipcrm -M shmkey // 移除用shmkey创建的共享内存段 |
共享内存在项目中的使用

与业务相关的共享内存结构图:
1 |
|
共享内存结构图: 头4个字节存放最大节点个数, 后面一共存放maxNode个秘钥信息

在遍历共享内存的时候, 首先获得共享内存的头4个字节内容, 也就是最大节点个数, 可以作为遍历的最大次数.
遍历的时候查找共享内存根据clientID和serverID来进行查找.
myipc_shm.h
1 |
|
myipc_shm.c
1 |
|
keymng_shmop.h
1 | // keymng_shmop.h |
keymng_shmop.c
1 | #include <unistd.h> |
进程间通讯的几种方式:
1 匿名管道pipe:
特点:
1 只能用于有血缘关系的进程间通信
2 管道有两端, 管道的数据流向是从管道的写端到管道的读端
3 管道的本质是一块内核缓冲区
4 数据从管道中读走之后就不存在了
5 管道的实现实际上是环形队列
6 默认情况下管道的读端和写端都是阻塞的
2 命名管道: fifo
特点:
1 有无血缘关系的进程间通信都可以
2 创建的fifo文件大小为0, 是linux文件类型之一
3 使用fifo需要先创建一个fifo文件
4 使用fifo完成通信两个进程必须打开相同的fifo文件
5 效率比pipe低
3 mmap
共享映射区本质是将文件内容映射到内存.
特点:
1 有无血缘关系都可以完成进程间通信
2 如果完成没有血缘关系的进程间通信必须使用文件.
3 若使用的是MAP_SHARED, 则对内存的修改会反映到文件中去
4 需要注意mmap可能存在调用失败的情况
5 匿名映射只能用于有血缘关系的进程间通信
4 信号
进程A给进程B发送信号的实现机制: 本质上是进程A先给内核发送信号, 然后内核给进程B发送
通signal或者sigaction注册信号
通过kill函数给指定进程发送信号
特点:
1 信号不能携带大量信息
2 信号的优先级高, 产生信号之后会打断程序的执行
3 不建议使用信号完成进程间通信.
4 一般使用kill命令给一个进程发送信号, 进程收到信号之后调用信号处理函数完成操作.
信号的处理动作:
1 忽略信号
2 执行默认处理动作
3 执行用户自定义的函数
5 本地socket通信
1 | unix_socket = socket(AF_UNIX, SOCK_STREAM, 0); |
1 本地socket通信既可以使用TCP也可以使用UDP
2 如果使用TCP通信, bind的时候需要指定一个文件, 若文件存在会保存, unlink删除.
3 读或者写其实是通过文件描述符去操作内核的缓冲区
4 编写流程, 可以直接参考TCP或者UDP开发流程.
6 共享内存
共享内存的实质是将内核的一块内存映射到进程中的内存, 操作本地内存就相当于操作共享内存.
使用共享内存的步骤:
1 创建共享内存
2 关联共享内存
3 使用共享内存–读写共享内存
4 断开与共享内存的关联
5 删除共享内存
共享内存和以上的管道, mmap和本地socket通信比较起来, 共享内存不需要文件描述符,
后者需要.
共享内存是进程间通信方式中效率最高的.