c - 为什么子线程中不能释放父进程的malloced内存空间?
问题描述
在这个程序中,我克隆了一个带有标志的子线程:CLONE_VM | 克隆_SETTLS。子进程的堆栈空间分配在前面的父进程中。
在子线程中,我想通过直接更改rsp寄存器来切换它的堆栈。并且新的栈空间也提前分配在了father中。
错误是:
当我尝试释放旧堆栈空间时,出现“分段错误”,如下所示
以下四个操作将使bug消失:
- 不要切换堆栈空间
malloc
旧堆栈空间之后和克隆方法之前的新堆栈空间malloc
子线程中的新堆栈空间- 不要使用 TLS
C程序:
#define _GNU_SOURCE
#include <sched.h>
#include <unistd.h>
#include <asm/prctl.h>
#include <string.h>
#include <malloc.h>
extern void gogo_switch_new_free_old_and_jmp(void *new_stack, void *old_stack, void *new_pc);
int arch_prctl(int code, unsigned long *addr);
typedef struct {
void *stack_space;
void *stack_top;
} stack_struct;
typedef struct {
stack_struct stack;
stack_struct stack2;
} T;
void get_tls(void *tls_addr) {
arch_prctl(ARCH_GET_FS, tls_addr);
}
void task_done() {
T *t;
get_tls(&t);
void *old_stack_space = t->stack.stack_space;
t->stack.stack_space = t->stack2.stack_space;
t->stack.stack_top = t->stack2.stack_top;
gogo_switch_new_free_old_and_jmp(t->stack.stack_top, old_stack_space, NULL);
}
#define stack_size 128
int main() {
stack_struct stack2;
stack2.stack_space = malloc(stack_size);
stack2.stack_top = stack2.stack_space + stack_size;
T *t = malloc(sizeof(T));
t->stack.stack_space = malloc(stack_size);
t->stack.stack_top = t->stack.stack_space + stack_size;
t->stack2 = stack2;
clone((void *) task_done, t->stack.stack_top,
CLONE_VM |
CLONE_SETTLS,
NULL, NULL, t, NULL);
sleep(1000000000);
return 0;
}
汇编器:
#void gogo_switch_new_free_old_and_jmp(void *new_stack, void *old_stack, void *new_pc);
.text
.globl gogo_switch_new_free_old_and_jmp
.type gogo_switch_new_free_old_and_jmp, @function
gogo_switch_new_free_old_and_jmp:
# switch stack
movq %rdi,%rsp
pushq %rdx
# free old space
movq %rsi,%rdi
call free@PLT
#JMP
popq %rax
movq %rsp, %rdi
addq $8, %rdi
pushq %rax
ret
解决方案
关于“.... in tcache_init () at malloc.c:2981”错误消息。
出于性能考虑,GLIBC 的动态内存分配器使用 pthread 的 TLS 来维护每线程竞技场(即每线程缓存)上的指针,线程可以使用专用互斥锁在其中进行内存分配以增加并行度(否则如果线程系统地使用分配器的全局互斥锁,则线程的所有动态分配都将被序列化)。
因此,当您通过 clone() 创建的“自制”线程调用任何 malloc()/free() 函数时,动态内存分配器会查找存储在当前线程的 TLS 中的 pthread 信息,以便检索线程的竞技场上的指针。但是这些信息并不一致,因为当前线程不是基于 pthread 的。因此,崩溃。
您应该使用不依赖于任何 pthread 特定信息的 mmap() 系统调用,而不是 GLIBC 的动态内存分配器。
推荐阅读
- java - 无法在 Java 中的 GUI 上绘制和绘制各种形状
- android - 为什么注销ConnectivityManager.NetworkCallback后会出现“无法执行HTTP请求”的错误?
- javascript - 获取从链式 Observable 发出的最后两个值
- html - 我怎样才能使我的图像风格和响应?
- typescript - Typescript - 采用键和新值的方法
- javascript - 自动播放 HTML5 视频
- c# - 如何更新 HelixViewport3D 中的大量视觉效果
- gitlab - 列出启用了镜像的 GitLab 存储库
- django - 如何在 Postgres DateRange 字段上查询重叠日期
- python - 如何处理 UnicodeDecodeError:在 python smtplib 中