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

多管道进程间输入输出流重定向异常问题

多管道模拟程序进程挂起问题排查与修复

你的管道模拟程序在处理ls -la | wc | wc -c这类多管道命令时出现挂起,核心原因是execvp所需的argv数组未以NULL指针终止,同时命令解析逻辑存在潜在缺陷。

问题根源分析

1. argv数组缺少NULL终止符

execvp函数要求传入的argv数组必须以NULL结尾,否则会读取超出数组边界的内存,引发未定义行为。你的parse_commands函数仅填充了命令参数,未在argv[link->argc]位置设置NULL,这会导致:

  • 中间进程(如wc)可能无法正常退出,使得管道写端始终处于打开状态;
  • 后续进程(如最后的wc -c)会一直等待管道输入,最终引发挂起。

2. 命令分割逻辑的硬编码缺陷

create_chain函数中处理管道符|时,硬编码跳过2个字符(tmp = tmp2 = tmp + 2;),假设|前后都有空格。若命令中出现|前后无空格的情况(如ls|wc),会导致命令截取错误,丢失部分字符。

修复方案

修复1:为argv数组添加NULL终止符

修改parse_commands函数,填充完参数后添加NULL终止符,同时优化空格处理:

#include <ctype.h> // 需要包含此头文件处理isspace

void parse_commands(char *command, chain_link_t *link) {
  char *tmp = command;
  char *tmp2 = command;
  link->argc = 0; // 从0开始计数更合理
  while ((tmp = strchr(tmp, ' '))) {
    // 跳过连续空格
    if (tmp == tmp2) {
      tmp2 = ++tmp;
      continue;
    }
    link->argv[link->argc] = strndup(tmp2, tmp - tmp2);
    link->argc++;
    tmp2 = ++tmp;
  }
  if (strlen(tmp2) > 0) {
    link->argv[link->argc] = strdup(tmp2);
    link->argc++;
  }
  // 必须添加NULL终止符,满足execvp要求
  link->argv[link->argc] = NULL;
  link->command = link->argc > 0 ? link->argv[0] : NULL;
}

修复2:完善管道符分割逻辑

修改create_chain函数,灵活处理|前后的空格,避免硬编码偏移:

void create_chain(char *command, chain_t *chain) {
  char *tmp = command;
  char *tmp2 = command;
  chain->chain_links_count = 0;
  while ((tmp = strchr(tmp, '|'))) {
    // 跳过命令段开头的空格
    while (tmp2 < tmp && isspace((unsigned char)*tmp2)) {
      tmp2++;
    }
    // 跳过命令段结尾的空格
    char *end = tmp - 1;
    while (end >= tmp2 && isspace((unsigned char)*end)) {
      end--;
    }
    // 仅当命令段非空时解析
    if (end >= tmp2) {
      parse_commands(strndup(tmp2, end - tmp2 + 1), &(chain->chain_links[chain->chain_links_count]));
      chain->chain_links_count++;
    }
    // 移动到|后面的第一个非空格字符
    tmp++;
    while (isspace((unsigned char)*tmp)) {
      tmp++;
    }
    tmp2 = tmp;
  }
  // 处理最后一段命令
  if (strlen(tmp2) > 0) {
    // 跳过开头空格
    while (isspace((unsigned char)*tmp2)) {
      tmp2++;
    }
    if (strlen(tmp2) > 0) {
      parse_commands(strdup(tmp2), &(chain->chain_links[chain->chain_links_count]));
      chain->chain_links_count++;
    }
  }
}

额外优化:修复内存泄漏

当前代码中strndup/strdup分配的内存未释放,长期运行会导致内存泄漏。添加内存释放函数并在主函数中调用:

void free_chain(chain_t *chain) {
  for (int i = 0; i < chain->chain_links_count; i++) {
    for (int j = 0; j < chain->chain_links[i].argc; j++) {
      free(chain->chain_links[i].argv[j]);
    }
  }
}

int main(int argc, char *argv[]) {
  if (argc < 2) {
    fprintf(stderr, "Usage: %s <command-chain>\n", argv[0]);
    return EXIT_FAILURE;
  }
  chain_t chain;
  create_chain(argv[1], &chain);
  run_chain(&chain);
  free_chain(&chain); // 释放分配的内存
  return 0;
}

验证

修复后执行ls -la | wc | wc -c,程序会正常结束,不会出现挂起现象,同时也能正确处理|前后无空格的命令格式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 08:35:56