进程间通信IPC对象
一 、查看ipc对象
ipcs //查看IPC对象
ipcs -m //查看共享内存
ipcs -q //消息队列
ipcs -s //显示信号灯集段
1.system V进程间的通信原理。
共享内存、消息队列、信号灯集
使用ftok()函数获得IPC对象唯一的名字,称为键key
ftok() 函数将路径名和项目标识符转化为IPC 对象密钥
进程都是通过IPC对象唯一的名字,称为键key,找到IPC对象。但内核还是通过IPC对象的ID来找到它.不同进程只要获得同一IPC对象的键key,就可以实现操作同一IPC对象,从而实际进程间通信.
//调用ftok()函数获得key值
key_t ftok(const char *pathname,int proj_id);
//功能:获得key值
//返回值:成功返回key值,失败返回-1
//参数:
//pathname 一个已经存在的文件路径
//proj_id 子序号,一般只会使用它的低八位(一个字节),常传字符[0,255]
#include"stdio.h"
#include"sys/types.h"
#include"sys/ipc.h"
int main(int argc,const char *argv[])
{
key_t key;
key=ftok("/home/linux",'c');
if(key<0)
{
perror("Fail to ftok");
return -1;
}
//到这里key值就有了
}
key值的生成原理可前往链接往地下翻
2、进程间通信之共享内存
(1)原理:两个或多个进程访问同一片物理内存。进程间通信最快的方法。不同进程把相同的物理空间映射到自己的虚拟地址中,达到访问不同的虚拟空间,操作同一块物理内存的目的,实现进程之间通信。
(2)共享内存实现步骤
1)创建共享内存
int shmget(ket_t key,size_t size,int shmflg);
//功能:申请一块指定大小的共享内存
//返回值:成功返回共享内存的id号,失败返回-1;
//参数:
// key IPC-PRIVATE :用于亲缘间进程的通信
// ftok()函数获得的:用于非亲缘间进程的通信
//
// size 申请共享内存的大小,注意:大小为4k的倍数,如果填1byte,操作
// 系统同样会分配4096byte,但是实际使用只能使用1byte
//
// shmflg 权限标志常用如下:
// IPC_CREAT |0666 //如果共享内存不存在,则创建,否则直接打开已存在的并返回其ID
// IPC_CREAT|IPC_EXCL |0666 //只有在共享内存不存在的时候,新的共享内存才建立,
//若存在,shmget调用失败
2)映射共享内存(把指定的共享内存映射到多个进程的地址空间)
void *shmat(int shmid,const void *shmaddr,int shmflg);
//功能;映射共享内存,把共享内存和地址空间联系起来
//返回值:成功返回共享内存映射的地址空间,失败返回(void *)-1并置errno
//参数:
// shmid 共享内存段的标识(由shmget()函数得到);
shmaddr 将共享内存映射到指定的地址空间,默认值NULL,让系统自动完成映射
shmflg 0 映射可以读写
SHM_RDONLY 映射之后只能读
3)撤销共享内存的映射
int shmdt(const void *shmaddr);
//功能:撤销共享内存到进程地址空间的映射
//返回值:成功返回0 ,失败返回-1,并置 errno
//参数:
// smaddr 共享内存映射到进程指定的地址空间(这里是shmat()函数的返回值)
4)删除共享内存
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
//功能:对共享内存进行控制
//返回值:成功返回0,失败返回-1
//参数:
// shmid 共享内存段的标识【由shmget()函数得到】
// cmd 共享内存的控制命令
IPC_RMID 删除共享内存
// buf shmid 的一些信息
NULL 不需要这个buf
//示例:一个进程写入共享内存,另一个进程从共享内存中读数据并打印
//写端
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
int main(int argc, const char *argv[])
{
key_t key;
int shm_id ;
char *paddr;
key=ftok("/home/linux/",'c');
if(key<0)
{
perror("Fail to ftok");
return -1;
}
printf("key :%d\n",key);
printf("key :%#x\n",key);
shm_id=shmget(key,4096,IPC_CREAT |0666);
if(shm_id<0)
{
perror("Fail to shmget");
return -1;
}
paddr=shmat(shm_id,NULL,0);
if(paddr==(void *)-1)
{
perror("Fail to shmat");
return -1;
}
*paddr=99;
paddr[1]='c';
getchar();
printf("*paddr=%d paddr[1]=%c\n",*paddr,paddr[1]);
if(shmdt(paddr)<0)
{
perror("Fail to shmdt");
return -1;
}
if(shmctl(shm_id,IPC_RMID,NULL)<0)
{
perror("Fail to shmctl");
return -1;
}
return 0;
}
//读端
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
int main(int argc, const char *argv[])
{
key_t key;
int shm_id ;
char *paddr;
key=ftok("/home/linux/",'c');
if(key<0)
{
perror("Fail to ftok");
return -1;
}
shm_id=shmget(key,4096,IPC_CREAT |0666);
if(shm_id<0)
{
perror("Fail to shmget");
return -1;
}
paddr=shmat(shm_id,NULL,0);
if(paddr==(void *)-1)
{
perror("Fail to shmat");
return -1;
}
getchar();
printf("*paddr=%d paddr[1]=%c\n",*paddr,paddr[1]);
if(shmdt(paddr)<0)
{
perror("Fail to shmdt");
return -1;
}
if(shmctl(shm_id,IPC_RMID,NULL)<0)
{
perror("Fail to shmctl");
return -1;
}
return 0;
}
3、进程间通信之消息队列【报文队列】
(1)原理:消息队列是具有一定的格式和优先级的消息链表。有写权限的进程可以向队列中写入数据,有读取权限的进程可以从队列中读取数据。
操作系统提供了一个struct msg_ids结构体来记录消息队列的全局数据结构。
(2)常用操作
1)创建消息队列
int msgget(key_t key,int msgflag);
//功能:创建消息队列信息,得到其ID
//返回值:成功返回显示消息队列的ID,失败返回-1
//参数:key : IPC_PRIVATE 或者ftok()函数得到的key
// msgflg :IPC_CREAT|0666 对应的消息队列不存在,则创建,存在直接返回ID
// IPC_CREAT|IPC_EXEL|0666 对应的消息队列段存在则调用失败,
// 否则创建新的消息队列段
2)发送消息
int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg);
//功能:向消息队列中添加消息
//返回值:成功返回0 ,失败返回-1
//参数:
// msqid :消息队列的ID
// msgp :消息存放的地址
// msgsz :消息正文的大小【sizeof(msg_t)-sizeof(long)】
// msgflg :0阻塞的方式发送消息
// IPC_NOWAIT:非阻塞发送消息(当消息队列中没有可用空
// 间时,不阻塞,立即返回错误码:EAGAIN
//消息模板
struct msgbuf
{
long mtype;//消息的类型
char mtext[1024];//消息的正文大小自定
};
3)接收消息
ssize_t msgrcv(int msqid,void *msgp,size_t msgsz,long msgtyp,int msgflg);
//功能:接收指定类型的消息
//返回值:成功返回接收正文的大小,失败返回-1;
//参数:
// msgid :消息队列ID
// msgp :接收的消息存放的地址
// msgsz :消息正文的大小
// msgtyp :接收的消息的类型
// msgflg :0 阻塞方式调用
// IPC_NOWAIT 没有指定类型消息,不阻塞立即返回。
4)删除消息队列
int msgctl(int msqid,int cmd,struct msqid_ds *buf);
//功能:对消息队列进行控制
//返回值:成功返回0,失败返回-1;
//参数:
// msgid :消息队列的id
// cmd :消息队列控制命令
// buf :填充当前进程的信息到msqid_ds结构体中
// 一般置NULL,不适用
示例:
4.进程间通信之信号灯集
(1)概念:
一个或多个信号组成的一个集合(计数信号量);
解决需求:解决多进程并发执行,对资源的访问出现竞争同步的问题。
原子操作:一次性执行完,不可中断的操作。
(2)信号灯的两种类型
A.二值信号灯:最简单的信号灯形式,信号灯的值只能取0或1,类似互斥锁
B.计数信号灯:信号灯的值可以取任意非负值,用来统计资源,其值代表可用资源的个数。
5.信号灯集和共享内存
信号灯集和共享内存是联合使用的,就是PV操作。
具体流程:
(1)获得一个指定的key值见共享内存;
(2)创建IPC对象
int semget(key_t key,int nsems,int semflg);
//功能:获得一个信号灯集的ID
//返回值:成功返回信号量的ID值,失败返回-1;
//参数:
key :IPC对象对应的key,和共享内存一样
nsems :要创建信号灯的个数
semflg :IPC_CREAT |0666 表示不存在则创建
IPC_CREAT|IPC_EXCL
(3)初始化信号灯集中信号灯的值
int semctl(int semid,int semnum,int cmd,...);
//功能:对信号灯集中信号灯进行控制
//返回值:成功 SETVAL被设置时,成功返回0
SETVAL被设置时,成功返回semval的值
IPC_RMID被设置时,成功返回0
失败返回-1,并置errno
//参数:semid :信号灯集的ID
semnum :信号灯在信号灯集中的编号,创建信号灯集的时候,编号从0开始
cmd :操作命令
(4)操作IPC对象———申请资源(P),释放资源(V)
(5)删除IPC对象