linux 消息队列

管道:没有数据的类型,数据遵守“先进先出”的原则。  
消息队列:加强型的管道,他可以指定存放的数据类型,与取走的数据类型,方便不同进程取数据。

消息队列,共享内存,信号量:属于system V(5) 版本的进程通信,也叫ipc通信对象,除了system V进程间的通信外,我们还有POSIX版本的。

查看system V (5)版本的通信对象命令:  
查看消息队列:  ipcs -q  
查看共享内存:  ipcs -m
查看信号量:     ipcs -s  

查看全部:ipcs -a

删除消息队列: 
ipcrm  -q   (对象ID)
ipcrm  -Q    (键值)

消息队列的信息
--------- 消息队列 -----------
键        msqid      		拥有者  权限     已用字节数 消息      
(KEY值)(消息队列通信对象)

//如何创建消息队列  
1.创建KEY值 
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);//proj_id ->不能超过255
							                
2.获取消息队列通信对象ID
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
参数一:键值key  
参数二:权限  
	  IPC_CREAT 创建
	  IPC_EXCL  检查是否存在
	  mode      0666
	  返回值:成功 返回 对象ID 
	  失败 返回 -1
					
3.进行数据的交互 
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>	  
//发送
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数一:需要发送的消息队列ID
参数二:数据缓存区  
参数三:数据的大小 
参数四: 是否阻塞  
		0   阻塞  
		IPC_NOWAIT   不阻塞
		返回值: 成功返回 0  
		失败返回 -1
					 		
PS,PS,PS 注意!!!! 
数据的缓冲区必须要定义为如下结构体: 
struct msgbuf {
      long mtype;       /*数据类型必须大于0, must be > 0 */
      char mtext[1];    /*数据,该数组的长度任意定义*/
};

//读取   
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
//参数一:对象ID  
//参数二:数据的缓存区  
//参数三:需要获取的数据大小  
//参数四:数据的类型  
参数五:是否阻塞  
	  0   阻塞  
	  IPC_NOWAIT   不阻塞
	  返回值: 成功返回  读到的数据大小 
	  失败返回 -1
			 
4.销毁消息队列  
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>			
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数一:对象ID
参数二:控制命令     	IPC_RMID  -》删除  
参数三:设置 获取的时候使用 
	   返回值:成功 0 
	   失败 -1
			        
---------------------------------信号量---------------------------------------------------------------
信号量的作用: 保护共享资源防止进程之间的竞争。  
信号量的版本分为两种:1.SYSTEM V  2.POSIX  (有名 无名)
posix :有名信号量用于不同进程之间的数据保护 
posix : 无名信号量用于不同线程之间的数据保护

POSIX版本的有名信号量的使用:   记得添加 posix的库文件   	-lpthread
							
1. 创建信号量对象 
#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>

sem_t *sem_open(const char *name, int oflag);  //直接打开信号量
//参数一:创建的信号量路径   (路径只能是 /目录下的 例如:/sem  /sem1)
//参数二:权限 
		 O_CREAT  -》创建 
		 O_EXCL   -》检查是否存在
//返回值:成功返回   信号地址
		 打开失败  SEM_FAILED
						
//带创建打开并赋值			
sem_t *sem_open(const char *name, int oflag,	  //直接打开信号量并赋值
                mode_t mode, unsigned int value);
                       
2.对信号量进行P  V 操作  
p操作申请资源   -1 -> sem_wait(3), 等待申请
v操作释放资源   +1 -> sem_post(3), 释放
		
int sem_wait(sem_t *sem);  //当申请的资源值为0时,函数阻塞!!
int sem_post(sem_t *sem);  
参数一:需要操作的信号量指针
返回值:成功0 
	  失败-1
			   
获取信号量的值: 
#include <semaphore.h>
int sem_getvalue(sem_t *sem, int *sval);
参数一:需要获取的信号量指针
参数二:获取后的值保存的地方
返回值:成功 0 
	  失败 -1
							   
关闭信号量
#include <semaphore.h>
int sem_close(sem_t *sem);
		 
销毁信号量
int sem_unlink(const char *name);
//msg_send.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h> //错误宏
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

struct msgbuf 
{
   long mtype; 
   char mtext[1024];
};

int main()
{
	//创建一个对象的KEY值 
    key_t key=ftok("/home/gec",66);
	if(key < 0)
	{
		perror("");
		exit(0);
	}

		
	int msgid=0;
label:	 
	//获取消息队列的对象ID 
	msgid=msgget(key,IPC_CREAT|IPC_EXCL|0666);
	if(msgid < 0)
	{
		//perror("creat fail\n");
		//处理错误信息 
		if(errno == EEXIST)
		{
			//printf("对象ID已经存在啦!\n");
			//删除
			char del[1024]={0};
			sprintf(del,"ipcrm  -Q %d",key);
			system(del);
			
			goto label;
		}
	}
	else 
	{
		printf("创建消息队列成功\n");
	}
	struct msgbuf  date;  
	date.mtype = 123; //设置类型 
	//date.mtext = "hello world";
	
	while(1)
	{
		//数据的发送
		scanf("%s",date.mtext);
		msgsnd(msgid,&date,strlen(date.mtext),0);
	}
	
	//销毁消息队列  
	msgctl(msgid,IPC_RMID,NULL);
	return 0;
}
//msg_recv.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h> //错误宏
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

struct msgbuf 
{
   long mtype; 
   char mtext[1024];
};

int main(int argc,char *argv[])
{
	//创建一个对象的KEY值 
    key_t key=ftok("/home/gec",66);
	if(key < 0)
	{
		perror("");
		exit(0);
	}

	//获取消息队列的对象ID 
	int msgid=msgget(key,IPC_CREAT|0666);
	if(msgid < 0)
	{
		perror("creat fail\n");
	}
	else 
	{
		printf("创建消息队列成功\n");
	}
		
	//读取消息队列中的数据
	while(1)
	{	
		struct msgbuf  date;
		bzero(&date,sizeof(date));		
		msgrcv(msgid,&date,sizeof(date),123,0);
		printf("date = %s\n",date.mtext);
	}
		
	//销毁消息队列  
	msgctl(msgid,IPC_RMID,NULL);
	return 0;
}
Linux系统中的消息队列是一种进程间通信(IPC)机制,允许不同进程之间通过发送和接收消息进行数据交换。它广泛应用于分布式系统、消息中间件等场景中,例如 RabbitMQ 和 Kafka。 ### 消息队列的基本使用 Linux 提供了 System V 消息队列和 POSIX 消息队列两种实现方式。System V 消息队列是较早的实现方式,使用 `msgget`、`msgsnd`、`msgrcv` 等系统调用来操作消息队列。以下是一个简单的使用示例: ```c #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <stdio.h> #include <string.h> struct my_msg { long mtype; char mtext[200]; }; int main() { struct my_msg msg; int msqid; key_t key = ftok("msgqueue", 'B'); msqid = msgget(key, 0666 | IPC_CREAT); if (msqid == -1) { perror("msgget"); return 1; } while (1) { msgrcv(msqid, &msg, sizeof(msg.mtext), 0, 0); printf("Received: %s\n", msg.mtext); } return 0; } ``` 在上述代码中,首先使用 `msgget` 创建或获取一个消息队列标识符,然后使用 `msgrcv` 接收消息。发送端则使用 `msgsnd` 发送消息。 ### 消息队列的配置 Linux 提供了几个关键的配置参数用于控制消息队列的行为: - `/proc/sys/kernel/msgmax`:指定单个消息的最大长度,默认值为 8192 字节。 - `/proc/sys/kernel/msgmnb`:指定一个消息队列的最大字节数,默认值为 16384 字节。 - `/proc/sys/kernel/msgmni`:指定消息队列标识的最大数目,默认值为 16[^2]。 这些参数可以通过 `sysctl` 命令进行修改。例如,要修改 `msgmax` 的值,可以使用以下命令: ```bash sudo sysctl -w kernel.msgmax=16384 ``` 如果需要永久生效,可以将配置写入 `/etc/sysctl.conf` 文件中: ```bash echo "kernel.msgmax = 16384" | sudo tee -a /etc/sysctl.conf sudo sysctl -p ``` ### 消息队列的管理 在 Linux 中,可以使用 `ipcs` 命令查看当前系统中的消息队列信息: ```bash ipcs -q ``` 该命令会列出所有当前存在的消息队列,包括它们的标识符、权限、所有者等信息。 要删除一个消息队列,可以使用 `ipcrm` 命令。例如,要删除一个标识符为 `123456` 的消息队列,可以执行以下命令: ```bash ipcrm -q 123456 ``` ### 消息队列的应用 除了传统的 System V 消息队列,现代系统中还常用一些高级消息队列系统,如 RabbitMQ 和 Kafka。 RabbitMQ 是一个基于 AMQP 协议的企业级消息队列系统,适用于需要高可靠性和复杂路由规则的场景。安装 RabbitMQ 通常需要先安装 Erlang 环境,然后通过包管理工具安装 RabbitMQ 服务器并启动服务。 Kafka 是一种高吞吐量的分布式消息队列系统,适用于大数据实时处理场景。Kafka 的安装相对简单,下载解压后即可启动服务。以下是一个消费 Kafka 消息的示例命令: ```bash ./bin/kafka-console-consumer.sh --topic test-topic --from-beginning --bootstrap-server localhost:9092 ``` 该命令会从指定的主题中消费消息,并从头开始读取。 ### 消息队列的优势与适用场景 消息队列的优势在于能够解耦生产者和消费者,提高系统的可扩展性和可靠性。它适用于需要异步处理、流量削峰、日志收集等场景。例如,在电商系统中,订单服务可以将订单信息发送到消息队列,库存服务和物流服务则分别从队列中获取订单信息进行处理。 ### 消息队列的注意事项 在使用消息队列时,需要注意以下几点: 1. **消息丢失**:确保消息在传输过程中不会丢失,可以通过持久化、确认机制等方式实现。 2. **消息重复**:消费者在处理消息时可能会遇到重复的消息,需要保证幂等性。 3. **消息顺序**:某些场景下需要保证消息的顺序性,这需要消息队列系统支持。 4. **性能调优**:根据实际需求调整消息队列的配置,以获得最佳性能。 ### 总结 Linux 消息队列是一种强大的进程间通信机制,适用于多种应用场景。无论是传统的 System V 消息队列,还是现代的 RabbitMQ 和 Kafka,都提供了丰富的功能和灵活的配置选项。通过合理配置和使用消息队列,可以有效提升系统的可靠性和可扩展性。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值