1、消息队列
消息队列是一种先进先出的队列型数据结构,实际上是系统内核中的一个内部链表。消息被顺序插入队列中,其中发送进程将消息添加到队列末尾,接受进程从队列头读取消息。多个进程可同时向一个消息队列发送消息,也可以同时从一个消息队列中接收消息。发送进程把消息发送到队列尾部,接受进程从消息队列头部读取消息,消息一旦被读出就从队列中删除。
函数原型:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
函数功能:向消息队列中发送消息
函数参数:
msqid : 消息队列号
msgp : 消息的结构体地址
struct msgbuf {
long mtype; /* 消息类型long 必须>0 */
char mtext[1]; /* 消息的正文(数值、字符、字符串、结构体等 */
};
msgsz : 消息正文的大小 (不包含类型)
msgflg : 发送消息的方式,消息队列为满,默认写阻塞
0 阻塞发送
IPC_NOWAIT 非阻塞发送
返回值:成功返回 0
函数原型:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
函数功能:向消息队列中发送消息
函数参数:
msqid : 消息队列号
msgp: 接收消息后存放消息的结构体地址
msgsz : 接收消息正文的大小 (不包含类型)
msgtyp: 接收消息的类型
0 : 直接读取第一条消息
>0: 读取类型为mtype的消息
<0:读取第一条小于等于mtype绝对值的消息
-100 -> 100 -> 99 -> 98
msgflg: 读取消息的方式,消息队列为空,默认读阻塞
0 阻塞
IPC_NOWAIT 非阻塞
返回值:成功返回接收的字节数,失败 -1
函数原型:
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
函数功能:控制消息队列(获取属性、设置属性、删除等)
函数参数:
msqid : 消息队列号
cmd : 命令
IPC_STAT : 获取属性 将属性放入buf中
IPC_SET : 设置属性 讲buf中的值存入刀消息队列的属性中
IPC_RMID : 删除消息队列
buf : 属性结构体地址,第二参数为IPC_RMID,该参数没有意义NULL即可
举例:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
struct msgbuf{
long mtype;
char mtext[64];
};
int main(){
int msgid = 0; //消息队列号
key_t key = ftok("/home/linux", 'a');
//创建
if(-1 == (msgid = msgget(key, IPC_CREAT | 0666))){
perror("msgget");return -1;
}
struct msgbuf msg;
//从消息队列中读
while(1){
memset(msg.mtext, 0x00, sizeof(msg)-sizeof(long));
printf("请输入要读取消息的类型 > ");
scanf("%ld", &msg.mtype);
//读取
msgrcv(msgid, &msg, sizeof(msg.mtext), msg.mtype, 0);
printf("recv data : %s\n", msg.mtext);
if(strncmp("exit", msg.mtext, 4) == 0)
break;
}
//删除
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
2、共享内存
是所有进程间最高效的通信方式,因为相当于共享内存分配出的物理地址直接映射到两个进程,绑定,以后在进程中对共享内存操作就相当于对分配出的物理地址操作,不需要用户空间和内核空间数据的拷贝过程,因此是最高效的。
- 为了防止内存碎片,创建共享内存的大小为一页4096字节的整数倍【4K的整数倍】
函数原型:
int shmget(key_t key, size_t size, int shmflg);
参数:
key : 键值
size: 创建共享内存的大小,4096整数倍
shmflg: IPC_CREAT: 不存在时创建,存在也创建 ,直接返回ipc对象标识符
IPC_EXCL : 不存在,创建 存在返回错误 EEXIST
功能:成功返回共享内存号,失败 返回-1
映射函数原型:
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射共享内存段首地址到当前进程(拿到共享内存段的首地址)
参数:
shmid: shmget的返回值是 共享内存号
shmaddr: 将共享内存段关联到当前调用进程的地址处,一般为NULL,交由系统分配
shmflg : 对共享内存段的操作权限
SHM_RDONLY: 只读,并且当前进程对ipc对象具有读权限
0 : 读写权限,并且当前进程对ipc对象具有读写权限
取消映射函数原型:
int shmdt(const void *shmaddr);//功能:取消映射
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
//功能:对共享内存段控制 一般作为删除作用
实例:
write.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
int main(){
key_t key;
int shmid;
void *addr = NULL;
char buf[32] = {0};
if(-1 == (key = ftok(".", 'a'))){
perror("ftok");return -1;
}
//创建共享内存
if(-1 == (shmid = shmget(key, 4096, IPC_CREAT | 0666))){
perror("shmget");return -1;
}
//映射
if((void *)-1 == (addr = shmat(shmid, NULL, 0))){
perror("shmat");return -1;
}
//对共享内存段操作
while(1){
memset(buf, 0x00, sizeof(buf));
printf("input > ");
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf)-1] = '\0';
sprintf(addr, "%s", buf);
}
//取消映射
shmdt(addr);
return 0;
}
read.c
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
int main(){
key_t key;
int shmid;
void *addr = NULL;
char buf[32] = {0};
if(-1 == (key = ftok(".", 'a'))){
perror("ftok");return -1;
}
//获取共享内存号
if(-1 == (shmid = shmget(key, 4096, IPC_CREAT | 0666))){
perror("shmget");return -1;
}
//映射
if((void *)-1 == (addr = shmat(shmid, NULL, 0))){
perror("shmat");return -1;
}
//对共享内存段操作--读
while(1){
printf("data=%s\n", (char *)addr); //没有读阻塞,直接返回
}
shmdt(addr);
return 0;
}
3、信号量
信号量本身是一个计数器,代表了有无资源和有多少资源可用
P:当一个进程想要访问共享资源的时候,会进行P操作,在进行P操作之前先查看信号量的值是否大于0,如果等于0,则会阻塞,如果大于0,会执行访问临界资源的代码区域,并且将信号量的值-1
V:给一个信号量释放资源,信号量的值+1,如果一个进程因为信号量原本的值为0而阻塞,当进行V操作之后值+1,此时进程会接触阻塞申请资源执行代码区域
int semget(key_t key, int nsems, int semflg);
功能:创建信号量
参数:
key:键值
nsems: 创建的信号量的个数
semflg: 标志位
返回值:成功返回信号量集的标识符,失败 -1
int semctl(int semid, int semnum, int cmd, ...);
功能:控制信号量
参数:
semid:信号量集的标识符
semnum: 要控制的信号量的编号(一个信号量集中编号从0开始)
cmd:命令
IPC_STAT IPC_SET 对属性操作
IPC_INFO 获取信息
IPC_RMID 删除信号量集
GETALL 返回array中所有信号量的semval(即当前值)。参数semnum被忽略。调用进程必须对信号量集具有读权限。
GETVAL 返回编号为Semnum的信号量的当前值(以返回值形式得到)
SETALL 设置所有信号量集中的信号量的值,调用进程必须对信号量集具有写权限
SETVAL 设置编号为Semnum的信号量的值为第四个参数或者第四个参数联合体中的第一个成员
...: 可变参数 具体取决于第三个参数 也可单独使用
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO */
};
返回值:失败-1 成功取决于第三个参数 GETVAL the value of semval.
int semop(int semid, struct sembuf *sops, size_t nsops);
功能:对信号量操作
参数:
semid: 信号量集编号
sops: 结构体指针 unsigned short sem_num;
short sem_op 信号量的操作 P申请资源 -1 / V释放资源 +1
IPC_NOWAIT 非阻塞 0阻塞
SEM_UNDO 进程终止时自动撤销
nsops: 操作的信号量的个数
返回值:成功返回 0 失败 -1