以下是一个实现,可以发送和接收任意类型的结构体消息,而不仅限于特定的CustomMsg类型:
// queue.c
#include <unistd.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <stdio.h>
#include "queue.h"
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include "all.h"
SafeQueue* queue_create(int size) {
SafeQueue *q = malloc(sizeof(SafeQueue));
q->buffer = malloc(size);
q->size = size;
q->head = q->tail = q->count = 0;
pthread_mutex_init(&q->lock, NULL);
pthread_cond_init(&q->not_empty, NULL);
pthread_cond_init(&q->not_full, NULL);
return q;
}
void queue_destroy(SafeQueue *q) {
if (q) {
pthread_mutex_destroy(&q->lock);
pthread_cond_destroy(&q->not_empty);
pthread_cond_destroy(&q->not_full);
free(q->buffer);
free(q);
}
}
bool queue_put(SafeQueue *q, const uint8_t *data, int len) {
pthread_mutex_lock(&q->lock);
while (q->count + len > q->size) {
if (pthread_cond_wait(&q->not_full, &q->lock) != 0) {
pthread_mutex_unlock(&q->lock);
return false;
}
}
// 循环缓冲区写入
int first_chunk = q->size - q->tail;
if (len <= first_chunk) {
memcpy(q->buffer + q->tail, data, len);
q->tail = (q->tail + len) % q->size;
} else {
memcpy(q->buffer + q->tail, data, first_chunk);
memcpy(q->buffer, data + first_chunk, len - first_chunk);
q->tail = len - first_chunk;
}
q->count += len;
pthread_cond_signal(&q->not_empty);
pthread_mutex_unlock(&q->lock);
return true;
}
int queue_get(SafeQueue *q, uint8_t *buffer, int max_len, int timeout_ms) {
struct timespec ts;
struct timeval tv;
pthread_mutex_lock(&q->lock);
if (q->count == 0) {
if (timeout_ms == 0) {
pthread_mutex_unlock(&q->lock);
return 0;
}
gettimeofday(&tv, NULL);
ts.tv_sec = tv.tv_sec + timeout_ms / 1000;
ts.tv_nsec = tv.tv_usec * 1000 + (timeout_ms % 1000) * 1000000;
if (ts.tv_nsec >= 1000000000) {
ts.tv_sec++;
ts.tv_nsec -= 1000000000;
}
if (pthread_cond_timedwait(&q->not_empty, &q->lock, &ts) != 0) {
pthread_mutex_unlock(&q->lock);
return 0;
}
}
if (q->count == 0) {
pthread_mutex_unlock(&q->lock);
return 0;
}
int to_read = (q->count < max_len) ? q->count : max_len;
// 循环缓冲区读取
int first_chunk = q->size - q->head;
if (to_read <= first_chunk) {
memcpy(buffer, q->buffer + q->head, to_read);
q->head = (q->head + to_read) % q->size;
} else {
memcpy(buffer, q->buffer + q->head, first_chunk);
memcpy(buffer + first_chunk, q->buffer, to_read - first_chunk);
q->head = to_read - first_chunk;
}
q->count -= to_read;
pthread_cond_signal(&q->not_full);
pthread_mutex_unlock(&q->lock);
return to_read;
}
int queue_count(SafeQueue *q) {
pthread_mutex_lock(&q->lock);
int count = q->count;
pthread_mutex_unlock(&q->lock);
return count;
}
//================================实现任意结构体消息队列========add by luo 250807=====================================
// 错误处理宏
#define ERROR_EXIT(msg) do { perror(msg); exit(EXIT_FAILURE); } while(0)
// 创建消息队列
#if 0
int create_message_queue(key_t key) {
int msgid;
// 创建消息队列 (IPC_CREAT | IPC_EXCL | 0666)
if ((msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666)) == -1) {
if (errno == EEXIST) {
// 如果已存在,则直接获取
msgid = msgget(key, 0666);
if (msgid == -1) {
ERROR_EXIT("msgget failed to get existing queue");
}
} else {
ERROR_EXIT("msgget failed to create new queue");
}
}
return msgid;
}
#endif
int create_message_queue(key_t key) {
int msgid;
struct msqid_ds buf;
// 尝试新建队列
if ((msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666)) == -1) {
if (errno == EEXIST) {
// 获取现有队列
if ((msgid = msgget(key, 0666)) == -1) {
ERROR_EXIT("msgget failed to get existing queue");
}
// ==== 新增:清空队列中的遗留消息 ====
if (msgctl(msgid, IPC_STAT, &buf) == -1)
{
perror("msgctl(IPC_STAT) failed");
}
else
{
while (buf.msg_qnum > 0) { // 当队列中有消息时
char dummy[buf.__msg_cbytes]; // 准备足够大的缓冲区
if (msgrcv(msgid, &dummy, sizeof(dummy), 0, IPC_NOWAIT) == -1) {
if (errno != ENOMSG) perror("msgrcv(flush) failed");
break;
}
msgctl(msgid, IPC_STAT, &buf); // 更新队列状态
}
printf("Flushed %lu messages from existing queue\n", buf.msg_qnum);
}
// ====== 清空结束 ======
} else {
ERROR_EXIT("msgget failed to create new queue");
}
}
return msgid;
}
// 发送任意结构体消息
// msg: 指向包含MSG_HEADER的结构体指针
// msg_size: 结构体总大小
void send_struct_message(int msgid, void *msg, size_t msg_size) {
// 计算实际数据大小 (减去mtype的大小)
size_t data_size = msg_size - sizeof(long);
if (msgsnd(msgid, msg, data_size, IPC_NOWAIT) == -1) {
ERROR_EXIT("msgsnd failed");
}
printf("Sent %zu byte message (type %ld)\n", msg_size, ((long*)msg)[0]);
}
// 接收任意结构体消息
// msg: 指向包含MSG_HEADER的结构体指针
// msg_size: 结构体总大小
// msg_type: 期望接收的消息类型
//阻塞接收方式
void receive_struct_message(int msgid, void *msg, size_t msg_size, long msg_type) {
// 计算实际数据大小 (减去mtype的大小)
size_t data_size = msg_size - sizeof(long);
ssize_t bytes = msgrcv(msgid, msg, data_size, msg_type, 0);
if (bytes == -1) {
ERROR_EXIT("msgrcv failed");
}
printf("Received %zd byte message (type %ld)\n", bytes + sizeof(long), ((long*)msg)[0]);
}
// 非阻塞接收任意结构体消息
int receive_struct_message_nonblocking(int msgid, void *msg, size_t msg_size, long msg_type) {
size_t data_size = msg_size - sizeof(long);
ssize_t bytes = msgrcv(msgid, msg, data_size, msg_type, IPC_NOWAIT);
if (bytes == -1)
{
if (errno == ENOMSG) {
// 没有消息不是错误,只是需要重试
return false;
}
ERROR_EXIT("msgrcv failed");
}
printf("Received %zd byte message (type %ld) [non-blocking]\n",bytes + sizeof(long), ((long*)msg)[0]);
return true;
}
// 删除消息队列
#if 0
void remove_message_queue(int msgid) {
if (msgctl(msgid, IPC_RMID, NULL) == -1) {
if (errno != EIDRM) { // 忽略已删除的错误
perror("msgctl IPC_RMID failed");
}
}
}
#endif
void remove_message_queue(int msgid) {
// 清空消息后再删除队列
struct msqid_ds buf;
if (msgctl(msgid, IPC_STAT, &buf) == 0) {
while (buf.msg_qnum > 0) {
char dummy[4096];
msgrcv(msgid, &dummy, sizeof(dummy), 0, IPC_NOWAIT);
msgctl(msgid, IPC_STAT, &buf);
}
}
msgctl(msgid, IPC_RMID, NULL); // 删除队列
}
// ===================== 使用示例 =====================
#if 0
typedef struct {
MSG_HEADER;
int counter;
char data[64];
} TestMessage;
int main() {
int msgid = create_message_queue(MSG_QUEUE_KEY);
pid_t pid = fork();
if (pid < 0) {
ERROR_EXIT("fork failed");
}
if (pid > 0) { // 父进程 - 发送者
sleep(1); // 等待接收者准备
// 发送3条消息
for (int i = 1; i <= 3; i++) {
TestMessage msg = {
.mtype = 1,
.counter = i,
.data = "Blocking test"
};
send_struct_message(msgid, &msg, sizeof(TestMessage));
sleep(1);
}
// 发送快速连续消息
for (int i = 4; i <= 6; i++) {
TestMessage msg = {
.mtype = 2,
.counter = i,
.data = "Non-blocking test"
};
send_struct_message(msgid, &msg, sizeof(TestMessage));
}
// 等待接收者处理
sleep(2);
remove_message_queue(msgid);
} else { // 子进程 - 接收者
// 1. 阻塞接收演示
printf("=== Blocking Receive Test ===\n");
for (int i = 0; i < 3; i++) {
TestMessage msg;
if (receive_struct_message_blocking(msgid, &msg, sizeof(TestMessage), 1)) {
printf("Blocking received: counter=%d, data=%s\n",
msg.counter, msg.data);
}
}
// 2. 非阻塞接收演示
printf("\n=== Non-blocking Receive Test ===\n");
int received = 0;
while (received < 3) {
TestMessage msg;
if (receive_struct_message_nonblocking(msgid, &msg, sizeof(TestMessage), 2)) {
printf("Non-blocking received: counter=%d, data=%s\n",
msg.counter, msg.data);
received++;
} else {
printf("No message available, doing other work...\n");
sleep(1); // 模拟其他工作
}
}
// 3. 超时接收演示
printf("\n=== Timeout Receive Test ===\n");
TestMessage msg;
if (receive_struct_message_timeout(msgid, &msg, sizeof(TestMessage), 3, 2)) {
printf("Received message within timeout\n");
} else {
printf("Timeout waiting for message (type 3)\n");
}
}
return EXIT_SUCCESS;
}
#endif
-
消息队列限制:
-
使用
msgctl(IPC_STAT)检查队列状态 -
监控队列使用情况,避免溢出
-
这个实现提供了高度灵活的消息传递机制,适用于各种嵌入式场景,从简单的传感器数据采集到复杂的设备控制命令,都可以通过定义适当的结构体来实现高效通信。
=========================阻塞和非阻塞接收方式===============================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <stdbool.h>
// 通用消息头要求
#define MSG_HEADER long mtype
// 消息队列配置
#define MSG_QUEUE_KEY 0x1234
// 错误处理宏
#define ERROR_EXIT(msg) do { perror(msg); exit(EXIT_FAILURE); } while(0)
// 创建消息队列
int create_message_queue(key_t key) {
int msgid = msgget(key, IPC_CREAT | 0666);
if (msgid == -1) {
ERROR_EXIT("msgget failed");
}
return msgid;
}
// 发送任意结构体消息(阻塞)
void send_struct_message(int msgid, void *msg, size_t msg_size) {
size_t data_size = msg_size - sizeof(long);
if (msgsnd(msgid, msg, data_size, 0) == -1) { // 阻塞发送
ERROR_EXIT("msgsnd failed");
}
printf("Sent %zu byte message (type %ld)\n",
msg_size, ((long*)msg)[0]);
}
// 阻塞接收任意结构体消息
bool receive_struct_message_blocking(int msgid, void *msg, size_t msg_size, long msg_type) {
size_t data_size = msg_size - sizeof(long);
ssize_t bytes = msgrcv(msgid, msg, data_size, msg_type, 0);
if (bytes == -1) {
return false;
}
printf("Received %zd byte message (type %ld) [blocking]\n",
bytes + sizeof(long), ((long*)msg)[0]);
return true;
}
// 非阻塞接收任意结构体消息
bool receive_struct_message_nonblocking(int msgid, void *msg, size_t msg_size, long msg_type) {
size_t data_size = msg_size - sizeof(long);
ssize_t bytes = msgrcv(msgid, msg, data_size, msg_type, IPC_NOWAIT);
if (bytes == -1) {
if (errno == ENOMSG) {
// 没有消息不是错误,只是需要重试
return false;
}
ERROR_EXIT("msgrcv failed");
}
printf("Received %zd byte message (type %ld) [non-blocking]\n",
bytes + sizeof(long), ((long*)msg)[0]);
return true;
}
// 带超时的接收(混合模式)
bool receive_struct_message_timeout(int msgid, void *msg, size_t msg_size,
long msg_type, int timeout_sec) {
for (int i = 0; i < timeout_sec * 10; i++) {
if (receive_struct_message_nonblocking(msgid, msg, msg_size, msg_type)) {
return true;
}
// 等待100ms后重试
usleep(100 * 1000);
}
return false;
}
// 删除消息队列
void remove_message_queue(int msgid) {
if (msgctl(msgid, IPC_RMID, NULL) == -1 && errno != EIDRM) {
perror("msgctl IPC_RMID failed");
}
}
// ===================== 使用示例 =====================
typedef struct {
MSG_HEADER;
int counter;
char data[64];
} TestMessage;
int main() {
int msgid = create_message_queue(MSG_QUEUE_KEY);
pid_t pid = fork();
if (pid < 0) {
ERROR_EXIT("fork failed");
}
if (pid > 0) { // 父进程 - 发送者
sleep(1); // 等待接收者准备
// 发送3条消息
for (int i = 1; i <= 3; i++) {
TestMessage msg = {
.mtype = 1,
.counter = i,
.data = "Blocking test"
};
send_struct_message(msgid, &msg, sizeof(TestMessage));
sleep(1);
}
// 发送快速连续消息
for (int i = 4; i <= 6; i++) {
TestMessage msg = {
.mtype = 2,
.counter = i,
.data = "Non-blocking test"
};
send_struct_message(msgid, &msg, sizeof(TestMessage));
}
// 等待接收者处理
sleep(2);
remove_message_queue(msgid);
} else { // 子进程 - 接收者
// 1. 阻塞接收演示
printf("=== Blocking Receive Test ===\n");
for (int i = 0; i < 3; i++) {
TestMessage msg;
if (receive_struct_message_blocking(msgid, &msg, sizeof(TestMessage), 1)) {
printf("Blocking received: counter=%d, data=%s\n",
msg.counter, msg.data);
}
}
// 2. 非阻塞接收演示
printf("\n=== Non-blocking Receive Test ===\n");
int received = 0;
while (received < 3) {
TestMessage msg;
if (receive_struct_message_nonblocking(msgid, &msg, sizeof(TestMessage), 2)) {
printf("Non-blocking received: counter=%d, data=%s\n",
msg.counter, msg.data);
received++;
} else {
printf("No message available, doing other work...\n");
sleep(1); // 模拟其他工作
}
}
// 3. 超时接收演示
printf("\n=== Timeout Receive Test ===\n");
TestMessage msg;
if (receive_struct_message_timeout(msgid, &msg, sizeof(TestMessage), 3, 2)) {
printf("Received message within timeout\n");
} else {
printf("Timeout waiting for message (type 3)\n");
}
}
return EXIT_SUCCESS;
}

583

被折叠的 条评论
为什么被折叠?



