gpt4 book ai didi

c - 为什么在clone创建的线程中不能正确调用malloc/free API?

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:38:47 25 4
gpt4 key购买 nike

为什么glibc的某些API(如函数malloc()realloc()free())不能被正确调用在系统调用克隆创建的线程中?

这里是我的代码,仅供测试:

int thread_func( void *arg )
{
void *ptr = malloc( 4096 );

printf( "tid=%d, ptr=%x\n", gettid(), ptr );
sleep(1);

if( ptr )
free( ptr );

return 0;
}


int main( int argc, char **argv )
{
int i, m;
void *stk;
int stksz = 1024 * 128;
int flag = CLONE_VM | CLONE _FILES | CLONE_FS | CLONE_SIGHAND;

for( i=m=0; i < 100; i++ )
{
stk = malloc( stksz );

if( !stk ) break;

if( clone( thread_func, stk+stksz, flags, NULL, NULL, NULL, NULL ) != -1 )
m++;
}

printf( "create %d thread\n", m );
sleep(10);

return 0;
}

测试结果:线程thread_func或主线程main会阻塞在malloc()free() 功能随机。或者有时会导致 malloc()free() 崩溃。

我认为可能是 malloc()free() 需要某些 TLS 数据来区分每个线程。

有谁知道原因,可以使用什么解决方案来解决这个问题?

最佳答案

I think may be malloc() and free() need certain TLS data to distinguish every thread.

Glibc 的 malloc()free() 不依赖于 TLS。它们使用互斥体来保护共享内存分配数据结构。为了减少对它们的争用,他们采用了一种策略,即使用独立的元数据和互斥体维护单独的内存分配区域。这记录在 their manual page 上.

在更正代码中的语法错误并虚拟化调用不存在的函数 gettid() 之后(请参阅问题的评论),我能够产生段错误,但没有堵塞。也许您将程序的 10 秒休眠导致的退出延迟与阻塞混淆了。

除了可能与您未公开的 gettid() 实现相关的任何问题外,您的程序还包含两个语义错误,每个错误都会产生未定义的行为:

  1. 正如我在评论中指出的那样,它传递了错误的子堆栈指针值。*

  2. 它在 thread_func() 中使用了错误的 printf() 指令来打印指针。指针值的指令是%p%x 用于 unsigned int 类型的参数。

在我也更正了这些错误之后,程序始终为我运行完成。修改后的代码:

int thread_func(void *arg) {
void *ptr = malloc(4096);

// printf( "tid=%d, ptr=%x\n", gettid(), ptr );
printf("tid=%d, ptr=%p\n", 1, ptr);
sleep(1);

if (ptr) {
free(ptr);
}

return 0;
}

int main(int argc, char **argv) {
int i, m;
char *stk; // Note: char * instead of void * to afford arithmetic
int stksz = 1024 * 128;
int flags = CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_SIGHAND;

for (i = m = 0; i < 100; i++) {
stk = malloc( stksz );

if( !stk ) break;

if (clone(thread_func, stk + stksz - 1, flags, NULL, NULL, NULL, NULL ) != -1) {
m++;
}
}

printf("create %d thread\n", m);
sleep(10);

return 0;
}

然而,即便如此,一切仍不尽如人意:我在程序输出中看到了各种异常情况,尤其是在开头附近。

底线是,与您的断言相反,您没有创建任何线程,至少不是在 C 库识别的意义上。您只是在创建具有类似于线程行为的进程。这对于某些目的可能就足够了,但您不能依赖系统将此类进程与线程一样对待。

在 Linux 上,系统和标准库将识别的真正线程是 POSIX 线程,通过 pthread_create() 启动。 (我在这里注意到,修改您的程序以使用 pthread_create() 而不是 clone() 为我解决了输出异常问题。)您可以将标志和参数添加到您的 clone() 调用使生成的进程足够像 pthreads 的 Linux 实现一样有效地相同,但是为什么您会做这样的事情而不是首先使用真正的 pthreads?


* 该程序还在 void * 上执行指针运算,这是 C 不允许的。但是,GCC 接受它作为扩展,并且由于您的代码无论如何都是深度特定于 Linux 的,所以我只放了这个注释。

关于c - 为什么在clone创建的线程中不能正确调用malloc/free API?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45282451/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com