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

C语言多进程管道通信:无法持续读取数据的问题求助

多进程管道通信循环退出问题的修复

问题概述

我编写了一个C语言程序,创建两个子进程分别实现字典序比较(lexcmp)和长度比较(lencmp)功能,通过4个管道实现父进程与子进程的通信。程序设计为循环持续处理用户输入,但当父进程尝试从管道读取子进程的计算结果时,会直接退出该循环。预期效果是向管道写入字符串后,读取子进程返回的结果并打印。

关键错误分析

  • 管道过早关闭:父进程在第一次处理请求时就关闭了管道的写端(如fd1[1])和读端(如fd2[0]),导致后续循环无法再使用这些管道通信。同时子进程会因为管道写端关闭收到EOF,退出自身循环,父进程后续读取时也会得到EOF,触发mygets返回NULL退出主循环。
  • 函数指针赋值逻辑错误:loopcmp中判断子进程类型时,if (strcmp(argv[1], "lexcmp"))的逻辑是字符串不相等时才赋值lexcmp函数,与预期完全相反,应使用!strcmp判断相等。
  • 输入处理残留问题:scanf("%d", &index)后未清理输入缓冲区的换行符,导致下一次mygets读取空行,直接触发退出逻辑。
  • 结果缓冲区不当修改:父进程中buf[4] = '\n';会破坏读取到的结果,应根据实际读取长度处理字符串终止。

修复后的代码

父进程代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/wait.h>
#define LINELEN (80)

char *mygets(char *buf, int len);
void clear_input_buffer();

int main()
{
    // fd[0] - 读端, fd[1] - 写端
    char *cmpstr[] = {"lexcmp", "lencmp"};
    int veclen = sizeof(cmpstr) / sizeof(char *);
    char str1[LINELEN + 1];
    char str2[LINELEN + 1];
    int index;
    int fd1[2], fd2[2], fd3[2], fd4[2];
    
    if (pipe(fd1) == -1 || pipe(fd2) == -1 || pipe(fd3) == -1 || pipe(fd4) == -1)
    {
        perror("pipe");
        return -2;
    }

    // 创建lexcmp子进程
    switch (fork())
    {
    case -1:
        perror("fork");
        return -2;
    case 0:
        close(fd3[1]); close(fd3[0]); close(fd4[1]); close(fd4[0]);
        close(fd1[1]); close(fd2[0]);
        
        dup2(fd1[0], STDIN_FILENO);
        close(fd1[0]);
        dup2(fd2[1], STDOUT_FILENO);
        close(fd2[1]);

        execlp("./loopcmp", "lexcmp", NULL);
        perror("execlp");
        return -2;
    default:
        break;
    }

    // 创建lencmp子进程
    switch (fork())
    {
    case -1:
        perror("fork");
        return -2;
    case 0:
        close(fd1[1]); close(fd1[0]); close(fd2[1]); close(fd2[0]);
        close(fd3[1]); close(fd4[0]);
        
        dup2(fd3[0], STDIN_FILENO);
        close(fd3[0]);
        dup2(fd4[1], STDOUT_FILENO);
        close(fd4[1]);

        execlp("./loopcmp", "lencmp", NULL);
        perror("execlp");
        return -2;
    default:
        break;
    }

    // 父进程关闭不需要的管道端
    close(fd1[0]);
    close(fd2[1]);
    close(fd3[0]);
    close(fd4[1]);

    while (1)
    {
        printf("Please enter first string:\n");
        if (mygets(str1, LINELEN) == NULL)
            break;
        printf("Please enter second string:\n");
        if (mygets(str2, LINELEN) == NULL)
            break;

        do
        {
            printf("Please choose:\n");
            for (int i = 0; i < veclen; i++)
                printf("%d - %s\n", i, cmpstr[i]);
            scanf("%d", &index);
            clear_input_buffer(); // 清理输入缓冲区的换行符
        } while ((index < 0) || (index >= 2));

        if (index == 0) // 处理lexcmp请求
        {
            printf("you are in lexcmp\n");
            if (write(fd1[1], str1, strlen(str1) + 1) == -1 ||
                write(fd1[1], str2, strlen(str2) + 1) == -1)
            {
                perror("write to lexcmp pipe");
                break;
            }

            char buf[80];
            ssize_t read_len = read(fd2[0], buf, sizeof(buf)-1);
            if (read_len <= 0)
            {
                perror("read from lexcmp child");
                break;
            }
            buf[read_len] = '\0';
            printf("%s(%s, %s) == %s", cmpstr[index], str1, str2, buf);
        }

        if (index == 1) // 处理lencmp请求
        {
            printf("you are in lencmp\n");
            if (write(fd3[1], str1, strlen(str1) + 1) == -1 ||
                write(fd3[1], str2, strlen(str2) + 1) == -1)
            {
                perror("write to lencmp pipe");
                break;
            }

            char buf[80];
            ssize_t read_len = read(fd4[0], buf, sizeof(buf)-1);
            if (read_len <= 0)
            {
                perror("read from lencmp child");
                break;
            }
            buf[read_len] = '\0';
            printf("%s(%s, %s) == %s", cmpstr[index], str1, str2, buf);
        }
    }

    // 退出前关闭管道,通知子进程结束
    close(fd1[1]);
    close(fd2[0]);
    close(fd3[1]);
    close(fd4[0]);
    // 等待子进程退出,避免僵尸进程
    wait(NULL);
    wait(NULL);
    return 0;
}

char *mygets(char *buf, int len)
{
    char *retval = fgets(buf, len, stdin);
    if (!retval)
        return NULL;
    
    buf[len-1] = '\0';
    size_t buf_len = strlen(buf);
    if (buf_len > 0 && buf[buf_len - 1] == '\n')
        buf[buf_len - 1] = '\0';
    else
    {
        // 清空超长输入的剩余字符
        int ch;
        while ((ch = getchar()) != '\n' && ch != EOF);
    }
    return retval;
}

void clear_input_buffer()
{
    int ch;
    while ((ch = getchar()) != '\n' && ch != EOF);
}

loopcmp 代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LINELEN (80)

int lencmp(const char *str1, const char *str2);
int lexcmp(const char *str1, const char *str2);
char *mygets(char *buf, int len);

int main(int argc, char *argv[])
{
    int(*cmpfunc)(const char *, const char *) = NULL;
    char str1[LINELEN + 1];
    char str2[LINELEN + 1];

    if (argc != 2)
        return -1;
    
    // 修正函数指针赋值逻辑
    if (!strcmp(argv[1], "lexcmp"))
        cmpfunc = lexcmp;
    else if (!strcmp(argv[1], "lencmp"))
        cmpfunc = lencmp;
    else
        return -1;

    while (1)
    {
        if (mygets(str1, LINELEN) == NULL)
            break;
        if (mygets(str2, LINELEN) == NULL)
            break;
        printf("%d\n", cmpfunc(str1, str2));
        fflush(stdout);
    }
    return 0;
}

int lencmp(const char *str1, const char *str2)
{
    int val = strlen(str1) - strlen(str2);
    if (val < 0)
        return 1;
    if (val > 0)
        return 2;
    return 0;
}

int lexcmp(const char *str1, const char *str2)
{
    int val = strcmp(str1, str2);
    if (val < 0)
        return 1;
    if (val > 0)
        return 2;
    return 0;
}

char *mygets(char *buf, int len)
{
    char *retval = fgets(buf, len, stdin);
    if (!retval)
        return NULL;
    
    buf[len-1] = '\0';
    size_t buf_len = strlen(buf);
    if (buf_len > 0 && buf[buf_len - 1] == '\n')
        buf[buf_len - 1] = '\0';
    else
    {
        int ch;
        while ((ch = getchar()) != '\n' && ch != EOF);
    }
    return retval;
}

编译与运行说明

  1. 先编译loopcmp生成可执行文件:gcc loopcmp.c -o loopcmp
  2. 编译父进程代码:gcc parent.c -o parent
  3. 运行父进程:./parent

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 06:37:05