You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

系统编程作业问题:二次订单触发程序终止及库存更新异常

问题分析与修复方案

问题描述

系统编程作业要求仅用管道和信号实现功能,程序接收3个订单后结束。当前第一个订单可正常处理,但输入第二个订单时程序立即终止,且父进程库存更新功能失效。已创建3个管道分别用于菜单传递、结果接收和库存更新,需排查问题。

代码问题点

  • 子进程仅创建一次,循环逻辑异常
    仅在main开头执行一次fork(),第一次循环后子进程已exit(0),后续循环中父进程的pid指向已终止的子进程,导致wait(NULL)行为异常,程序终止。需每次处理订单前重新创建子进程。

  • 管道描述符重复关闭导致通信中断
    父进程在第一次循环中关闭了管道的读写端,第二次循环时这些描述符已失效,无法进行管道读写,引发程序异常。应在每次fork后,在子/父进程中仅关闭当前不需要的管道描述符,而非循环内重复关闭。

  • 库存更新逻辑错误

    1. 子进程修改的stock是独立内存副本,父进程无法直接获取修改结果,必须通过管道传递更新信息。
    2. 子进程发送的成功信息是"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;
}

修复说明

  1. 每次循环创建新子进程与管道:确保每个订单都有独立的子进程处理,避免子进程终止后循环逻辑异常。
  2. 修正管道描述符管理:在每次fork后,子/父进程仅关闭当前不需要的管道端,循环结束后关闭当前管道的剩余端,避免描述符泄漏或重复关闭。
  3. 修复库存更新的字符串匹配:将子进程发送的成功信息改为"Success",与父进程的判断逻辑一致,确保库存更新代码执行。
  4. 优化信号处理:仅向运行中的子进程发送信号,避免对已终止进程执行无效操作。

内容的提问来源于stack exchange,提问作者kkwadk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 21:14:56