系统编程作业问题:二次订单触发程序终止及库存更新异常
问题分析与修复方案
问题描述
系统编程作业要求仅用管道和信号实现功能,程序接收3个订单后结束。当前第一个订单可正常处理,但输入第二个订单时程序立即终止,且父进程库存更新功能失效。已创建3个管道分别用于菜单传递、结果接收和库存更新,需排查问题。
代码问题点
子进程仅创建一次,循环逻辑异常
仅在main开头执行一次fork(),第一次循环后子进程已exit(0),后续循环中父进程的pid指向已终止的子进程,导致wait(NULL)行为异常,程序终止。需每次处理订单前重新创建子进程。管道描述符重复关闭导致通信中断
父进程在第一次循环中关闭了管道的读写端,第二次循环时这些描述符已失效,无法进行管道读写,引发程序异常。应在每次fork后,在子/父进程中仅关闭当前不需要的管道描述符,而非循环内重复关闭。库存更新逻辑错误
- 子进程修改的
stock是独立内存副本,父进程无法直接获取修改结果,必须通过管道传递更新信息。 - 子进程发送的成功信息是
"Success!",但父进程判断的是"Success",字符串不匹配导致库存更新代码从未执行。
- 子进程修改的
信号处理函数的冗余操作
信号处理中kill(pid, SIGINT)在子进程已终止时会报错,且无需向已完成任务的子进程发送信号。
修复后的代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> #include <stdarg.h> int stock[5]; // 库存 pid_t current_pid; // 打印菜单与库存 void printMenuStock() { printf("Menu and Stock:\n"); printf("Pizza %d\n", stock[0]); printf("Salad %d\n", stock[1]); printf("Steak %d\n", stock[2]); printf("Chicken %d\n", stock[3]); printf("Beer %d\n", stock[4]); } // 记录订单到文件 void recordOrderHistory(const char* format, ...) { FILE* file = fopen("Order_History.txt", "a"); if (file == NULL) { printf("Can't record order.\n"); return; } va_list args; va_start(args, format); vfprintf(file, format, args); va_end(args); fclose(file); } // 打印订单历史 void printOrderHistory() { printf("\nOrder List:\n"); FILE* file = fopen("Order_History.txt", "r"); if (file != NULL) { char Order_History[100]; while (fgets(Order_History, sizeof(Order_History), file) != NULL) { printf("%s", Order_History); } fclose(file); } } // 父进程信号处理 void handle_parent_signal(int signal) { if (current_pid > 0) { // 仅向运行中的子进程发送信号 if (kill(current_pid, 0) == 0) { kill(current_pid, SIGINT); waitpid(current_pid, NULL, 0); } } printOrderHistory(); exit(0); } int main() { signal(SIGINT, handle_parent_signal); stock[0] = 1; stock[1] = 2; stock[2] = 0; stock[3] = 0; stock[4] = 2; int order_count = 0; int pipefd_menu[2]; int pipefd_result[2]; while (order_count < 3) { char menu[20]; // 每次循环创建新管道与子进程 pipe(pipefd_menu); pipe(pipefd_result); current_pid = fork(); if (current_pid == 0) { // 子进程 close(pipefd_menu[1]); // 关闭不需要的写端 close(pipefd_result[0]); // 关闭不需要的读端 read(pipefd_menu[0], menu, sizeof(menu)); printf("子进程收到订单: %s\n", menu); int menu_index = -1; if (strcmp(menu, "Pizza") == 0) menu_index = 0; else if (strcmp(menu, "Salad") == 0) menu_index = 1; else if (strcmp(menu, "Steak") == 0) menu_index = 2; else if (strcmp(menu, "Chicken") == 0) menu_index = 3; else if (strcmp(menu, "Beer") == 0) menu_index = 4; if (menu_index != -1 && stock[menu_index] > 0) { recordOrderHistory("%s 1 Success\n", menu); char success_message[20] = "Success"; write(pipefd_result[1], success_message, strlen(success_message) + 1); write(pipefd_result[1], &menu_index, sizeof(int)); } else { recordOrderHistory("%s 1 Fail\n", menu); char fail_message[20] = "Fail"; write(pipefd_result[1], fail_message, strlen(fail_message) + 1); } // 关闭子进程的管道描述符 close(pipefd_menu[0]); close(pipefd_result[1]); exit(0); } else { // 父进程 close(pipefd_menu[0]); // 关闭不需要的读端 close(pipefd_result[1]); // 关闭不需要的写端 printf("\n--- 当前库存 ---\n"); printMenuStock(); printf("请输入订单: "); scanf("%s", menu); getchar(); write(pipefd_menu[1], menu, sizeof(menu)); waitpid(current_pid, NULL, 0); // 等待当前子进程结束 char result_message[20]; read(pipefd_result[0], result_message, sizeof(result_message)); if (strcmp(result_message, "Success") == 0) { int stock_update; read(pipefd_result[0], &stock_update, sizeof(stock_update)); stock[stock_update]--; printf("订单处理成功!\n"); } else if (strcmp(result_message, "Fail") == 0) { printf("订单处理失败(库存不足或菜单不存在)!\n"); } // 关闭父进程的管道描述符 close(pipefd_menu[1]); close(pipefd_result[0]); order_count++; } } printf("\n--- 所有订单处理完成 ---\n"); printOrderHistory(); return 0; }
修复说明
- 每次循环创建新子进程与管道:确保每个订单都有独立的子进程处理,避免子进程终止后循环逻辑异常。
- 修正管道描述符管理:在每次fork后,子/父进程仅关闭当前不需要的管道端,循环结束后关闭当前管道的剩余端,避免描述符泄漏或重复关闭。
- 修复库存更新的字符串匹配:将子进程发送的成功信息改为
"Success",与父进程的判断逻辑一致,确保库存更新代码执行。 - 优化信号处理:仅向运行中的子进程发送信号,避免对已终止进程执行无效操作。
内容的提问来源于stack exchange,提问作者kkwadk
相关产品推荐
相关产品推荐

