IPC对象:
内存文件
命令
1.ipcs
查看所有ipc对象的信息2.ipcrm 删除IPC对象
ipcrm -Q/M/S Key
ipcrm -q/m/s 消息队列ID/共享内存ID/信号灯ID
消息队列
步骤
1.创建IPC对象名称
2.创建消息队列
3.发送消息
4.接收消息
5.消息队列销毁
函数接口
(1)ftok
ftok
key_t ftok(const char *pathname, int proj_id);
功能:
创建一个IPC对象名称
参数:
pathname:文件的路径
proj_id:项目ID(8bits)
返回值:
成功返回IPC对象名称
失败返回-1
(2)msgget
msgget
int msgget(key_t key, int msgflg);
功能:
创建一个消息队列
参数:
key:IPC对象名称
msgflg:消息队列属性
IPC_CREAT:创建一个消息队列
IPC_EXCL: 如果消息队列存在就报错
返回值:
成功返回消息队列ID
失败返回 -1
(3) msgsnd
msgsnd
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:
向消息队列中发送消息
参数:
msqid:消息队列的ID号
msgp:发送消息的内容
msgsz:发送消息的大小
msgflg:消息属性 默认为0
0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列
IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回
IPC_NOERROR:若发送的消息大于size字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程。
返回值:
成功返回0
失败返回-1发送消息结构体需自己定义
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data */
};
(4)msgrcv
msgrcv
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
功能:
从消息队列中接收消息
参数:
msqid:消息队列的ID号
msgp:存放消息的空间首地址
msgsz:最多接收消息的大小
msgtyp:接收消息的类型
msgflg:消息属性 默认为0
返回值:
成功返回接收到数据的字节数
失败返回-1
(5)msgctl
msgctl
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:
向消息队列发送命令
参数:
msqid:消息队列的ID号
cmd:命令
cmd取值 含义 IPC_STAT 获取消息队列的状态 IPC_SET 设置消息队列的属性 IPC_RMID 从系统中移除消息队列 返回值:
成功返回0
失败返回-1
#include "../head.h"
typedef struct msgget
{
long mtype;
char mtext[250];
}message_t;
int main()
{
key_t key;
int msgid=0;
message_t sendmsg;
message_t recvmsg;
ssize_t nsize;
int ret=0;
key=ftok("/",'a');//创建key值
if(key==-1)
{
return -1;
}
printf("key=%#x\n",key);
msgid=msgget(key,IPC_CREAT|0664);//创建消息队列
if(msgid==-1)
{
return -1;
}
printf("msgid=%d\n",msgid);
sendmsg.mtype=100;
strcpy(sendmsg.mtext,"how are you");
ret=msgsnd(msgid,&sendmsg,sizeof(sendmsg)-sizeof(long),0);//发送消息,类型100
if(ret==-1)
{
return -1;
}
printf("发送成功\n");
sendmsg.mtype=200;
strcpy(sendmsg.mtext,"hello world");
ret=msgsnd(msgid,&sendmsg,sizeof(sendmsg)-sizeof(long),0);//发送消息,类型200
if(ret==-1)
{
return -1;
}
printf("发送成功\n");
nsize=msgrcv(msgid,&recvmsg,sizeof(recvmsg.mtext),200,0);
if(nsize==-1)
{
return -1;
}
printf("接收到的字节数:%ld,内容为:%s\n",nsize,recvmsg.mtext);
msgctl(msgid,IPC_RMID,NULL);
//查看消息队列命令 ipcs -q
//编译一次发送一次,编译第二次在原来的基础上继续增加
//删除消息队列命令 ipcrm -q <msgid>
return 0;
}
练习:
1.编写两个进程,实现利用消息队列的通信
send.c 创建消息队列 -> 发送消息
recv.c 创建消息队列 -> 接收消息 -> 销毁消息队列
recv.c
#include "../head.h"
typedef struct msg
{
long mtype;
char mtext[200];
}message_t;
int main()
{
key_t key;
message_t recv;
int msgid=0;
ssize_t nsize;
//创建key值
key=ftok("/",'b');
if(key==-1)
{
return -1;
}
//创建消息队列
msgid=msgget(key,IPC_CREAT|0664);
if(msgid==-1)
{
return -1;
}
//接收消息
nsize=msgrcv(msgid,&recv,sizeof(recv.mtext),200,0);
if(nsize==-1)
{
return -1;
}
printf("接收到的字节数:%ld,内容为:%s\n",nsize,recv.mtext);
msgctl(msgid,IPC_RMID,NULL);
return 0;
}
send.c
#include "../head.h"
typedef struct msg
{
long mtype;
char mtext[200];
}message_t;
int main()
{
key_t key;
message_t send;
int msgid=0;
int ret=0;
//创建key值
key=ftok("/",'b');
if(key==-1)
{
return -1;
}
//创建消息队列
msgid=msgget(key,IPC_CREAT|0664);
if(msgid==-1)
{
return -1;
}
//发送消息
send.mtype=200;
strcpy(send.mtext,"hello world");
ret=msgsnd(msgid,&send,sizeof(send.mtext),0);
if(ret==-1)
{
return -1;
}
return 0;
}
运行结果
共享内存
进程间通信最高效的方法
操作步骤:
1.创建 key 值
2.创建共享内存
3.映射共享内存
4.解除映射
5.销毁共享内存在Linux中,每个进程都有属于自己的进程控制块(PCB)和虚拟地址空间(Addr Space),并且都有一个与之对应的页表,负责将进程的虚拟地址与物理地址进行映射,通过内存管理单元(MMU)进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这块区域即共享内存。
共享内存的通信原理示意图:
函数接口
(1)ftok 同上
(2)shmget
shmget
int shmget(key_t key, size_t size, int shmflg);
功能:
创建一个共享内存
参数:
key:IPC对象名称
size:共享内存的大小
shmflg:
IPC_CREAT 创建
IPC_EXCL 如果存在就报错
返回值:
成功返回共享内存ID号
失败返回-1
(3)shmat
shmat
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:
将地址映射到共享内存中
参数:
shmid:共享内存ID号
shmaddr:
NULL: 让系统选择一个合适的地址映射到共享内存中
shmflg:
属性,默认为0
返回值:
成功返回映射到共享空间的地址
失败返回NULL
(4)shmdt
shmdt
int shmdt(const void *shmaddr);
功能:
解除映射空间
参数:
shmaddr:映射到共享内存中的地址
返回值:
成功返回0
失败返回-1
(5)shmctl
shmctl
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:
向共享内存发送命令
参数:
shmid:共享内存ID号
cmd:命令
IPC_RMID 删除
返回值:
成功返回0
失败返回-1
#include "../head.h"
int main()
{
int shmid;
char *pshmadder=NULL;
int ret=0;
//创建key值
key_t key;
key=ftok("/",'c');
if(key==-1)
{
return -1;
}
//创建共享内存
shmid=shmget(key,4096,IPC_CREAT|0664);
if(shmid==-1)
{
return -1;
}
//映射共享内存
pshmadder=shmat(shmid,NULL,0);
if(pshmadder==NULL)
{
return -1;
}
fgets(pshmadder,4096,stdin);
pshmadder[strlen(pshmadder)-1]='\0';
printf("共享空间:%s\n",pshmadder);
//解除映射
ret=shmdt(pshmadder);
if(ret==-1)
{
return -1;
}
//销毁共享空间
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
练习:
1.编写两个进程,实现利用共享内存的通信
write.c 创建共享内存 -> 映射 -> 从终端接收数据写入共享内存中
read.c 创建共享内存 -> 映射 -> 从共享内存中读取数据 -> 显示到终端
read.c
#include "../head.h"
int main()
{
int shmid;
key_t key;
int ret=0;
char *pshread=NULL;
//创建key值
key=ftok("/",'c');
if(key==-1)
{
return -1;
}
//创建共享空间
shmid=shmget(key,4096,IPC_CREAT|0664);
if(shmid==-1)
{
return -1;
}
//映射到共享空间中
pshread=shmat(shmid,NULL,0);
if(pshread==NULL)
{
return -1;
}
while(1)
{
printf("共享空间:%s\n",pshread);
if(strcmp(pshread,"quit")==0)
{
break;
}
}
//解除映射
ret=shmdt(pshread);
if(-1==ret)
{
perror("failed to shmdt");
return -1;
}
//销毁共享空间
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
write.c
#include "../head.h"
int main()
{
int shmid;
key_t key;
char *pshwrite=NULL;
int ret=0;
//创建key值
key=ftok("/",'c');
if(key==-1)
{
return -1;
}
//创建共享空间
shmid=shmget(key,4096,IPC_CREAT|0664);
if(shmid==-1)
{
return -1;
}
//映射到共享空间中
pshwrite=shmat(shmid,NULL,0);
if(pshwrite==NULL)
{
return -1;
}
while(1)
{
fgets(pshwrite,4096,stdin);
pshwrite[strlen(pshwrite)-1]='\0';
if(strcmp(pshwrite,"quit")==0)
{
break;
}
}
//解除映射
ret=shmdt(pshwrite);
if(-1==ret)
{
perror("failed to shmdt");
return -1;
}
return 0;
}
运行结果
read循环打印,当write写入内容,read循环打印新的内容
信号灯
有名信号量数组
函数接口
(1)semget
semget
int semget(key_t key, int nsems, int semflg);
功能:
创建信号量数组
参数:
key:IPC对象名称
nsems:信号量个数
semflg:信号量属性
IPC_CREAT:创建一个信号量数组
返回值:
成功返回所创建的信号灯id
失败返回-1
(2)semctl
semctl
int semctl(int semid, int semnum, int cmd, ...);
功能:
向信号灯发送命令
参数:
IPC_RMID 删除信号灯
SETVAL 设置第semnum-th信号量的值为arg.val
返回值:
成功返回0
失败返回-1
(3)semop
semop
int semop(int semid, struct sembuf *sops, size_t nsops);
功能:
对信号量完成申请和释放操作
参数:
semid:信号灯ID号
sops:信号灯操作数组
unsigned short sem_num; //操作信号量的下标
short sem_op; //对信号量的操作 +1(释放信号量) -1(申请信号量)
short sem_flg; //SEM_UNDO 操作结束后,信号量的值会恢复到原来的值
nsops:数组元素个数
返回值:
成功返回0
失败返回-1
#include "../head.h"
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
(Linux-specific) */
};
int main()
{
int semid=0;
key_t key;
int ret=0;
union semun myun;
struct sembuf mybuf;
//创建IPC对象名称
key=ftok("/",'a');
if(key==-1)
{
perror("failed to ftok");
return -1;
}
//创建信号量数组
semid=semget(key,2,IPC_CREAT|0664);
if(semid==-1)
{
perror("failed to semget");
return -1;
}
//将下标为0的读信号量设置为0
myun.val=0;
ret=semctl(semid,0,SETVAL,myun);
if(ret==-1)
{
perror("failed to semctl");
return -1;
}
//将下标为1的写信号设置为1
myun.val=1;
ret=semctl(semid,1,SETVAL,myun);
if(ret==-1)
{
perror("failed to semctl");
return -1;
}
//申请写信号量
mybuf.sem_num=1;
mybuf.sem_op=-1;
mybuf.sem_flg=SEM_UNDO;
ret=semop(semid,&mybuf,1);
if(-1==ret)
{
perror("failed to semop");
return -1;
}
printf("拿到写信号量了\n");
//释放读信号量
mybuf.sem_num=0;
mybuf.sem_op=+1;
mybuf.sem_flg=SEM_UNDO;
ret=semop(semid,&mybuf,1);
if(-1==ret)
{
perror("failed to semop");
return -1;
}
printf("释放读信号量了\n");
//申请读信号量
mybuf.sem_num=0;
mybuf.sem_op=-1;
mybuf.sem_flg=SEM_UNDO;
ret=semop(semid,&mybuf,1);
if(-1==ret)
{
perror("failed to semop");
return -1;
}
printf("释放读信号量了\n");
#if 0
//再次申请读信号量阻塞
mybuf.sem_num=0;
mybuf.sem_op=-1;
mybuf.sem_flg=SEM_UNDO;
ret=semop(semid,&mybuf,1);
if(-1==ret)
{
perror("failed to semop");
return -1;
}
printf("释放读信号量了\n");
#endif
return 0;
}
练习:
利用信号灯,共享内存实现进程间阻塞通信
sem.c
#include "../head.h"
/*对信号量初始化*/
int init_sem(int semid,int *parray,int len)
{
int i=0;
union semun myun;
int ret=0;
for(i=0;i<len;i++)
{
myun.val=parray[i];
ret=semctl(semid,i,SETVAL,myun);
if(-1==ret)
{
return -1;
}
}
}
/*信号量申请操作*/
int sem_p(int semid,int semun)
{
int ret=0;
struct sembuf mybuf;
mybuf.sem_num=semun;
mybuf.sem_op=-1;
mybuf.sem_flg=SEM_UNDO;
ret=semop(semid,&mybuf,1);
if(-1==ret)
{
return -1;
}
return 0;
}
/*信号量释放操作*/
int sem_v(int semid,int semun)
{
int ret=0;
struct sembuf mybuf;
mybuf.sem_num=semun;
mybuf.sem_op=+1;
mybuf.sem_flg=SEM_UNDO;
ret=semop(semid,&mybuf,1);
if(-1==ret)
{
return -1;
}
return 0;
}
write.c写进程
#include "../head.h"
//编译gcc write.c sem.c -o write
int main()
{
key_t key;
int semid=0;
int shmid=0;
int ret=0;
int val[2]={0,1};
char *pshmaddr=NULL;
//创建key值
key=ftok("/",'a');
if(key==-1)
{
return -1;
}
//创建共享内存
shmid=shmget(key,4096,IPC_CREAT|0664);
if(shmid==-1)
{
perror("failed to shmget");
return -1;
}
//创建信号量数组
semid=semget(key,2,IPC_CREAT|0664);
if(shmid==-1)
{
perror("failed to semget");
return -1;
}
//对信号量数组初始化
init_sem(semid,val,2);
//映射共享内存
pshmaddr=shmat(shmid,NULL,0);
if(NULL==pshmaddr)
{
perror("failed to shmat");
return -1;
}
while(1)
{
//申请写资源
sem_p(semid,1);
gets(pshmaddr);
//释放读资源
sem_v(semid,0);
if(!strcmp(pshmaddr,"quit"))
{
break;
}
}
shmdt(pshmaddr);
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID);
return 0;
}
read.c读进程
#include "../head.h"
//编译gcc read.c sem.c -o read
int main()
{
key_t key;
int semid=0;
int shmid=0;
int ret=0;
int val[2]={0,1};
char *pshmaddr=NULL;
//创建key值
key=ftok("/",'a');
if(key==-1)
{
return -1;
}
//创建共享内存
shmid=shmget(key,4096,IPC_CREAT|0664);
if(shmid==-1)
{
perror("failed to shmget");
return -1;
}
//创建信号量数组
semid=semget(key,2,IPC_CREAT|0664);
if(shmid==-1)
{
perror("failed to semget");
return -1;
}
//对信号量数组初始化
init_sem(semid,val,2);
//映射共享内存
pshmaddr=shmat(shmid,NULL,0);
if(NULL==pshmaddr)
{
perror("failed to shmat");
return -1;
}
while(1)
{
//申请读资源
sem_p(semid,0);
printf("内容:%s\n",pshmaddr);
if(!strcmp(pshmaddr,"quit"))
{
break;
}
//释放写资源
sem_v(semid,1);
}
shmdt(pshmaddr);
shmctl(shmid, IPC_RMID, NULL);
semctl(semid, 0, IPC_RMID);
}
运行结果