进程间通信的分类:
文件
文件锁
管道和匿名管道FIFO
信号
消息队列
共享内存
信号量
互斥量
条件变量
读写锁
套接字
两个进程同时向管道中写数据,内核要保证它们是原子的,内核按照页做限制。
进程间共享信息的三种方式:一个通过文件系统,一个通过内核,一个在应用空间
IPC对象的持续性:
随进程持续:一直存在,直到打开的最后一个进程结束。(如pipe和FIFO)
随内核持续:一直存在,直到内核自举或者显式删除。(如System V消息队列、共享内存、信号量)
随文件系统持续:一直存在,直到显式删除,即使内核自举还存在。(POSIX消息队列、共享内存、信号量(如果是使用映射文件来实现))
查看系统的IPC对象以及它们的一些属性可以使用ipcs命令,如下:
其中key表示键,semid表示这个对象的id,owner表示拥有者。共享内存还有个属性是nattch,表示有多少个人连接上共享内存了。
ipcs -l可以查看IPC对象的一些系统限制:
消息队列提供了从一个进程向另一个进程发送一块数据的方法
每个数据块都有一个类型,接收者进程接收的数据块可以有不同的类型值
消息队列也有管道一样的不足,就是每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)。
管道是基于数据流的,先进先出。
消息队列是有边界的,可以后进先出。
消息队列有如下三个限制:
cat /proc/sys/kernel/msgmax 最大消息长度限制
cat /proc/sys/kernel/msgmnb 消息队列总的字节数
cat /proc/sys/kernel/msgmni 消息条目数
使用man 2 msgctl可以查看消息队列的一些数据结构:
struct msqid_ds {
struct ipc_perm msg_perm; /* Ownership and permissions */
time_t msg_stime; /* Time of last msgsnd(2) */
time_t msg_rtime; /* Time of last msgrcv(2) */
time_t msg_ctime; /* Time of last change */
unsigned long __msg_cbytes; /* Current number of bytes in
queue (nonstandard) */
msgqnum_t msg_qnum; /* Current number of messages
in queue */
msglen_t msg_qbytes; /* Maximum number of bytes
allowed in queue */
pid_t msg_lspid; /* PID of last msgsnd(2) */
pid_t msg_lrpid; /* PID of last msgrcv(2) */
};
struct ipc_perm {
key_t __key; /* Key supplied to msgget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions */
unsigned short __seq; /* Sequence number */
};
消息队列在内核中的表示如下:
msq_ids存储了消息的一些元信息,具体的消息由内核来管理。
消息队列相关的一些API函数:
msgget函数原型如下:
int msgget(key_t key, int msgflg)
功能:用来创建和访问一个消息队列
参数:
key表示某个消息队列的名字
msgflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该消息队列的标识码;失败返回-1
编写如下的msgget测试函数:
1 #include <sys/types.h> 2 #include <unistd.h> 3 #include <stdio.h> 4 #include <string.h> 5 #include <stdlib.h> 6 #include <errno.h> 7 #include <sys/msg.h> 8 9 int main() 10 { 11 int msgid; 12 13 msgid = msgget(0x1234, 0666); 14 if(msgid < 0) 15 { 16 perror("msgget error"); 17 exit(0); 18 } 19 20 return 0; 21 }
这段程序代表打开一个消息队列,执行结果如下:
系统中没有key值为0x1234的消息队列,所以出错返回。我们可以根据errno错误码进行更精确的控制。
我们修改msgget函数的第二个参数,程序如下:
#include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <sys/msg.h> int main() { int msgid; msgid = msgget(0x1234, 0666 | IPC_CREAT); if(msgid < 0) { perror("msgget error"); exit(0); } printf("create msg queue success\n"); return 0; }
现在的参数表示,如果这个消息队列存在就打开旧的,如果不存在就创建新的。
现在我们的系统中是没有消息队列的,执行结果如下:
使用ipcs查看系统中的消息队列如下:
可以看到确实出现了一个key值为0x1234的消息队列IPC对象。