进程间通信之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对象

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jun8086

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值