记一次pthread_key_create导致的__nptl_deallocate_tsd段错误

2023-10-14 06:48

本文主要是介绍记一次pthread_key_create导致的__nptl_deallocate_tsd段错误,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

__nptl_deallocate_tsd

rtoax
2021年5月25日

记一次由于pthread_key_create导致的__nptl_deallocate_tsd

  • 版本:glibc-2.17
  • 完整示例代码

1. 简介

#include <pthread.h>int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));

其中destructor为析构函数,它将在__nptl_deallocate_tsd中被调用。

2. Coredump:__nptl_deallocate_tsd

Thread 2 "a.out" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff77f0700 (LWP 140173)]
__GI___libc_free (mem=0x1) at malloc.c:2941
2941	  if (chunk_is_mmapped(p))                       /* release mmapped memory. */
(gdb) 
(gdb) bt
#0  __GI___libc_free (mem=0x1) at malloc.c:2941
#1  0x00007ffff7bc6c62 in __nptl_deallocate_tsd () at pthread_create.c:155
#2  0x00007ffff7bc6e73 in start_thread (arg=0x7ffff77f0700) at pthread_create.c:314
#3  0x00007ffff78ef88d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111

为什么会这样?为什么会执行析构函数。一步一步分析。

首先创建key,这里我们挂入malloc对应的free,因为我们将为每个线程的key使用malloc分配内存:

pthread_key_create(&key, free);

然后创建线程:

pthread_create(&thid1, NULL, thread1, NULL);

在thread1中申请内存并将其设置为该线程的TLS的key值:

int *key_va = malloc(sizeof(int)) ;
*key_va = 2;
pthread_setspecific(key, (void*)key_va);

当线程thread1执行结束后,主线程调用pthread_join回收线程,这时候析构函数将被执行:

pthread_join(thid1, NULL);

这里我在调研过程中,有些文章也讲到,由于key的malloc对应的析构函数被设置为NULL,导致内存泄漏。

当key使用完毕后进行删除:

pthread_key_delete(key);

示例代码见完整示例代码或者文末章节。

那么段错误如何产生呢?

我们还是使用free填充析构函数:

pthread_key_create(&key, free);

但是我将pthread_setspecific入参的value填写问静态变量值:

int key_va = 2;
pthread_setspecific(key, (void*)key_va);

没错,这时候在运行程序并用gdb调试,就会产生文章开头描述的段错误。

3. 不显示调用pthread_key_create的段错误

不显示调用pthread_key_create的程序也可能出错,比如说文末章节的实例sane.c.

不能对开源软件有过高的要求,有bug大家一起解决。

void* scan_thread(void *arg) {SANE_Status status;status = sane_init(NULL, NULL);assert(status == SANE_STATUS_GOOD);const SANE_Device** device_list = NULL;status = sane_get_devices(&device_list, false);assert(status == SANE_STATUS_GOOD);int i;for(i = 0; device_list[i] != NULL; ++i){printf("%s\n", device_list[i]->name);}sane_exit();
}

gdb运行程序,并设置断点:

(gdb) b pthread_key_create
Function "pthread_key_create" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (pthread_key_create) pending.

到达断点:

Thread 2 "test.c.out" hit Breakpoint 1, __GI___pthread_key_create (key=key@entry=0x7ffff75c20c0 <key>, destr=destr@entry=0x7ffff73c01f0 <free_key_mem>) at pthread_key_create.c:26
26	{

继续执行,产生段错误:

(gdb) c
Continuing.Thread 2 "test.c.out" hit Breakpoint 1, __GI___pthread_key_create (key=0x7fffe4eb6dc8, destr=0x7fffe4c77600 <cups_globals_free>) at pthread_key_create.c:26
26	{
(gdb) c
Continuing.Thread 2 "test.c.out" received signal SIGSEGV, Segmentation fault.
0x00007fffe4c77600 in ?? ()
(gdb) bt
#0  0x00007fffe4c77600 in ?? ()
#1  0x00007ffff7998c62 in __nptl_deallocate_tsd () at pthread_create.c:155
#2  0x00007ffff7998e73 in start_thread (arg=0x7ffff4168700) at pthread_create.c:314
#3  0x00007ffff76c188d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
(gdb) 

这种问题如何解决呢?

  • 去除sane_exit();,但是可能面临内存泄漏的危险;
  • TODO:走读sane-backends源码,提交patch;

4. 示例代码

4.1. main.c

#include <pthread.h>
#include <stdio.h>
#include <malloc.h>pthread_key_t key;
pthread_t thid1;
pthread_t thid2;#ifndef MALLOC_KEY
#define MALLOC_KEY  0
#endifvoid* thread2(void* arg)
{printf("thread:%lu is running\n", pthread_self());
#if MALLOC_KEY    int *key_va = malloc(sizeof(int)) ;*key_va = 2;
#elseint key_va = 2;
#endifpthread_setspecific(key, (void*)key_va);printf("thread:%lu return %d\n", pthread_self(), (int)pthread_getspecific(key));
}void* thread1(void* arg)
{printf("thread:%lu is running\n", pthread_self());#if MALLOC_KEY    int *key_va = malloc(sizeof(int)) ;*key_va = 1;
#elseint key_va = 1;
#endifpthread_setspecific(key, (void*)key_va);pthread_create(&thid2, NULL, thread2, NULL);printf("thread:%lu return %d\n", pthread_self(), (int)pthread_getspecific(key));
}int main()
{printf("main thread:%lu is running\n", pthread_self());//如果 pthread_setspecific 传入的是局部变量,//并且 pthread_key_create 传入了析构函数,//那么将产生如下段错误//#0  __GI___libc_free (mem=0x5) at malloc.c:2941//#1  0x00007feb4c550c62 in __nptl_deallocate_tsd () at pthread_create.c:155//#2  0x00007feb4c550e73 in start_thread (arg=0x7feb4c17a700) at pthread_create.c:314//#3  0x00007feb4c27988d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111pthread_key_create(&key, free); //这里会段错误 pthread_create(&thid1, NULL, thread1, NULL);pthread_join(thid1, NULL);pthread_join(thid2, NULL);#if MALLOC_KEY    int *key_va = malloc(sizeof(int)) ;*key_va = 2;
#elseint key_va = 2;
#endifpthread_setspecific(key, (void*)key_va);printf("thread:%lu return %d\n", pthread_self(), (int)pthread_getspecific(key));pthread_key_delete(key);printf("main thread exit\n");return 0;
}

4.2. sane.c

此示例参见https://bugzilla.redhat.com/show_bug.cgi?id=1065695。

#include <assert.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>
#include <malloc.h>
#include <sane/sane.h>#define PTHREAD_STACK_MIN	16384void* scan_thread(void *arg) {SANE_Status status;status = sane_init(NULL, NULL);assert(status == SANE_STATUS_GOOD);const SANE_Device** device_list = NULL;status = sane_get_devices(&device_list, false);assert(status == SANE_STATUS_GOOD);int i;for(i = 0; device_list[i] != NULL; ++i){printf("%s\n", device_list[i]->name);}sane_exit();
}int main()
{    pthread_t t;pthread_attr_t attr;void *stackAddr = NULL;int paseSize = getpagesize();size_t stacksize = paseSize*4;pthread_attr_init(&attr);posix_memalign(&stackAddr, paseSize, stacksize);pthread_attr_setstack(&attr, stackAddr, stacksize);pthread_create(&t, NULL, scan_thread, NULL);pthread_join(t, NULL);return 0;
}

这篇关于记一次pthread_key_create导致的__nptl_deallocate_tsd段错误的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/208831

相关文章

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

git ssh key相关

step1、进入.ssh文件夹   (windows下 下载git客户端)   cd ~/.ssh(windows mkdir ~/.ssh) step2、配置name和email git config --global user.name "你的名称"git config --global user.email "你的邮箱" step3、生成key ssh-keygen

STM32 ADC+DMA导致写FLASH失败

最近用STM32G070系列的ADC+DMA采样时,遇到了一些小坑记录一下; 一、ADC+DMA采样时进入死循环; 解决方法:ADC-dma死循环问题_stm32 adc dma死机-CSDN博客 将ADC的DMA中断调整为最高,且增大ADCHAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, ADC_Buffer_Size); 的ADC_Bu

【经验交流】修复系统事件查看器启动不能时出现的4201错误

方法1,取得『%SystemRoot%\LogFiles』文件夹和『%SystemRoot%\System32\wbem』文件夹的权限(包括这两个文件夹的所有子文件夹的权限),简单点说,就是使你当前的帐户拥有这两个文件夹以及它们的子文件夹的绝对控制权限。这是最简单的方法,不少老外说,这样一弄,倒是解决了问题。不过对我的系统,没用; 方法2,以不带网络的安全模式启动,运行命令行,输入“ne

DBeaver 连接 MySQL 报错 Public Key Retrieval is not allowed

DBeaver 连接 MySQL 报错 Public Key Retrieval is not allowed 文章目录 DBeaver 连接 MySQL 报错 Public Key Retrieval is not allowed问题解决办法 问题 使用 DBeaver 连接 MySQL 数据库的时候, 一直报错下面的错误 Public Key Retrieval is

DAY16:什么是慢查询,导致的原因,优化方法 | undo log、redo log、binlog的用处 | MySQL有哪些锁

目录 什么是慢查询,导致的原因,优化方法 undo log、redo log、binlog的用处  MySQL有哪些锁   什么是慢查询,导致的原因,优化方法 数据库查询的执行时间超过指定的超时时间时,就被称为慢查询。 导致的原因: 查询语句比较复杂:查询涉及多个表,包含复杂的连接和子查询,可能导致执行时间较长。查询数据量大:当查询的数据量庞大时,即使查询本身并不复杂,也可能导致

(function() {})();只执行一次

测试例子: var xx = (function() {     (function() { alert(9) })(); alert(10)     return "yyyy";  })(); 调用: alert(xx); 在调用的时候,你会发现只弹出"yyyy"信息,并不见弹出"10"的信息!这也就是说,这个匿名函数只在立即调用的时候执行一次,这时它已经赋予了给xx变量,也就是只是

SQL2005 性能监视器计数器错误解决方法

【系统环境】 windows 2003 +sql2005 【问题状况】 用户在不正当删除SQL2005后会造成SQL2005 性能监视器计数器错误,如下图 【解决办法】 1、在 “开始” --> “运行”中输入 regedit,开启注册表编辑器,定位到 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVer

flume系列之:记录一次flume agent进程被异常oom kill -9的原因定位

flume系列之:记录一次flume agent进程被异常oom kill -9的原因定位 一、背景二、定位问题三、解决方法 一、背景 flume系列之:定位flume没有关闭某个时间点生成的tmp文件的原因,并制定解决方案在博主上面这篇文章的基础上,在机器内存、cpu资源、flume agent资源都足够的情况下,flume agent又出现了tmp文件无法关闭的情况 二、

71-java 导致线程上下文切换的原因

Java中导致线程上下文切换的原因通常包括: 线程时间片用完:当前线程的时间片用完,操作系统将其暂停,并切换到另一个线程。 线程被优先级更高的线程抢占:操作系统根据线程优先级决定运行哪个线程。 线程进入等待状态:如线程执行了sleep(),wait(),join()等操作,使线程进入等待状态或阻塞状态,释放CPU。 线程占用CPU时间过长:如果线程执行了大量的I/O操作,而不是CPU计算