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

如何解决clone()与pipe结合时write()出现的无效文件描述符错误?

问题:clone创建的线程调用write()时出现bad file descriptor错误

我已尝试许久,但始终不清楚为何调用write()时会出现bad file descriptor错误,猜测可能与clone()函数及管道参数传递有关。我已查阅资料、尝试信号处理,但均未解决问题。以下是我的代码:

#define _GNU_SOURCE
#include <stdlib.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <sched.h>
#include <stdio.h>
#include <unistd.h>

// 64kB stack
#define FIBER_STACK (1024*64)

int threadFunction(void* argument)
{
    int pipefd = *((int*)argument);
  
    char* info = "INFO!\n";
    if (write(pipefd, info, 10) == -1) {
      perror("write");
      exit(EXIT_FAILURE);
    }
    
    printf("Child thread exiting\n");
    return 0;
}

int main()
{
    void* stack;
    pid_t pid;
    int pipefd[2];

    if (pipe(pipefd) == -1)
    {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    // Allocate the stack
    stack = malloc(FIBER_STACK);
    if (stack == 0)
    {
        perror("malloc: could not allocate stack");
        exit(EXIT_FAILURE);
    }


printf("Creating child thread\n");

    // Call the clone system call to create the child thread
    pid = clone(&threadFunction, (char*)stack + FIBER_STACK,
                SIGCHLD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM,(void*)&pipefd[1]); 
    if (pid == -1)
    {
        perror("clone");
        exit(EXIT_FAILURE);
    }

    close(pipefd[1]);

    char buffer[1024];
  
    
    /* read pipe */
    read(pipefd[0], buffer, 5); 
    printf("%s \n", buffer); 
     

    pid = waitpid(pid, 0, 0);
    if (pid == -1)
    {
        perror("waitpid");
        exit(EXIT_FAILURE);
    }

    free(stack);
    printf("Child thread returned and stack freed.\n");

    return 0;
}

理论上代码应能正常执行write(),但我始终遇到该错误,且我对管道概念完全陌生,希望得到解决建议。


问题根源及修复方案

1. 主线程过早关闭管道写端

你使用了CLONE_FILES标志,这意味着子线程和主线程共享同一个文件描述符表。主线程在调用clone()后立刻执行close(pipefd[1]),此时子线程还未开始执行write()操作,导致写端文件描述符在共享表中被关闭,子线程再调用write()自然触发bad file descriptor错误。

修复方法:将close(pipefd[1])的调用时机延后,比如等主线程完成read()之后再关闭。

2. 字符串写入长度错误

你写入的字符串"INFO!\n"实际长度是6字节(包含换行符),但write()中指定了10字节,会导致写入多余的垃圾数据。建议使用strlen(info)获取正确长度。

3. 读取后未添加字符串终止符

read()不会自动在缓冲区末尾添加'\0',直接用printf("%s", buffer)会导致读取内容后出现乱码,需要手动在读取到的字节数后添加终止符。

修复后的完整代码

#define _GNU_SOURCE
#include <stdlib.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <sched.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h> // 新增strlen头文件

// 64kB stack
#define FIBER_STACK (1024*64)

int threadFunction(void* argument)
{
    int pipefd = *((int*)argument);
  
    char* info = "INFO!\n";
    // 使用strlen获取正确长度
    if (write(pipefd, info, strlen(info)) == -1) {
      perror("write");
      exit(EXIT_FAILURE);
    }
    
    printf("Child thread exiting\n");
    return 0;
}

int main()
{
    void* stack;
    pid_t pid;
    int pipefd[2];

    if (pipe(pipefd) == -1)
    {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    // Allocate the stack
    stack = malloc(FIBER_STACK);
    if (stack == 0)
    {
        perror("malloc: could not allocate stack");
        exit(EXIT_FAILURE);
    }

    printf("Creating child thread\n");

    // Call the clone system call to create the child thread
    pid = clone(&threadFunction, (char*)stack + FIBER_STACK,
                SIGCHLD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM,(void*)&pipefd[1]); 
    if (pid == -1)
    {
        perror("clone");
        exit(EXIT_FAILURE);
    }

    char buffer[1024];
    ssize_t read_len;
    
    /* read pipe */
    read_len = read(pipefd[0], buffer, sizeof(buffer)-1); // 留一个字节给终止符
    if (read_len > 0) {
        buffer[read_len] = '\0'; // 添加字符串终止符
        printf("%s", buffer); 
    }

    // 读完之后再关闭写端
    close(pipefd[1]);

    pid = waitpid(pid, 0, 0);
    if (pid == -1)
    {
        perror("waitpid");
        exit(EXIT_FAILURE);
    }

    close(pipefd[0]); // 关闭读端
    free(stack);
    printf("Child thread returned and stack freed.\n");

    return 0;
}

管道基础补充

  • 管道是单向通信的,pipefd[0]为读端,pipefd[1]为写端。
  • 当所有写端被关闭时,读端的read()会返回0,表示无更多数据。
  • 当所有读端被关闭时,写端的write()会触发SIGPIPE信号(默认终止进程),同时返回EPIPE错误。
  • 使用CLONE_FILES时,父子线程共享文件描述符,对文件描述符的操作(如close)会影响所有共享的线程/进程。

内容的提问来源于stack exchange,提问作者Guilherme Garcia Lima

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 20:35:11