文章目录
- 共享内存
- 信号
- 管道
- 消息队列
通信方法 | 无法介于内核态与用户态的原因 |
管道(不包括命名管道) | 局限于父子进程间的通信。 |
消息队列 | 在硬、软中断中无法无阻塞地接收数据。 |
信号量 | 无法介于内核态和用户态使用。 |
共享内存 | 需要信号量辅助,而信号量又无法使用。 |
共享内存
共享内存是最快的进程间通讯的方式
-
原因:相对于其他几种方式,共享内存直接在进程的虚拟地址空间进行操作,不再通过执行进入内核的系统调用来传递彼此的数据
-
shmget函数
功能 | 用来创建共享内存 |
---|---|
原型 | int shmget(key_t key, size_t size, int shmflg); |
参数key: | 这个共享内存段名字 |
size: | 共享内存大小 |
shmflg: | 由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的 |
返回值: | 成功返回⼀一个⾮非负整数,即该共享内存段的标识码;失败返回-1 |
- shmat函数
功能: | 将共享内存段连接到进程地址空间 |
---|---|
原型 | void *shmat(int shmid, const void *shmaddr, int shmflg); |
参数shmid: | 共享内存标识 |
shmaddr: | 指定连接的地址 |
shmflg: | 它的两个可能取值是SHM_RND和SHM_RDONLY |
返回值: | 成功返回⼀一个指针,指向共享内存第⼀一个节;失败返回-1 |
- shmctl函数
功能: | ⽤用于控制共享内存 |
---|---|
原型 | int shmctl(int shmid, int cmd, struct shmid_ds *buf); |
参数shmid: | 由shmget返回的共享内存标识码 |
cmd: | 将要采取的动作(有三个可取值) |
buf: | 指向⼀一个保存着共享内存的模式状态和访问权限的数据结构 |
返回值: | 成功返回0;失败返回-1 |
- shmdt函数
功能: | 将共享内存段与当前进程脱离 |
---|---|
原型 | int shmdt(const void *shmaddr); |
参数shmaddr: | 由shmat所返回的指针 |
返回值: | 成功返回0;失败返回-1 |
注意: | 将共享内存段与当前进程脱离不等于删除共享内存段 |
#include<stdio.h>
#include<stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<string.h>
#include<errno.h>typedef struct _Teacher
{char name[64];int age;
}Teacher;int main(int argc, char *argv[])
{int ret = 0;int shmid;//创建共享内存 ,相当于打开文件,文件不存在则创建shmid = shmget(0x2234, sizeof(Teacher), IPC_CREAT | 0666); if (shmid == -1){perror("shmget err");return errno;}printf("shmid:%d \n", shmid);Teacher *p = NULL;//将共享内存段连接到进程地址空间p = shmat(shmid, NULL, 0);//第二个参数shmaddr为NULL,核心自动选择一个地址if (p == (void *)-1 ){perror("shmget err");return errno;}strcpy(p->name, "aaaa");p->age = 33;//将共享内存段与当前进程脱离shmdt(p);printf("键入1 删除共享内存,其他不删除\n");int num;scanf("%d", &num);if (num == 1){//用于控制共享内存ret = shmctl(shmid, IPC_RMID, NULL);//IPC_RMID为删除内存段if (ret < 0){perror("rmerrr\n");}} return 0;
}
#include<stdio.h>
#include<stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<string.h>
#include<errno.h>typedef struct _Teacher
{char name[64];int age;
}Teacher;int main(int argc, char *argv[])
{int ret = 0;int shmid;//shmid = shmget(0x2234, sizeof(Teacher), IPC_CREAT |IPC_EXCL | 0666); //打开获取共享内存shmid = shmget(0x2234, 0, 0); if (shmid == -1){perror("shmget err");return errno;}printf("shmid:%d \n", shmid);Teacher *p = NULL;//将共享内存段连接到进程地址空间p = shmat(shmid, NULL, 0);if (p == (void *)-1 ){perror("shmget err");return errno;}printf("name:%s\n", p->name);printf("age:%d \n", p->age);//将共享内存段与当前进程脱离shmdt(p);printf("键入1 程序暂停,其他退出\n");int num;scanf("%d", &num);if (num == 1){pause();} return 0;
}
选取简单的代码,可运行观察效果
信号
信号量和以前的IPC通信方式不同,信号量的本质是计数器,用于多进程对共享数据对象的访问。
在进程访问临界资源之前,需要测试信号量,如果为正数,则信号量-1并且进程可以进入临界区,若为非正数,则进程挂起放入等待队列,直至有进程退出临界区,释放资源并+1信号量,此时唤醒等待队列的进程。
信号量本身就是临界资源,所以必须是原子操作。
- 生产者消费者模型:
https://blog.csdn.net/csdn_kou/article/details/81240666
管道
单向,一端输入,另一端输出,先进先出FIFO。管道也是文件。管道大小4096字节。
- 特点:管道满时,写阻塞;空时,读阻塞。
- 分类:普通管道(仅父子进程间通信)位于内存;命名管道位于文件系统,没有亲缘关系管道只要知道管道名也可以通讯。
- 管道是由内核管理的一个缓冲区(buffer),相当于我们放入内存中的一个纸条。管道的一端连接一个进程的输出。这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管道可以被循环利用。当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失
- 匿名管道与命名管道的区别
- 匿名管道由pipe函数创建并打开。
- 命名管道由mkfifo函数创建,打开用open
- FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义。
消息队列
消息队列是先进先出FIFO原则
ipcs & ipcrm命令
- ipcs: 显示IPC资源
- ipcrm: 手动删除IPC资源