记录一次数组越界导致的线程死锁问题

1. 问题描述

在一次代码调试的过程中,遇到过一个问题,线程在调用pthread_cancel时,提示未找到目标线程,然后程序阻塞在了与目标线程相关的条件变量的释放上,造成了死锁的现象。

2. 问题复现

#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <cstring>#define BUF_SIZE 8192unsigned char g_buf[BUF_SIZE] = {0xff};class Worker {
public:Worker() {m_run = false;pthread_mutex_init(&m_mtx, NULL);pthread_cond_init(&m_cond, NULL);pthread_create(&m_tid, 0, writeThread, (void*)this);std::cout << "create thread " << std::hex << std::showbase << m_tid << std::endl;}~Worker() {pthread_t id = pthread_self();std::cout << "cur thread " << id << std::endl;m_run = false;int ret = pthread_cancel(m_tid);std::cout << "ret:" << ret << std::endl;// pthread_join(m_tid, NULL);// Destroy the signalsstd::cout << "finished 1" << std::endl;pthread_cond_destroy(&m_cond);std::cout << "finished 2" << std::endl;pthread_mutex_destroy(&m_mtx);std::cout << "finished 3" << std::endl;}static void* writeThread(void* obj) {((Worker*)obj)->writeWork();return 0;}void writeWork() {char buf[4096];while(true) {sched_yield();if(!m_run) {memcpy(buf, g_buf, BUF_SIZE);pthread_mutex_lock(&m_mtx);while (!m_run){pthread_cond_wait(&m_cond, &m_mtx);}pthread_mutex_unlock(&m_mtx);// write buf}}
private:bool m_run{false};pthread_cond_t m_cond;pthread_mutex_t m_mtx;pthread_t m_tid;
};int main() {Worker work;// avoid main thread exit before work threadsleep(3);return 0;
}

程序卡死,执行结果如下:
在这里插入图片描述
打印堆栈信息如下:
在这里插入图片描述
可以看到主线程阻塞在析构函数中销毁条件变量m_cond的位置,而工作线程则阻塞在pthread_cond_wait处等待条件变量的满足。因此看到的现象就如第一张图那样,主线程卡死,无法完成资源释放。
从pthread_cancel的返回值可以看出,工作线程并没有按照预期那样在取消点(pthread_cond_wait)处被成功释放。返回值3意味着没有找到要取消的线程。
表面原因我们找到了,但是根本原因还没有找出来,也就是为什么会造成这种结果,正常情况下pthread_cancel是不会失败的。猜测一种可能的原因:线程的栈空间被其他数据覆盖,线程相关信息找不到了

3. 问题分析与定位

在这里插入图片描述
从上图就可以看出,0x7ffff6e83700是工作线程,该值应该是线程控制信息的地址,打印地址内容发现结果为0,这就有问题了,正常情况下应该是非0值,参考当前线程地址的打印信息就可以对比出。所以对该线程的任何操作(pthread_cancel、pthread_join)都不会正常执行。
知道了原因,那就来找找是什么地方导致了这种结果呢?猜测是不是赋值操作时,数组越界了,并且超出了足够大的范围。那么什么操作会有这种结果呢:

  1. 循环赋值,且循环次数足够大;
  2. 通过memcpy、memset、memmove进行赋值,且size足够大。

根据以上猜测,我们再来看看代码,发现在调用memcpy时,局部变量buf的大小只有4096,而传入的size值确是8192,远远超出了buf的大小。将size的大小改为5000后,再次执行程序。
在这里插入图片描述
可以看到程序正常结束,但是5000依然超出了实际内存大小,这说明要复现上述问题,需要的size值得足够大才行。

实际上通过检测工具可以更方便的check数组越界的情况,此处使用gcc自带的工具sanitizers,只需要在编译选项中增加-g -fsanitize=address选项即可,增加调试信息可以更容易定位代码位置。
在这里插入图片描述
可以看到该工具定位到可能越界位置在45行代码,也就是调用memcpy的位置。

4. 总结

  • pthread_cancel并不一定保证线程被释放,它只是给目标线程发送了一个信号,而只有当目标线程到达一个取消点(系统调用)时,目标线程才会退出。
  • 如果需要等待线程退出,应该调用pthread_join来保证这一点。上述代码中,如果在cancel之后调用该函数,程序会出现段错误,因为在使用线程相关的信息时,拿到的是一个空值。
  • 在对字符数组进行赋值时,c语言提供的一些函数并不安全,很多时候越界却不自知。所以我们需要有一些检测工具来帮助我们避免这些情况的发生,常用的工具如sanitizers、valgrind等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/776679.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微机原理与接口技术-精选复习题

1、ADC0809的START和EOC引脚的功能是什么&#xff1f;在查询方式和中断方式中&#xff0c;EOC引脚分别如何处理&#xff1f; START是转换启动信号&#xff0c;EOC信号是转换结束信号。在查询方式中&#xff0c;EOC可作为状态信息输入至CPU以供查询&#xff1b;在中断方式中EOC…

python--初学函数

函数&#xff08;function&#xff09;&#xff1a; 什么是函数&#xff1f; 具有名称的&#xff0c;是为了解决某一问题&#xff0c;功能代码的集合&#xff0c;叫做函数 python中函数如何定义&#xff1a;def>define function定义函数 def function_name([args临时变量…

JavaScript 常用方法(1):JS日期格式化函数、JS实现并列排序、JS实现数字每三位添加逗号、JS 实现根据日期和时间进行排序

1、JS日期格式化函数 JS日期格式化转换方法 /*** description 格式化时间* param fmt 格式 如&#xff1a;yyyy-MM-dd、yyyy-MM-dd HH:mm:ss、yyyy年MM月dd日 W HH:mm:ss等* param {String} date 时间戳* returns {string|null}* 对 Date 的扩展&#xff0c;将 Date 转化为指…

如何在CentOS使用Docker搭建MinIO容器并实现无公网ip远程访问本地服务

文章目录 前言1. Docker 部署MinIO2. 本地访问MinIO3. Linux安装Cpolar4. 配置MinIO公网地址5. 远程访问MinIO管理界面6. 固定MinIO公网地址 前言 MinIO是一个开源的对象存储服务器&#xff0c;可以在各种环境中运行&#xff0c;例如本地、Docker容器、Kubernetes集群等。它兼…

拥有超小型领先工艺射频微波电子元器件厂商兆讯授权世强硬创代理

射频前端芯片在模拟芯片中&#xff0c;属于进入门槛较高、设计难度较大的细分领域&#xff0c;由于国内射频前端芯片行业起步较晚&#xff0c;其市场份额主要被外企所占据&#xff0c;而在国产化浪潮的推动下&#xff0c;上游厂商的射频前端产品及技术逐渐具备领先的竞争优势。…

【C++初阶】之类和对象(下)

【C初阶】之类和对象&#xff08;下&#xff09; ✍ 再谈构造函数&#x1f3c4; 初始化列表的引入&#x1f498; 初始化列表的语法&#x1f498; 初始化列表初始化元素的顺序 &#x1f3c4; explicit关键字 ✍ Static成员&#x1f3c4; C语言中的静态变量&#x1f3c4; C中的静…

Leveled mode of TFHE

参考文献&#xff1a; [CGGI16] Chillotti I, Gama N, Georgieva M, et al. Faster fully homomorphic encryption: Bootstrapping in less than 0.1 seconds[C]//Advances in Cryptology–ASIACRYPT 2016: 22nd International Conference on the Theory and Application of C…

蓝桥杯嵌入式学习笔记(6):IIC程序设计

目录 前言 1. IIC基本原理 2. 电路原理 3. 代码编程 3.1 预备工作 3.2 AT24C02写读功能编写 3.2.1 AT24C02写操作实现 3.2.2 AT24C02读操作实现 3.3 MCP4017写读功能编写 3.3.1 MCP4017写操作实现 3.3.2 MCP4017读操作实现 3.4 main.c编写 3.4.1 头文件引用 3.4.…

蓝桥杯每日一题(floyd算法)

4074 铁路与公路 如果两个城市之间有铁路t11&#xff0c;公路就会t2>1,没铁路的时候t1>1,公路t21。也就是公路铁路永远都不会相等。我们只需要计算通过公路和铁路从1到n最大的那个即可。 floyd是直接在数组上更新距离。不需要新建dis数组。另外一定要记得把邻接矩阵初始…

【数据结构】链表习题之环形链表的约瑟夫问题

&#x1f451;个人主页&#xff1a;啊Q闻 &#x1f387;收录专栏&#xff1a;《数据结构》 &#x1f389;道阻且长&#xff0c;行则将至 前言 今天这道题目时牛客上的题目&#xff0c;名为环形链表的约瑟夫问题&#xff0c;很有趣的的一道题目 环形链表的约瑟…

SpringBoot在线电影订票系统实战开发教程及源码之手把手教你做一个在线电影订票系统(带参考论文)

今天发布的是一款基于SpringBoot脚手架开发的在线电影订票系统实战开发教程和完整源码&#xff0c;这里强调一下&#xff1a;本系统基于springboot脚手架开发&#xff0c;如果还没学习脚手架的话先去学习脚手架&#xff0c;不然学不懂这个电影订票系统哦&#xff0c;重要的事说…

2024河北石家庄矿业矿山展览会|河北智慧矿山展会|河北矿博会

2024中国&#xff08;石家庄&#xff09;国际矿业博览会      时间&#xff1a;2024年7月4-6日 地点&#xff1a;石家庄国际会展中心.正定      随着全球经济的持续增长和矿产资源需求的不断攀升&#xff0c;矿业行业正迎来前所未有的发展机遇。作为矿业领域的盛会&…

蓝桥OJ3510 冶炼金属(暴力+二分)

冶炼金属 学习了b站Turing_Sheep的思路 一、暴力模拟 思路&#xff1a; b[i] a[i] / v b[1] a[1] / v b[2] a[2] / v .... b[n] a[n] / v 以上列举中v要满足所有的记录&#xff0c;但凡一个记录不满足&#xff0c;v就不满足题意。 从小到大列举v,设置v最大为1e6 设置一个标…

如何用Flask中的Blueprints构建大型Web应用

本文分享自华为云社区《构建大型Web应用Flask中的Blueprints指南》&#xff0c;作者&#xff1a; 柠檬味拥抱。 什么是Blueprints&#xff1f; 什么是Blueprints&#xff1f; Blueprints是Flask中的一种模式&#xff0c;用于将应用程序分解为可重用的模块。每个蓝图实际上是…

时序数据库IoTDB:功能详解与行业应用

一文读懂时序数据库 IoTDB。 01 为什么需要时序数据库 解释时序数据库前&#xff0c;先了解一下何谓时序数据。 时序数据&#xff0c;也称为时间序列数据&#xff0c;是指按时间顺序记录的同一统计指标的数据集合。这类数据的来源主要是能源、工程、交通等工业物联网强关联行业…

蓝牙耳机哪个品牌的好?2024年精选硬核机型推荐

​随着时代的进步和潮流的演进&#xff0c;人们对蓝牙耳机的需求已不再局限于音质&#xff0c;舒适度也成为了关键考量。下面&#xff0c;我将为你推荐五款既舒适又性能出色的蓝牙耳机。 一、如何挑选蓝牙耳机&#xff1f;&#xff08;重点码住&#xff09; 1.选择知名大品牌&…

1.4.1 着色器

着色器&#xff08;Shader&#xff09;是运行在GPU上的小程序&#xff0c;这些小程序为图形渲染管线的某个特定部分而运行&#xff0c;从基本意义上来说&#xff0c;着色器只是一种把输入转化为输出的程序。 一、着色器类QOpenGLShaderProgram QOpenGLShaderProgram是Qt中对着…

C++ 迭代器与反向迭代器

目录 一&#xff0c;什么是迭代器 1&#xff0c;定义 2&#xff0c;迭代器的设计思维 3&#xff0c;迭代器种类 二&#xff0c;迭代器与容器 1&#xff0c;容器中的迭代器 2&#xff0c;迭代器失效问题 三&#xff0c;迭代器的类型萃取&#xff08;traits&#xff09; …

sdwan本地组网分析

随着数字化转型的深入发展&#xff0c;企业对网络架构的要求也不断提高。SDWAN&#xff08;软件定义广域网&#xff09;作为一种创新的网络技术&#xff0c;正在逐渐受到企业的关注和采用。SDWAN本地组网技术可以帮助企业快速搭建高效稳定的企业网络架构&#xff0c;提升企业的…

Linux:基础IO

回顾C文件接口 stdin & stdout & stderr C 默认会打开三个输入输出流&#xff0c;分别是 stdin, stdout, stderr 仔细观察发现&#xff0c;这三个流的类型都是 FILE*, fopen 返回值类型&#xff0c;文件指针 系统文件I/O 接口介绍 open man open #include <sy…