C语言命名管道读取错误值问题排查求助
我编写了两个进程handler.c和calculate.c,二者通过命名管道实现通信。handler.c接收命令行传入的操作数与运算符,将其发送至calculate.c,随后读取计算结果并打印;calculate.c从管道读取操作数和运算符,执行计算后将结果写入管道。但目前遇到问题:无论进行何种操作,handler.c从管道读取结果时始终得到错误值822096384。
HANDLER.C 原代码
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> int main(int argc, char* argv[]) { char* name = argv[1];// pipe name int a, b; int fd = open(name, O_RDWR); a=atoi(argv[3]); b=atoi(argv[4]); write(fd, &a, sizeof(int)); sleep(1); write(fd, &b, sizeof(int)); sleep(1); write(fd, argv[2], sizeof(argv[2])); int res = 0; sleep(1); read(fd, &res, sizeof(int)); //it read the wrong value i.e. 822096384 printf("%d", res); printf("\n %s %s %s = %d\n", argv[3], argv[2], argv[4], res); close(fd); return 0; }
CALCULATE.C 原代码
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> int main(int argc, char* argv[]) { char* name = argv[1]; int res = 0; int a,b; char o; int fd = open(name, O_RDWR); read(fd, &a, sizeof(int)); printf("op1: %d ", a); sleep(1); read(fd, &b, sizeof(int)); printf("op2: %d ", b); sleep(1); read(fd, &o, sizeof(char)); printf("operator: %c ", o); if(strcmp(&o, "+") == 0)//if(o = '+') { res = a + b; } else if(strcmp(&o, "/") == 0) { res = a / b; } else if(strcmp(&o, "-") == 0) { res = a - b; } else if(strcmp(&o, "*") == 0) { res = a * b; } printf("result: %d\n", res); //here res have the correct value write(fd, &res, sizeof(res)); close(fd); return 0; }
运行输出
handler.c 运行输出
User@V-Ubuntu:~/Desktop/pipes$ ./handler pipe + 2 1 822096384 2 + 1 = 822096384
calculate.c 运行输出
User@V-Ubuntu:~/Desktop/pipes$ ./calculate pipe op1: 2 op2: 1 operator: + result: 3
问题原因及修复方案
1. 命名管道读写模式错误
两个进程都用O_RDWR打开管道,导致每个进程同时拥有读写端。handler.c最后读取时,会读到自己写操作残留的垃圾数据,而非calculate.c写入的结果。
修复:拆分读写操作,handler.c先以O_WRONLY打开管道发送数据,关闭后再以O_RDONLY打开读取结果;calculate.c先以O_RDONLY打开读取请求,关闭后再以O_WRONLY打开写入结果。
2. 运算符写入错误
原代码中write(fd, argv[2], sizeof(argv[2]))会写入指针大小的字节(64位系统为8字节),但我们只需要单个运算符字符。且argv[2]是字符串,应取第一个字符argv[2][0]。
修复:改为write(fd, &argv[2][0], sizeof(char))。
3. 运算符比较错误
strcmp(&o, "+")用于比较字符串,但&o是单个字符的地址,并非以\0结尾的字符串,会导致越界访问。
修复:直接用字符比较if (o == '+')替代strcmp。
4. 依赖sleep同步不可靠
用sleep(1)保证同步的方式不可靠,应利用管道的阻塞特性自然同步,删除所有sleep调用。
修复后的代码
修复后的 HANDLER.C
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> int main(int argc, char* argv[]) { char* name = argv[1]; int a, b; // 打开管道发送请求 int fd_write = open(name, O_WRONLY); if (fd_write == -1) { perror("open write pipe failed"); exit(1); } a = atoi(argv[3]); b = atoi(argv[4]); write(fd_write, &a, sizeof(int)); write(fd_write, &b, sizeof(int)); write(fd_write, &argv[2][0], sizeof(char)); close(fd_write); // 打开管道读取结果 int fd_read = open(name, O_RDONLY); if (fd_read == -1) { perror("open read pipe failed"); exit(1); } int res = 0; read(fd_read, &res, sizeof(int)); printf("%d\n", res); printf("%s %s %s = %d\n", argv[3], argv[2], argv[4], res); close(fd_read); return 0; }
修复后的 CALCULATE.C
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> int main(int argc, char* argv[]) { char* name = argv[1]; int res = 0; int a, b; char o; // 打开管道读取请求 int fd_read = open(name, O_RDONLY); if (fd_read == -1) { perror("open read pipe failed"); exit(1); } read(fd_read, &a, sizeof(int)); printf("op1: %d ", a); read(fd_read, &b, sizeof(int)); printf("op2: %d ", b); read(fd_read, &o, sizeof(char)); printf("operator: %c ", o); if (o == '+') { res = a + b; } else if (o == '/') { res = a / b; } else if (o == '-') { res = a - b; } else if (o == '*') { res = a * b; } printf("result: %d\n", res); close(fd_read); // 打开管道写入结果 int fd_write = open(name, O_WRONLY); if (fd_write == -1) { perror("open write pipe failed"); exit(1); } write(fd_write, &res, sizeof(res)); close(fd_write); return 0; }
运行说明
- 先创建命名管道:
mkfifo pipe - 启动计算进程:
./calculate pipe - 启动处理进程并传入参数:
./handler pipe + 2 1
内容的提问来源于stack exchange,提问作者Qaisar Mateen

