如何实现维持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
相关产品推荐
相关产品推荐

