整理了一下IPC通信的函数,包括消息队列,共享内存,信号量集;信号量集的使用是在共享内存的基础上使用,函数太多啦,慢慢学吧cc,争取全部记住
其中在使用有关信号量集的函数的时候,进行简单的封装函数功能之后,再进行使用,会更加方便,在文章最后对信号量集的两个进程通信进行代码展示。
目录
***********有关消息队列
1、ftok:创建用于生成消息队列的钥匙
2、msgget:通过钥匙创建出一个消息队列对象
3、msgsnd:向消息队列中存放消息
4、msgrcv:从消息队列中取消息
5、msgctl:控制消息队列
***********有关共享内存
1、shmget:创建共享内存对象
2、shmat:将共享内存地址映射到用户空间
3、shmdt:取消共享内存的映射
4、shmctl:共享内存的控制函数
***********有关信号量集
1、semget:创建一个信号量集
2、semctl:信号量集的控制函数
3、semop:对信号灯进行PV操作(申请资源和释放资源)
**信号量集函数展示
函数原型:
key_t ftok(const char *pathname, int proj_id);
例子:
//1、创建key值,用于生产消息队列
key_t key = ftok("/", 'k');
参数1:文件路径,该文件的inode号占key值的2字节,该文件的设备号占key值的1字节
参数2:一个给定的随机值,该值占key值的1字节
功能:通过给定的文件路径和一个随机ID值创建出一个用于IPC通信的key值
返回值:成功返回创建出的key值,失败返回-1并置位错误码
函数原型:
int msgget(key_t key, int msgflg);
/*
参数1:用于创建消息队列的key值,该值可以由ftok创建出来,也可以是 IPC_PRIVATE,表示进行亲缘进程间的通信
参数2:创建标识位
IPC_CREAT:表示本次操作要创建一个消息队列,如果该key值对应的消息队列已经存在,则直接打开该消息对象
IPC_EXCL:表示本次确保要创建一个新的消息队列,如果该消息队列已经存在,则该函数报错,错误码为EEXIST 创建文件的权限,也在该参数中,使用位或连接
*/
例子:
功能:通过给定的key值创建一个消息队列
返回值:成功返回消息队列的id号,失败返回-1并置位错误码
函数原型:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
例子:
//3、将消息发送到消息队列中
msgsnd(msqid, &buf, SIZE, 0);
/*
参数1:消息队列的id号
参数2:是指向封装好的消息的起始地址,通常类型如下,但是需要用户自己定义
struct msgbuf {
long mtype;
char mtext[1];
};
参数3:表示参数2中,消息正文的大小,不包含消息类型的大小
参数4:发送标识位,表示是否阻塞
0:表示阻塞
IPC_NOWAIT:表示非阻塞
*/
功能:向消息队列中存放消息,要求当前进程对消息队列具有可写权限
返回值:成功返回0,失败返回-1并置位错误码
函数原型:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
例子:
msgrcv(msqid, &buf, SIZE, 0, 0);
//参数1:消息队列id号
//参数2:数据容器起始地址
//参数3:数据的正文大小
//参数4:消息类型,0表示任意类型
//参数5:表示阻塞读取消息
功能:从消息队列中取消息
返回值:成功返回成功读取的字节个数,失败返回-1并置位错误码
函数原型:
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
例子:
//删除消息队列
if(msgctl(msqid, IPC_RMID, NULL) ==-1)
{
perror("msgctl error");
return -1;
}
// 参数1:消息队列的id号
// 参数2:操作指令
// IPC_STAT:获取消息队列的属性,此时参数3必须要给定,表示接收消息队列的属性
// struct msqid_ds {
// struct ipc_perm msg_perm; /* 拥有者和权限 */
// time_t msg_stime; /* 最新一次向消息队列中发送数据的时间 */
// time_t msg_rtime; /* 最新一次消息队列接受数据的时间 */
// time_t msg_ctime; /* 最新一次操作消息队列的时间 */
// unsigned long __msg_cbytes; /* 当前队列中已用的字节数 */
// msgqnum_t msg_qnum; /* 当前队列中消息的个数*/
// msglen_t msg_qbytes; /* 队列的最大容量,默认是16K */
// pid_t msg_lspid; /* 最后一次向消息队列中发送消息的进程id号 */
// pid_t msg_lrpid; /* 最后一次从消息队列中取消息的进程id号 */
// };
// 对第一个成员的介绍
// struct ipc_perm {
// key_t __key; /* 键值 */
// uid_t uid; /* 当前拥有者的用户id号 */
// gid_t gid; /*当前拥有者的组id号 */
// uid_t cuid; /* 创建消息队列的进程的用户id */
// gid_t cgid; /* 创建消息队列进程的组id号 */
// unsigned short mode; /* 操作权限 */
// unsigned short __seq; /* 队列号 */
// };
// IPC_SET:设置消息队列的属性
// IPC_RMID:删除消息队列,当参数2位该值时,参数3可以忽略,直接填NULL即可
功能:完成对消息队列指定的cmd操作
返回值:成功返回0,失败返回-1并置位错误码
函数原型:
int shmget(key_t key, size_t size, int shmflg);
例子:
//1、创建key值用于创建共享内存段
key_t key = ftok("/", 't');
//2、创建一个共享内存的对象
int shmid = shmget(key, PAGE_SIZE, IPC_CREAT|0664);
/*
参数1:key值,可以是IPC_PRIVATE也可以是由ftok创建出来的
参数2:申请的共享内存段的大小,必须是PAGE_SIZE的整数倍,如果超过,则向上取整
参数3:创建的标识
IPC_CREAT:表示本次操作要创建一个共享内存,如果该key值对应的共享内存已经存在,则直接打开该共享内存对象
IPC_EXCL:表示本次确保要创建一个新的共享内存,如果该共享内存已经存在,则该函数报错,错误码为EEXIST
创建文件的权限,也在该参数中,使用位或连接
*/
功能:通过给定的key值创建一个共享内存的对象,并返回该对象的id
返回值:成功返回创建出来的共享内存的id,失败返回-1并置位错误码
函数原型:
void *shmat(int shmid, const void *shmaddr, int shmflg);
例子:
//3、将共享内存段映射到程序中来
char *addr = (char *)shmat(shmid, NULL, 0);
/*
参数1:共享内存ID
参数2:对齐页地址,一般填NULL,让系统选择合适的对齐页
参数3:共享内存的权限
SHM_RDONLY:只读权限
0: 读写权限
*/
功能:映射共享内存的地址到用户空间
返回值:返回值:成功返回0,失败返回-1并置位错误码
函数原型:
int shmdt(const void *shmaddr);
例子:
if(shmdt(addr) == -1)
{
perror("shmdt error");
return -1;
}
//参数:共享内存映射的地址
功能:取消共享内存的映射
返回值:成功返回0,失败返回-1并置位错误码
函数原型:
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
例子:
//删除共享内存
if(shmctl(shmid, IPC_RMID, NULL) == -1)
{
perror("shmctl error");
return -1;
}
/*
参数1:共享内存的id号
参数2:操作指令
IPC_STAT:获取共享内存的属性,此时参数3必须要给定,表示接收共享内存的属性
IPC_SET:设置共享内存的属性
IPC_RMID:删除共享内存,当参数2位该值时,参数3可以忽略,直接填NULL即可
*/
功能:控制共享内存对象
返回值:成功返回0,失败返回-1并置位错误码
函数原型:
int semget(key_t key, int nsems, int semflg);
例子:
//创建信号量集
int semid = semget(key, semcont, IPC_CREAT|IPC_EXCL|0664);
/*
参数1:用于创建信号量集的key值,可以是IPC_PRIVATE,也可以由ftok创建出来
参数2:创建的信号灯集中的信号量的个数
参数3:创建的标识
IPC_CREAT:表示本次操作要创建一个信号量集,如果该key值对应的信号量集已经存在,则直接打开该信号量集对象
IPC_EXCL:表示本次确保要创建一个新的信号量集,如果该信号量集已经存在,则该函数报错,错误码为EEXIST
创建文件的权限,也在该参数中,使用位或连接
*/
功能:创建信号灯集
返回值:成功返回创建出来的共享内存的id,失败返回-1并置位错误码
函数原型:
int semctl(int semid, int semnum, int cmd, ...);
例子:
semctl(semid, semnum, SETVAL, buf);
// 参数1:信号量集的id
// 参数2:要控制的信号灯集中的信号灯的编号,编号从0开始的
// 参数3:控制指令
// IPC_RMID:删除信号灯集,此时参数4可以省略不写,参数2被忽略
// IPC_STAT\IPC_SET:获取或设置信号灯集的属性
// struct semid_ds {
// struct ipc_perm sem_perm; /* Ownership and permissions */
// time_t sem_otime; /* Last semop time */
// time_t sem_ctime; /* Last change time */
// unsigned long sem_nsems; /* No. of semaphores in set */
// };
// The ipc_perm structure is defined as follows (the highlighted fields are settable using IPC_SET):
// struct ipc_perm {
// key_t __key; /* Key supplied to semget(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 */
// };
// GETVAL\SETVAL:设置参数2这一个信号灯中的值,放到参数4提供的整数中
// GETALL\SETALL:设置或获取所有信号灯的值,放入到参数4提供的数组中
// 参数4:可变参数,会根据参数3的不同,使用的类型也不同,所以是一个共用体变量
// union semun {
// int val; /* 参数3为 SETVAL使用该成员 */
// struct semid_ds *buf; /* 参数3位 IPC_STAT, IPC_SET使用该成员 */
// unsigned short *array; /* 参数3位 GETALL, SETALL 使用该成员*/
// struct seminfo *__buf; /* 参数3为 IPC_INFO
// 使用该成员*/
// };
功能:信号量集的控制函数
返回值:对于GETVAL成功返回获取的信号号
对于GETVAL:返回信号量集ID
失败全部返回-1并置位错误码
函数原型:
int semop(int semid, struct sembuf *sops, size_t nsops);
例子:
semop(semid, &buf, 1)
/*
参数1:信号灯集的id
参数2:执行的操作,是一个结构体,成员如下
unsigned short sem_num; 要操作的信号灯的编号
short sem_op; 执行的操作:正数表示释放资源,负数表示申请资源
short sem_flg; 是否阻塞:0表示阻塞,IPC_NOWAIT表示非阻塞
参数3:参数2的个数
*/
功能:完成对信号灯集的相关操作
返回值:成功返回0,失败返回-1并置位错误码
//sem.h
#ifndef SEM_H
#define SEM_H
#include
union semun
{
int val; /* 参数3为 SETVAL使用该成员 */
struct semid_ds *buf; /* 参数3位 IPC_STAT, IPC_SET使用该成员 */
unsigned short *array; /* 参数3位 GETALL, SETALL 使用该成员*/
struct seminfo *__buf; /* 参数3为 IPC_INFO
使用该成员*/
};
// 创建信号灯集并初始化
// 返回值:信号灯集id
// 参数:信号灯集中灯的个数
int sem_create(int semcont);
// 执行申请某个信号灯的资源操作(P操作)
// 返回值:成功返回0,失败返回-1
// 参数1:信号灯集id
// 参数2:要操作的信号灯编号
int P(int semid, int semnum);
// 执行释放某个信号灯的资源操作(V操作)
// 返回值:成功返回0,失败返回-1
// 参数1:信号灯集id
// 参数2:要操作的信号灯编号
int V(int semid, int semnum);
// 删除信号灯集
// 参数:信号灯id号
int sem_del(int semid);
#endif
//sem.c
#include"sem.h"
//定义设置某个信号灯的值的函数
int init_semnum(int semid, int semnum)
{
int val = 0;
printf("请输入第%d号灯的初始值:", semnum+1);
scanf("%d", &val);
getchar();
//准备共用体变量
union semun buf;
buf.val = val; //要传递的数据
//调用semctl函数完成对信号灯的值的设置
if(semctl(semid, semnum, SETVAL, buf)==-1)
{
perror("semctl error");
return -1;
}
return 0;
}
//创建信号灯集并初始化
int sem_create(int semcont)
{
//1、创建key值
key_t key = ftok("/", 't');
if(key == -1)
{
perror("ftok error");
return -1;
}
//2、创建信号量集
int semid = semget(key, semcont, IPC_CREAT|IPC_EXCL|0664);
if(semid == -1)
{
//对错误码进行判断
if(errno == EEXIST)
{
//说明消息队列已经存在,直接打开即可
semid = semget(key, semcont, IPC_CREAT|0664);
return semid;
}
perror("semget error");
return -1;
}
//3、对信号灯进行初始化
for(int i=0; i
//snd.c
#include
#include
#include"sem.h"
int main(int argc, const char *argv[])
{
//11、创建一个信号灯集并初始化
int semid = sem_create(2);
//1、创建key值用于创建共享内存段
key_t key = ftok("/", 't');
if(key == -1)
{
perror("ftok error");
return -1;
}
printf("key = %d\n", key);
//2、创建一个共享内存的对象
int shmid = shmget(key, PAGE_SIZE, IPC_CREAT|0664);
if(shmid == -1)
{
perror("shmget error");
return -1;
}
printf("shmid = %d\n", shmid);
//3、将共享内存段映射到程序中来
char *addr = (char *)shmat(shmid, NULL, 0);
//参数1:共享内存的id号
//参数2:系统自动映射对齐页
//参数3:表示对共享内存的操作权限为读写权限
printf("addr = %p\n", addr); //输出映射的地址
//向共享内存中写入数据
while(1)
{
printf("请输入>>>");
//22、申请资源
P(semid, 0);
fgets(addr, PAGE_SIZE, stdin);
addr[strlen(addr)-1] = 0;
//33、释放资源
V(semid, 1);
printf("发送成功\n");
if(strcmp(addr,"quit") == 0)
{
break;
}
}
//取消映射关系
if(shmdt(addr) ==-1)
{
perror("shmdt error");
return -1;
}
return 0;
}
//recv.c
#include
#include
#include"sem.h"
int main(int argc, const char *argv[])
{
//11、创建信号灯集
int semid = sem_create(2);
//1、创建key值用于创建共享内存段
key_t key = ftok("/", 't');
if(key == -1)
{
perror("ftok error");
return -1;
}
printf("key = %d\n", key);
//2、创建一个共享内存的对象
int shmid = shmget(key, PAGE_SIZE, IPC_CREAT|0664);
if(shmid == -1)
{
perror("shmget error");
return -1;
}
printf("shmid = %d\n", shmid);
//3、将共享内存段映射到程序中来
char *addr = (char *)shmat(shmid, NULL, 0);
//参数1:共享内存的id号
//参数2:系统自动映射对齐页
//参数3:表示对共享内存的操作权限为读写权限
printf("addr = %p\n", addr); //输出映射的地址
//读出共享内存中的数据
while(1)
{
//22、申请资源
P(semid, 1);
printf("消息为:%s\n", addr);
if(strcmp(addr,"quit") == 0)
{
break;
}
//33、释放资源
V(semid, 0);
}
if(shmdt(addr) == -1)
{
perror("shmdt error");
return -1;
}
//删除共享内存
if(shmctl(shmid, IPC_RMID, NULL) == -1)
{
perror("shmctl error");
return -1;
}
//44、删除信号灯集
sem_del(semid);
return 0;
}
输出结果如下: