C语言IPC队列动态消息问题:msgsnd/msgrcv错误及跨平台打印异常
问题分析与解决方案
核心问题概述
- IPC消息队列的
msgsnd/msgrcv调用存在错误 - 跨平台输出不一致:MacOS下
msgsnd前缓冲区内容正常,Ubuntu下打印部分内容后出现乱码
问题定位与修复
1. IPC消息队列调用错误
消息队列调用错误通常集中在结构体定义和参数传递上:
- 消息结构体必须以
long mtype作为第一个成员,发送与接收时需保证类型匹配 msgsnd的第三个参数是消息正文的长度(不包含mtype字段),常见错误是传入整个结构体的长度msgrcv的第四个参数是接收正文的最大长度,需与发送端一致,同时要指定正确的消息类型(msgtyp参数)
正确调用示例:
// 标准消息结构体定义 typedef struct { long mtype; char mtext[BUFSIZ]; } msgbuf; // 发送消息 msgbuf buf; buf.mtype = 1; // 自定义消息类型,接收端需对应 strncpy(buf.mtext, content, BUFSIZ-1); buf.mtext[BUFSIZ-1] = '\0'; // 传入正文长度(含终止符) msgsnd(msgid, &buf, strlen(buf.mtext)+1, 0); // 接收消息 // 仅接收类型为1的消息,正文最大长度为BUFSIZ msgrcv(msgid, &buf, BUFSIZ, 1, 0);
2. Ubuntu下的乱码问题
乱码主要由以下因素导致:
- 缓冲区未终止:读取文件内容时未强制添加
\0,Ubuntu内存布局差异会导致读取到垃圾数据 - 线程同步缺失:生产者写入队列与消费者读取时无同步机制,导致缓冲区被覆盖
- 文件编码差异:若文件非UTF-8编码,Ubuntu终端解码会出现乱码
修复建议:
- 读取文件内容后,强制在缓冲区末尾添加
\0,确保字符串终止 - 添加互斥锁与信号量实现生产者-消费者同步,避免数据竞争
- 统一文件编码为UTF-8
同步代码示例:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; sem_t sem; sem_init(&sem, 0, 0); // 生产者端 pthread_mutex_lock(&mutex); // 队列写入操作 pthread_mutex_unlock(&mutex); sem_post(&sem); // 消费者端 sem_wait(&sem); pthread_mutex_lock(&mutex); // 队列读取操作 pthread_mutex_unlock(&mutex);
完整修复后的代码
#include <stdio.h> #include <stdlib.h> #include <dirent.h> #include <string.h> #include <sys/msg.h> #include <pthread.h> #include <semaphore.h> #define BUFSIZ 1024 #define MSG_TYPE 1 typedef struct { long mtype; char mtext[BUFSIZ]; } msgbuf; int msgid; pthread_mutex_t mutex; sem_t sem; // 消费者线程函数 void *consumer(void *arg) { msgbuf buf; while(1) { sem_wait(&sem); pthread_mutex_lock(&mutex); if(msgrcv(msgid, &buf, BUFSIZ, MSG_TYPE, 0) == -1) { perror("msgrcv"); pthread_mutex_unlock(&mutex); break; } pthread_mutex_unlock(&mutex); printf("%s", buf.mtext); // 检测结束标记(空字符串) if(strlen(buf.mtext) == 0) break; } return NULL; } // 遍历文件夹并处理符合模式的文件 void process_folder(const char *folder, const char *pattern) { DIR *dir = opendir(folder); if(!dir) { perror("opendir"); return; } struct dirent *entry; msgbuf buf; buf.mtype = MSG_TYPE; char path[BUFSIZ]; FILE *fp; size_t n; while((entry = readdir(dir)) != NULL) { if(strstr(entry->d_name, pattern) != NULL) { snprintf(path, BUFSIZ, "%s/%s", folder, entry->d_name); fp = fopen(path, "r"); if(!fp) { perror("fopen"); continue; } while((n = fread(buf.mtext, 1, BUFSIZ-1, fp)) > 0) { buf.mtext[n] = '\0'; // 强制添加字符串终止符 pthread_mutex_lock(&mutex); if(msgsnd(msgid, &buf, strlen(buf.mtext)+1, 0) == -1) { perror("msgsnd"); pthread_mutex_unlock(&mutex); fclose(fp); closedir(dir); return; } pthread_mutex_unlock(&mutex); sem_post(&sem); } fclose(fp); } } // 发送结束标记 memset(buf.mtext, 0, BUFSIZ); pthread_mutex_lock(&mutex); msgsnd(msgid, &buf, 1, 0); pthread_mutex_unlock(&mutex); sem_post(&sem); closedir(dir); } int main(int argc, char *argv[]) { if(argc != 3) { fprintf(stderr, "Usage: %s <folder> <pattern>\n", argv[0]); exit(EXIT_FAILURE); } // 创建消息队列 key_t key = ftok(".", 'M'); if(key == -1) { perror("ftok"); exit(EXIT_FAILURE); } msgid = msgget(key, IPC_CREAT | 0666); if(msgid == -1) { perror("msgget"); exit(EXIT_FAILURE); } // 初始化同步工具 pthread_mutex_init(&mutex, NULL); sem_init(&sem, 0, 0); // 创建消费者线程 pthread_t tid; if(pthread_create(&tid, NULL, consumer, NULL) != 0) { perror("pthread_create"); exit(EXIT_FAILURE); } // 处理目标文件夹 process_folder(argv[1], argv[2]); // 等待消费者线程结束 pthread_join(tid, NULL); // 清理资源 msgctl(msgid, IPC_RMID, NULL); pthread_mutex_destroy(&mutex); sem_destroy(&sem); return EXIT_SUCCESS; }
编译与运行命令
gcc program.c -o program -pthread ./program my_folder score
内容的提问来源于stack exchange,提问作者Tahseen
相关产品推荐
相关产品推荐

