• 进程间通信之IPC对象


    一 、查看ipc对象

    ipcs    //查看IPC对象
    ipcs -m //查看共享内存
    ipcs -q //消息队列
    ipcs -s //显示信号灯集段
    
    • 1
    • 2
    • 3
    • 4

    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]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    #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值就有了
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    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调用失败
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2)映射共享内存(把指定的共享内存映射到多个进程的地址空间)

    void *shmat(int shmid,const void *shmaddr,int shmflg);
    //功能;映射共享内存,把共享内存和地址空间联系起来
    //返回值:成功返回共享内存映射的地址空间,失败返回(void *)-1并置errno
    //参数:
    //	shmid  共享内存段的标识(由shmget()函数得到);
    	shmaddr 将共享内存映射到指定的地址空间,默认值NULL,让系统自动完成映射
    	shmflg 	0	映射可以读写
    			SHM_RDONLY	映射之后只能读
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3)撤销共享内存的映射

    int shmdt(const void *shmaddr);
    //功能:撤销共享内存到进程地址空间的映射
    //返回值:成功返回0 ,失败返回-1,并置 errno
    //参数:
    //	smaddr	共享内存映射到进程指定的地址空间(这里是shmat()函数的返回值)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4)删除共享内存

    int shmctl(int shmid,int cmd,struct shmid_ds *buf);
    //功能:对共享内存进行控制
    //返回值:成功返回0,失败返回-1
    //参数:
    //	shmid	共享内存段的标识【由shmget()函数得到】
    //	cmd		共享内存的控制命令
    			IPC_RMID	删除共享内存
    //	buf		shmid	的一些信息
    			NULL	不需要这个buf
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    //示例:一个进程写入共享内存,另一个进程从共享内存中读数据并打印
    //写端

    #include
    #include
    #include
    #include
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    //读端

    #include
    #include
    #include
    #include
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    3、进程间通信之消息队列【报文队列】

    (1)原理:消息队列是具有一定的格式和优先级的消息链表。有写权限的进程可以向队列中写入数据,有读取权限的进程可以从队列中读取数据。
    操作系统提供了一个struct msg_ids结构体来记录消息队列的全局数据结构。

    
    
    • 1

    (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 对应的消息队列段存在则调用失败,
    //										否则创建新的消息队列段		
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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];//消息的正文大小自定
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    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	 没有指定类型消息,不阻塞立即返回。		
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4)删除消息队列

    int msgctl(int msqid,int cmd,struct msqid_ds *buf);
    //功能:对消息队列进行控制
    //返回值:成功返回0,失败返回-1;
    //参数:
    //	msgid	:消息队列的id
    //	cmd		:消息队列控制命令
    //	buf		:填充当前进程的信息到msqid_ds结构体中
    //			 一般置NULL,不适用	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    示例:

    
    
    • 1

    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	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (3)初始化信号灯集中信号灯的值

    int semctl(int semid,int semnum,int cmd,...);
    //功能:对信号灯集中信号灯进行控制
    //返回值:成功 SETVAL被设置时,成功返回0
    			  SETVAL被设置时,成功返回semval的值
    			  IPC_RMID被设置时,成功返回0
    		失败返回-1,并置errno
    //参数:semid	:信号灯集的ID
    		semnum 	:信号灯在信号灯集中的编号,创建信号灯集的时候,编号从0开始
    		cmd	 	:操作命令
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    (4)操作IPC对象———申请资源(P),释放资源(V)
    (5)删除IPC对象

  • 相关阅读:
    vue导入导出csv文件(插件papaparse + jschardet)
    定时任务基本使用指南(cron 时间表达式、Spring 自带调度器、JDK 原生定时器)
    【Linux】【开发】Linux shell环境下如何产生随机数
    了解 Flutter 开发者们的 IDE 使用情况
    Word控件Spire.Doc 【表单域】教程(五):如何在 C# 中更新 Ask 字段
    Java 初学者必备核心基础知识有哪些?
    怎样可以让导电滑环的信号更好
    vue - 登录 API 接口的封装
    面试算法13:二维子矩阵的数字之和
    vue.extend 实现表格,同时编辑多条数据
  • 原文地址:https://blog.csdn.net/jun8086/article/details/127313337