消息队列
消息队列是一种进程间通信的机制,它提供了一个先进先出(FIFO)的消息存储缓冲区。
简单来说,消息队列就像是一个邮箱,进程可以将消息放入这个“邮箱”中,而其他进程可以从“邮箱”中取出消息。
消息队列中的消息通常具有一定的格式,包含消息类型、消息内容等信息。
1.特点
消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级
消息队列独立于发送和接收进程,进程终止时,消息队列及其内容仍存在
消息队列可以实现消息的随机查询,消息不一定要先进先出的次序读取,也可以按消息的类型读取。
相关函数
2.相关函数
1.int msgget(key_t key, int msgflg); //创建或打开消息队列,
参数:
key:和消息队列关联的key值
msgflg:是一个权限标志,表示消息队列的访问权限,它与文件的访问权限一样。msgflg可以与IPC_CREAT做或 操作,表示当key所命名的消息队列不存在时创建一个消息队列,如果key所命名的消息队列存在时,IPC_CREAT标志会被 忽略,而只返回一个标识符。
返回值:成功返回队列ID,失败则返回‐1,
这里我们出现创建一个队列,注意创建函数所用到的头文件,最后调用system()函数,是为了让其显示在我们终端上面
每运行一次就会生成一个消息队列
2.int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); //读取消息,成功返回消息数据的长度,失败返回‐1
参数:
msgid:消息队列的ID
msgp:指向消息的指针,常用结构体msgbuf如下:
struct msgbuf
{ long mtype; //消息类型
char mtext[N]; //消息正文
}
size:发送的消息正文你的字节数
flag:
IPC_NOWAIT 消息没有发送完成函数也会立即返回
0:知道发送完成函数才返回
返回值:
成功:0
失败:‐1
我们使用代码,举个例子,注意这里边消息类型(mtype)是一个消息的一个特征,在我们需要对这个消息进行操作的时候需要通过这个消息类型进程查找或其他操作
成功创建了一个消息,并且占据12个字节
3.ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg); //从一个消息队列中获取消息
参数:
msgid:消息队列的ID
msgp:要接收消息的缓冲区
size:要接收的消息的字节数
msgtype:
0:接收消息队列中第一个消息
大于0:接收消息队列中第一个类型为msgtyp的消息
小于0:接收消息队列中类型值不大于msgtyp的绝对值且类型值又最小的消息。
flag:
0:若无消息函数一直阻塞
IPC_NOWAIT:若没有消息,进程会立即返回ENOMSG。
返回值:
成功:接收到的消息i长度
出错:‐1
我们写个例子,要求读出我们写入到消息中的数据
成功读取
注意点:
我们写入的消息ID是8,但是会发现我们再次查看这个消息的时候,内容的字节数变成了0,这是因为,我们写入了12个字节,然后又读取了12个字节,里面的内容被完全取走了,所以在查看的时候变为0(只是说那个8的节点还存在)
4.int msgctl(int msqid, int cmd, struct msqid_ds *buf); //控制消息队列,成功返回0,失败返回‐1
参数:
msqid:消息队列的队列ID
cmd:
IPC_STAT:把msgid_ds结构中的数据设置为消息队列的当前关联值,即用消息队列的当前关联值覆盖msgid_ds的值。
IPC_SET:如果进程有足够的权限,就把消息列队的当前关联值设置为msgid_ds结构中给出的值 IPC_RMID:删除消息队列
buf:是指向 msgid_ds 结构的指针,它指向消息队列模式和访问权限的结构
返回值:
成功:0
失败:‐1
这里我们引用第一个创建函数的代码进行修改,实现删除消息队列的操作
成功删除编号为4的消息队列
在以下两种情况下,msgget将创建一个新的消息队列:
如果没有与键值key相对应的消息队列,并且flag中包含了IPC_CREAT标志
key参数为IPC_PRIVATE
函数msgrcv在读取消息队列时,type参数有下面几种情况
type ==0,返回队列中的第一消息
type >0,返回队列中消息队列类型为type的第一个消息
type<0 返回队列中消息类型值小于或等于type绝对值的消息,如果有多个,则取类型值最小的消息。
可以看出,type值非0时用于以非先进先出次序读取消息,也可以把type看成优先级的权值
消息赌对了
多个进程之间的消息通信
ftok函数
key_t ftok( char * fname, int id )
//系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id值通过ftok函数得到。
参数:
fname就时你指定的文件名(该文件必须是存在而且可以访问的)。
id是子序号, 虽然为int,但是只有8个比特被使用(0‐255)。
返回值:
当成功执行的时候,一个key_t值将会被返回,否则 ‐1 被返回。
半双工通信
即一读一写
读函数
写函数
成功执行
全双工通信
即父子进程通信,两个消息队列之间都可以对对方进行通信进行读和写的操作
代码如下:
上面代码相同
该部分是父进程(消息类型:100)写入,子进程(消息类型:200)读取
父进程(消息类型:100)读取,子进程(消息类型:200)写入
可以实现一边写,另外一边读,另外一边写,一边写》
共享内存
概念:
共享内存(Shared Memory)就是允许多个进程访问同一个内存空间,是在多个进程之间共享和传递数据最 高效的方式。操作系统将不同进程之间共享内存安排为同一段物理内存,进程可以将共享内存连接到它们自 己的地址空间中,如果某个进程修改了共享内存中的数据,其它的进程读到的数据也将会改变。 这种方式是较为高效的(不需要通过其他复杂的传递方式,比如管道或者消息队列)。
相关函数
1.int shmget(key_t key, size_t size, int shmflg); //用来获取或创建共享内存
参数:
key:IPC_PRIVATE(值为0) 或 ftok的返回值
size:共享内存区大小
shmflg:同open函数的权限位,也可以用8进制表示法
返回值:
成功:共享内存段标识符‐‐‐ID‐‐‐文件描述符
出错:‐1
成功创建,标识符为27
tip:删除一个共享内存可以用指令 ipcrm -m
2.void *shmat(int shm_id, const void *shm_addr, int shmflg); //把共享内存连接映射到当前进程的地址空间
参数:
shm_id:ID号
shm_addr:映射到的地址,NULL为系统自动完成的映射
shmflg:
SHM_RDONLY共享内存只读
默认是0,表示共享内存可读写
返回值:
成功:映射后的地址
失败:NULL
注:共享内存中不会因为读写把数据删除。(管道会)
3.int shmdt(const void *shmaddr); //将进程里的地址映射删除
共享内存的地址映射删除,通俗地来说,就是解除进程与共享内存之间的关联,使得进程无法再通过之前映射的地址来访问那块共享内存区域。
参数:
shmid:要操作的共享内存标识符
返回值:
成功:0
出错:‐1
案例同第4函数
4.int shmctl(int shm_id, int command, struct shmid_ds *buf); //删除共享内存对象
参数:
shmid:要操作的共享内存标识符
cmd :
IPC_STAT (获取对象属性)‐‐‐ 实现了命令ipcs ‐m
IPC_SET (设置对象属性)
IPC_RMID (删除对象) ‐‐‐实现了命令ipcrm ‐m
buf :指定IPC_STAT/IPC_SET时用以保存/设置属性
返回值:
成功:0
出错:‐1
实现ipcrm -m功能
生成共享内存函数
传参函数
执行
特点:
1.共享内存创建之后,一直存在于内核中,直到被删除或系统关闭
2.共享内存和管道不一样,读取后,内容仍然在共享内存中
进行通信
写
读