系统编程04-消息队列(ftok、msgget、msgsnd、msgrcv、msgctl、shmget、shmat、shmdt、shmctl、semget、semctl、semop)

本文介绍了Linux进程间通信(IPC)中的消息队列和共享内存机制。首先解释了IPC对象的概念,然后详细阐述了如何使用消息队列,包括申请key值、创建消息队列、发送和接收消息以及删除消息队列。接着,讨论了共享内存的作用范围和实现步骤,包括申请key值、创建共享内存、映射和解除映射以及删除共享内存。最后提到了信号量作为解决共享内存同步互斥问题的方法。
摘要由CSDN通过智能技术生成

 

目录

 一、linuxIPC对象 (Inter Process Communicate)

1.什么是IPC对象

1)想要操作文件(设备文件/dev):

2)想要操作消息队列:

2.查看系统中所有的IPC对象

1)查看IPC对象: ipcs -a

2)删除IPC对象

3.使用IPC对象之前,要申请key值,那么这个key值是怎么来的??? ---》ftok  --> man 3 ftok

二、进程之间通信方式之一----消息队列

1.消息队列是属于IPC对象,所以使用之前一定要先申请key值。

2.管道通信跟消息队列非常相似,它们之间的区别???

1)管道通信:

2)消息队列:

3.消息队列机制:

4.消息队列作用的范围: linux下任意两个进程。

三、使用消息队列来通信的步骤

1.申请消息队列的key值

2.根据消息队列的key值获取ID号 ,如果该key值对应的消息不存在,会创建 -->man 2 msgget

3.发送 /写入数据

4.从消息队列中读取数据(接收数据) ---》man 2 msgrcv

 5.删除消息队  msgctl=message contol

四、进程之间的通信方式 --- 共享内存。share-memory

1.共享内存也是属于IPC对象,所以使用之前必须申请key值。2.共享内存作用范围以及机制是如何?

1)作用范围:在linux下任意两个进程。2)机制:任意两个进程通过申请key值,ID号,共享内存得到一片内存空间,那么这两个进程就可以将数据写入到共享内存/读取共享内存上的数据进行数据的交换。

3.实现共享内存步骤:

1)先申请key值。

2)根据申请到的key值去申请共享内存的ID号。  -> shmget()  -> man 2 shmget   (share memory get)

#include #include int shmget(key_t key, size_t size, int shmflg); //shm --->share memory参数:    key: key值    size: 共享内存的总字节数,必须是PAGE_SIZE的倍数   #define PAGE_SIZE 1024    shmflg:IPC_CREAT  -> 不存在则创建        IPC_CREAT|0666返回值:    成功:共享内存ID号    失败:-1    3)根据ID号 将共享内存映射至本进程虚拟内存空间的某个区域---映射

4)当不再使用时,解除映射关系

5)当没有进程再需要使用这块共享内存时,删除它-> shmctl()  -> man 2 shmctl

4.例子:通过共享内存实现两个进程的通信

1)写端:

2)读端:

五、处理进程之间通信--共享内存的同步互斥问题  -> 信号量。

1.什么是信号量?

2.信号量的函数接口?--semaphore

1)由于信号量属于IPC对象,所以要申请key值。

2)根据key值申请信号量ID号。 -> semget()  -> man 2 semget

3)控制/设置信号量值参数。  -> semctl()  -> man 2 semctl

4)如何实现信号量的P/V操作?  (P操作: 1->0  V操作: 0->1)

作业 


 一、linuxIPC对象 (Inter Process Communicate)

1.什么是IPC对象

在linux下,IPC对象指的是 消息队列、共享内存、信号量。如果用户需要使用IPC对象来进行进程之间的通信,首先必须为IPC对象申请对应的资源
比如,如果想要使用消息队列来通信,那么就必须先申请消息队列对应的key值和ID号。

1)想要操作文件(设备文件/dev):

需要获得的资源:
>文件的路径名
>文件的文件描述符


2)想要操作消息队列:

需要获得的资源:
>需要获得key值------文件的路径名
>ID号  -----文件的文件描述符

2.查看系统中所有的IPC对象

1)查看IPC对象: ipcs -a

gec@ubuntu:/mnt/hgfs/gz2166/07-系统编程/03/1-code$ ipcs -a
------ Shared Memory Segments -------- //共享内存
key值      ID号
key        shmid      owner      perms      bytes      nattch     status
------ Semaphore Arrays -------- //信号量
key        semid      owner      perms      nsems
------ Message Queues -------- //消息队列
key        msqid      owner      perms      used-bytes   messages
key值: 类似于 文件的路径名
ID号: 类似于文件描述符

2)删除IPC对象

想删除消息队列: ipcrm  -q   消息队列的key值  /  ipcrm  -q   消息队列的ID值   (ipc remove  -queue)
想删除共享内存: ipcrm  -m   共享内存的key值  /  ipcrm  -m   共享内存的ID值 (ipc remove -memory)
想删除信号量: ipcrm  -s   信号量的key值  /  ipcrm  -s   信号量的ID值  (ipc remove sem)

3.使用IPC对象之前,要申请key值,那么这个key值是怎么来的??? ---》ftok  --> man 3 ftok

#include <sys/types.h>
#include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id);
函数作用: 获得一个key值
参数:
    pathname: 一个合法的路径。  常用 "."
    proj_id: 非0整数。   常用 10
返回值:
    成功 key值
    失败 -1
 The resulting value is the same for all pathnames that  name  the  same file,  when  the  same  value  of  proj_id is used.  
 当文件路径pathname 和 proj_id 是一样的时候,两个ftok函数的返回值---key是一样的。
 
 The value returned should be different when the (simultaneously  existing)  files  or  the project IDs differ.
  只要文件路径pathname 或者 proj_id 有一个不一样,返回的key值 就是不一样的。
key_t key = ftok(".",10);
key_t key = ftok(".",10);
例子: 验证key值。
 

#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>

int main()
{
    key_t key;

    //获取key值
    key = ftok(".", 10);
    printf("key:%x\n",key);

    //获取key值
    key = ftok("..", 10);
    printf("key:%x\n",key);


    //获取key值
    key = ftok(".", 20);
    printf("key:%x\n",key);


    //获取key值
    key = ftok(".", 10);
    printf("key:%x\n",key);

    return 0;
}

结论:如果想要使用IPC对象实现两个进程之间的通信,那么两个进程的IPC对象的key值必须是一样的。

验证:两个进程之间当ftok的两个参数一样的时候,获取的key值是一样的
进程1: key_t key = ftok(".",10);
进程2: key_t key = ftok(".",10);

二、进程之间通信方式之一----消息队列

1.消息队列是属于IPC对象,所以使用之前一定要先申请key值。

2.管道通信跟消息队列非常相似,它们之间的区别???

1)管道通信:

不能读取指定的数据,只要管道中有数据,就一定要读取出来,操作的时候使用
open /  write  read -->文件描述符(系统IO来操作 write read)


2)消息队列:

消息队列是一种带有数据标识的特殊管道,消息队列可以读取指定的数据,如果里面有多个数据,但是不符合我的类型,我可以不读取,
操作时候使用消息队列中独有的函数接口:msgsnd / msgrcv

3.消息队列机制:

进程1往消息队列中写入数据时,“类型”+ “数据正文”  //类型 ---数据的编号
进程2从消息队列中读取数据时,只需要提供数据的编号就可以读取到指定的数据了。

4.消息队列作用的范围: linux下任意两个进程。

三、使用消息队列来通信的步骤

1.申请消息队列的key值

key_t key = ftok(".",10);


2.根据消息队列的key值获取ID号 ,如果该key值对应的消息不存在,会创建 -->man 2 msgget

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg); // 类似 之前的open("./1.txt",O_CREAT,0666)
函数作用
    得到一个消息队列的ID号
参数:
    key:消息队列的key值
    msgflg: IPC_CREAT|0666  如果不存在则创建。并且给权限
返回值:
    成功返回 消息队列的ID号
    失败返回 -1
例子:创建一条消息队列,并且把key值和id打印出来
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

//进程1 :  往消息队列中发送数据

//自己定义的消息队列的数据结构体
struct msgbuf{
    long mtype;//数据的编号/类型
    char mtext[1024]; //数据的正文
};

int main()
{
    //1、确定 文件路径名 ---获取消息队列的key值
    key_t key = ftok(".", 100);

    //2、根据文件路径名,打开文件 文件不存在则创建 得到fd ---根据key值 获取消息队列的ID,如果消息队列不存在则创建
    int msgid = msgget(key,IPC_CREAT|0666);
    if(msgid == -1){
        perror("msgget error");
        return -1;
    }

    printf("消息队列 key:%#x msgid:%d\n",key,msgid); //0x

    //3、往文件中写入数据 --------往消息队列中 发送数据
    struct msgbuf data;
    memset(&data,0,sizeof(data));

    strcpy(data.mtext,"nihao");
    data.mtype = 10;

    int ret = msgsnd(msgid, &data,strlen(data.mtext),0);
    if(ret == -1){
    perror("msgsnd error");
    return -1;
    }


    return 0;
}

3.发送 /写入数据

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
函数作用:
    往消息队列中写入数据
参数:
    msqid:消息队列的ID号
    msgp:你要写入的数据,注意传递的是 入的数据结构体的地址
    msgsz:数据正文的大小,也就是 char mtext[1024] 的大小,(注意不是整个结构体的大小)
    msgflg:一般属性,默认为0
返回值:
    成功返回 0
    失败返回 -1

电子相册:
struct node{
    char bmpName[256]; //数据的正文
    int index;//数据的编号
}
消息队列:
10 + "数据"
//写入结构体 是由 用户 自己定义的 ,也就是该结构体的数据类型是我们自己设计
struct msgbuf {
long mtype;       /* 消息类型/数据的编号  而且是>0* /
char mtext[1024];    /* 数据的正文 */
};


4.从消息队列中读取数据(接收数据) ---》man 2 msgrcv

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
参数:
    msqid:消息队列的ID号
    msgp:读取的数据存储到这里,注意 传递的是 读取的数据结构体的地址
    msgsz:数据正文的大小,也就是 char mtext[1024] 的大小,(注意不是整个结构体的大小)
    msgtyp:读取的数据的类型或者说数据的编号
    msgflg:一般属性,默认为0
返回值:
    成功返回  读取的字节数
    失败 返回 -1
练习1:
发送端先发10+“hai”再发20+"nihao"
接收端选择性接收10或者20的内容

#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

//自己定义的消息队列的数据结构体
struct msgbuf
{
	long mtype;//数据的编号/类型
	char mtext[1024]; //数据的正文
};

int main()
{
	//1、确定 文件路径名 ---获取消息队列的key值
	key_t key = ftok(".", 100);

	//2、根据文件路径名,打开文件 文件不存在则创建 得到fd ---根据key值 获取消息队列的ID,如果消息队列不存在则创建
	int msgid = msgget(key,IPC_CREAT|0666);
	if(msgid == -1)
	{
		perror("msgget error");
		return -1;
	}

	printf("消息队列 key:%#x msgid:%d\n",key,msgid); //0x

	//通过消息队列发送
	
	//3、往文件中写入数据 --------往消息队列中 发送数据
	struct msgbuf data;
	memset(&data,0,sizeof(data));
	
	//给char mtext[1024]赋值的时候要用strcpy
	data.mtype = 10; //数据编号是10
	strcpy(data.mtext,"hai");
	
	//msgsend
	int ret = msgsnd(msgid, &data,strlen(data.mtext),0);
	if(ret == -1)
	{
		perror("msgsnd error");
		return -1;
	}	
	
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值