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

循环Fork进程时进程编号打印异常及CPU/IO密集型任务实现问题

问题分析与修复方案

问题1:进程完成信息重复打印

你的代码中,所有子进程和父进程都会执行最后的等待循环,导致打印次数远超N次。正确逻辑应该是:

  • 子进程完成任务后直接退出,不参与等待和打印
  • 仅由父进程负责等待所有子进程,并打印每个进程的完成信息

另外,原cpuNum和ioNum的分配逻辑错误:fork后每个子进程拥有独立的变量副本,无法通过cpuNum--来分配任务,应该在创建子进程时,根据循环序号n直接指定该进程是CPU密集型还是IO密集型。

问题2:CPU/IO密集型伪工作的正确性

  • CPU密集型:用volatile修饰循环变量的写法是对的,能防止编译器优化空循环,确保进程持续占用CPU资源。可通过调整循环次数控制CPU工作时长。
  • IO密集型:sleep(200)只是让进程休眠,没有实际IO操作,不属于真正的IO密集型任务。应模拟频繁IO行为,比如反复读写文件(如/dev/null),这样才会产生真实的IO负载。

修正后的代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdint.h>
#include <fcntl.h>
#include <string.h>

// 假设waitStats函数实现(需根据实际需求完善)
int waitStats(int options, uint32_t *rtime, uint32_t *wtime) {
    pid_t pid = waitpid(-1, NULL, options);
    if (pid == -1) return -1;
    // 示例赋值,实际需通过rusage等系统调用计算真实时间
    *rtime = 100;
    *wtime = 50;
    return pid;
}

int main(int argc, char* argv[]) {
    // 参数格式检查:需传入 -c <CPU进程数> -i <IO进程数>
    if (argc != 5 || strcmp(argv[1], "-c") != 0 || strcmp(argv[3], "-i") != 0) {
        fprintf(stderr, "用法: %s -c <cpu_num> -i <io_num>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    int cpuNum = atoi(argv[2]);
    int ioNum = atoi(argv[4]);
    const int MAX_PROC = cpuNum + ioNum;
    printf("\n总进程数: %d\n", MAX_PROC);

    int totRunTime = 0;
    int totWaitTime = 0;
    uint32_t rTime = 0;
    uint32_t wTime = 0;

    // 创建子进程
    for (int n = 0; n < MAX_PROC; n++) {
        pid_t pid = fork();
        if (pid == -1) {
            perror("fork失败");
            exit(EXIT_FAILURE);
        }
        if (pid == 0) {
            // 子进程:根据序号分配任务类型
            if (n < cpuNum) {
                printf("子进程 %d (PID: %d) 开始CPU密集型任务\n", n, getpid());
                // CPU密集型:空循环(volatile防止编译器优化)
                for (volatile int i = 0; i < 1000000000; i++);
            } else {
                printf("子进程 %d (PID: %d) 开始IO密集型任务\n", n, getpid());
                // IO密集型:反复写入/dev/null模拟IO负载
                int fd = open("/dev/null", O_WRONLY);
                if (fd == -1) {
                    perror("打开/dev/null失败");
                    exit(EXIT_FAILURE);
                }
                char buf[1024];
                memset(buf, 'a', sizeof(buf));
                for (int i = 0; i < 1000; i++) {
                    write(fd, buf, sizeof(buf));
                }
                close(fd);
                // 也可用多次短休眠模拟IO等待:for (int i=0; i<200; i++) usleep(1000);
            }
            // 子进程完成任务后直接退出,不执行后续代码
            exit(EXIT_SUCCESS);
        }
        // 父进程继续创建下一个子进程
    }

    // 仅父进程执行:等待所有子进程并统计信息
    printf("\n父进程等待子进程完成...\n");
    for (int p = 0; p < MAX_PROC; p++) {
        if (waitStats(0, &rTime, &wTime) >= 0) {
            printf("进程 %d 完成 | 运行时间: %u, 等待时间: %u\n", p, rTime, wTime);
            totRunTime += rTime;
            totWaitTime += wTime;
        }
    }

    // 计算并打印平均时间
    if (MAX_PROC > 0) {
        printf("\n平均运行时间: %d, 平均等待时间: %d\n", 
               totRunTime / MAX_PROC, totWaitTime / MAX_PROC);
    }

    exit(EXIT_SUCCESS);
}

关键修复点说明

  1. 任务分配逻辑:通过循环序号直接指定子进程任务类型,避免变量副本导致的分配错误。
  2. 子进程退出逻辑:子进程完成任务后立即exit,确保只有父进程执行等待和打印操作。
  3. IO任务修正:替换sleep为实际文件写入操作,真实模拟IO负载;也可根据需求改用短时间多次休眠。
  4. 参数检查:增加命令行参数格式校验,避免非法输入导致崩溃。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 20:55:20