C语言消息队列进程通信问题:Analyzer无法完整打印文件名
消息队列进程通信:文件名无法完整接收打印的问题排查
问题背景
用C语言实现基于System V消息队列的进程通信程序,包含Scanner和Analyzer两个子进程:
- Scanner进程扫描指定目录,将普通文件名发送给Analyzer
- Analyzer进程接收文件名并打印,但实际输出中文件名被截断,无法完整显示
原始代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ipc.h> #include <sys/msg.h> #include <sys/stat.h> #include <unistd.h> #include <sys/wait.h> #include <dirent.h> #define MAX_DIM 2048 typedef struct { char eof; long mtype; // 0 Analyzer long count; char mtext[MAX_DIM]; } msg; void child_scanner(const char *directory, int coda) { DIR *dp; struct dirent *entry; msg messaggio; // struct dove salvare le informazioni struct stat statBuffer; // buffer su dove scrivere le informazioni // apro la directory if ((dp = opendir(directory)) == NULL) { perror(directory); exit(1); } // cambia la directory corrente if (chdir(directory) == -1) { perror(directory); exit(1); } while ((entry = readdir(dp)) != NULL) { // prendo le informazioni lstat(entry->d_name, &statBuffer); if (S_ISREG(statBuffer.st_mode)) { strcpy(messaggio.mtext, entry->d_name); messaggio.mtype = 0; messaggio.eof = 0; messaggio.count = 0; if (msgsnd(coda, &messaggio, sizeof(char) + strlen(messaggio.mtext) + 1, 0) == -1) { perror("msgsnd"); exit(1); } printf("Scanner: %s\n", messaggio.mtext); // stampa il nome del file } } strcpy(messaggio.mtext, "quit\n"); messaggio.eof = 1; messaggio.mtype = 0; messaggio.count=0; if (msgsnd(coda, &messaggio, sizeof(char) + strlen(messaggio.mtext) + 1, 0) == -1) { perror("msgsnd"); exit(1); } closedir(dp); exit(0); } void child_analyzer(int coda) { printf("\n"); msg messaggio; do { if (msgrcv(coda, &messaggio, sizeof(messaggio) - sizeof(long), 0, 0) == -1) { perror("msgrcv"); exit(1); } printf("Analyzer: %s\n", messaggio.mtext); } while (messaggio.eof != 1); exit(0); } int main(int argc, char const *argv[]) { int ds_coda; // coda struct stat statBuffer; // buffer di tipo struct stat su cui scrivere le informazioni if (argc > 2) { fprintf(stderr, "uso: %s <directory>\n", argv[0]); exit(1); } if ((ds_coda = msgget(IPC_PRIVATE, IPC_CREAT | 0600)) == -1) { perror("coda msgget"); exit(1); } if (((stat(argv[1], &statBuffer)) == -1 || (!S_ISDIR(statBuffer.st_mode)))) { fprintf(stderr, "Problema con parametro: '%s'\n", argv[1]); exit(1); } if (fork() == 0) { // Figlio Scanner child_scanner(argv[1], ds_coda); } else if (fork() == 0) { sleep(1); child_analyzer(ds_coda); } wait(NULL); wait(NULL); msgctl(ds_coda, IPC_RMID, NULL); return 0; }
输入命令
./alpha-usage /Users/valentinofabrisvalenti/Desktop/Sistemi\ Operativi\ \(Laboratorio\)/Esami/Code/12-02-2016
实际输出
Scanner: palindrome-filter.c Scanner: dictionary.txt Scanner: palindrome-filter Scanner: compito_2016-02-12 (1).pdf Analyzer: palin Analyzer: palin Analyzer: palin Analyzer: compito_2016 Analyzer: compito_2016
问题原因分析
1. 消息结构体违反System V消息队列规范
System V消息队列要求消息结构体的第一个成员必须是long类型的mtype,内核通过该字段识别消息类型。你的结构体把eof放在mtype前面,导致内核解析时:
- 错误将
eof及后续字节当作mtype值 - 消息的
eof、count、mtext字段全部错位,接收时无法正确映射到结构体成员
2. 消息长度参数不匹配
- 发送端(msgsnd):传入的长度
sizeof(char) + strlen(messaggio.mtext) + 1仅包含部分数据,未覆盖eof和count字段,不符合消息队列对“mtype之后所有数据长度”的要求 - 接收端(msgrcv):传入的长度
sizeof(messaggio) - sizeof(long)基于“mtype是第一个成员”的错误假设计算,导致接收数据长度错误,截断了文件名
修复方案
步骤1:修正消息结构体顺序
将mtype移到结构体第一个位置:
typedef struct { long mtype; // 必须是第一个成员,用于消息类型识别 char eof; long count; char mtext[MAX_DIM]; } msg;
步骤2:统一消息长度参数
发送和接收时,使用sizeof(msg) - sizeof(long)作为消息长度(即mtype之后所有数据的字节数):
- 修正Scanner中的
msgsnd调用(两处都要改):
// 替换原来的msgsnd长度参数 if (msgsnd(coda, &messaggio, sizeof(msg) - sizeof(long), 0) == -1)
- Analyzer中的
msgrcv参数保持sizeof(msg) - sizeof(long)即可(现在计算正确)
步骤3:移除冗余的sleep
Analyzer进程中的sleep(1)是不必要的,消息队列本身会同步进程间的收发操作,移除后可避免不必要的延迟。
修复后预期输出
Scanner: palindrome-filter.c Scanner: dictionary.txt Scanner: palindrome-filter Scanner: compito_2016-02-12 (1).pdf Analyzer: palindrome-filter.c Analyzer: dictionary.txt Analyzer: palindrome-filter Analyzer: compito_2016-02-12 (1).pdf Analyzer: quit
内容的提问来源于stack exchange,提问作者Valentino Fabris Valenti
相关产品推荐
相关产品推荐

