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

如何实现维持1-8个并发子进程并正确读取stdin命令?

多进程命令执行程序修复方案

核心问题定位

现有代码存在三个关键缺陷导致仅能执行单条命令:

  • 未实现循环读取stdin的逻辑,处理完首条命令后就进入阻塞等待,无法读取后续输入
  • 缺乏并发进程数控制机制,无法维持1-8个活跃子进程的要求
  • 未按需求实现16字节块读取stdin的命令解析逻辑,可能导致命令读取不完整

分步修复实现

1. 实现16字节块式的命令读取逻辑

按16字节块读取stdin,逐步拼接命令内容,直到遇到换行符或达到128字节上限:

#include <unistd.h>
#include <string.h>

#define BLOCK_SIZE 16
#define MAX_CMD_LEN 128

char cmd_buffer[MAX_CMD_LEN + 1] = {0};
int cmd_pos = 0;
char read_block[BLOCK_SIZE];
ssize_t bytes_read;

// 循环读取直到stdin耗尽
while ((bytes_read = read(STDIN_FILENO, read_block, BLOCK_SIZE)) > 0) {
    for (int i = 0; i < bytes_read; i++) {
        // 遇到换行符或命令长度超限,触发命令处理
        if (read_block[i] == '\n' || cmd_pos >= MAX_CMD_LEN) {
            if (cmd_pos > 0) {
                cmd_buffer[cmd_pos] = '\0';
                handle_command(cmd_buffer, &active_procs, max_procs);
                cmd_pos = 0;
                memset(cmd_buffer, 0, sizeof(cmd_buffer));
            }
            // 跳过换行符,继续读取下一条命令
            if (read_block[i] == '\n') continue;
            // 长度超限的剩余字符直接丢弃
        } else {
            cmd_buffer[cmd_pos++] = read_block[i];
        }
    }
}

// 处理最后一行无换行符的命令
if (cmd_pos > 0) {
    cmd_buffer[cmd_pos] = '\0';
    handle_command(cmd_buffer, &active_procs, max_procs);
}

2. 添加并发进程数控制

通过计数器跟踪活跃子进程,配合非阻塞waitpid回收已结束进程,动态控制并发数在1-8之间:

#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>

void handle_command(const char *cmd, int *active_procs, int max_procs) {
    // 等待直到活跃进程数低于上限
    while (*active_procs >= max_procs) {
        int status;
        pid_t finished_pid = waitpid(-1, &status, WNOHANG);
        if (finished_pid > 0) {
            (*active_procs)--;
        } else if (finished_pid == 0) {
            // 无进程结束,短暂休眠避免空转占用CPU
            usleep(10000);
        } else {
            // 无活跃子进程,退出等待循环
            break;
        }
    }

    pid_t pid = fork();
    if (pid == 0) {
        // 子进程解析命令并执行
        char *args[16]; // 足够容纳大部分命令参数
        parse_command(cmd, args);
        execvp(args[0], args);
        // exec失败才会走到这里
        perror("exec failed");
        exit(EXIT_FAILURE);
    } else if (pid > 0) {
        (*active_procs)++;
    } else {
        perror("fork failed");
    }
}

// 简易命令解析函数(可优化以支持带引号的参数)
void parse_command(const char *cmd, char **args) {
    int arg_idx = 0;
    char *token = strtok((char*)cmd, " ");
    while (token != NULL && arg_idx < 15) {
        args[arg_idx++] = token;
        token = strtok(NULL, " ");
    }
    args[arg_idx] = NULL;
}

3. 解析命令行参数(支持-p选项)

处理用户传入的-p参数,设置最大并发数(限制在1-8范围内):

#include <getopt.h>

int main(int argc, char *argv[]) {
    int max_procs = 8; // 默认最大8个并发
    int active_procs = 0;
    int opt;

    // 解析命令行参数
    while ((opt = getopt(argc, argv, "p:")) != -1) {
        switch (opt) {
            case 'p':
                max_procs = atoi(optarg);
                // 强制限制在1-8之间
                max_procs = (max_procs < 1) ? 1 : (max_procs > 8) ? 8 : max_procs;
                break;
            default:
                fprintf(stderr, "Usage: %s [-p max_procs(1-8)]\n", argv[0]);
                exit(EXIT_FAILURE);
        }
    }

    // 调用上述命令读取逻辑...
}

4. 收尾处理:等待所有子进程完成

当stdin读取完毕后,主进程需等待所有活跃子进程执行完毕再退出:

// stdin读取完成后,等待剩余子进程
while (*active_procs > 0) {
    int status;
    waitpid(-1, &status, 0);
    (*active_procs)--;
}

测试验证

使用提供的测试用例验证修复效果:

echo -e "ls -l\nls -l" | ./mycode -p 1
  • 预期输出:两次ls -l的结果,串行执行(因-p 1限制最大并发为1)
  • 批量测试:输入10条命令,观察系统进程列表(如ps aux | grep mycode),活跃子进程数始终不超过设置的上限

优化建议

  • 命令解析函数可优化,支持带空格的引号包裹参数(如echo "hello world")
  • 添加信号处理(如SIGCHLD),实时回收子进程,避免非阻塞waitpid的轮询开销
  • 增加命令长度超限的错误提示,而非直接丢弃

内容的提问来源于stack exchange,提问作者Joaquín Ayala Filardi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 02:45:42