linux下实现System V消息队列实现任意结构体传输

以下是一个实现,可以发送和接收任意类型的结构体消息,而不仅限于特定的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
  1. 消息队列限制

    • 使用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;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值