如何解决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
相关产品推荐
相关产品推荐

