linux下错误使用pthread_mutex_lock导致程序奔溃问题分析

2023-11-16

在进行程序开发过程中,错误使用了pthread_mutex_lock导致程序概率性的奔溃,奔溃时报如下错误:


问题分析:

本文分析在Linux应用程序中错误使用pthread_mutex锁时会概率性触发SIG_ABRT信号而导致程序崩溃(库打印输出 :Assertion `mutex->__data.__owner == 0' failed)的原因。

首先给出出错的示例程序:

#include <stdio.h>    
#include <unistd.h>    
#include "pthread.h"    
    
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;      
    
void * process(void * arg)    
{    
    fprintf(stderr, "Starting process %s\n", (char *) arg);    
  
    while (1) {  
        /* 加锁等待某些资源 */  
        pthread_mutex_lock(&lock);  
        fprintf(stderr, "Process %s lock mutex\n", (char *) arg);    
        /* 加锁成功表示资源就绪 */  
        usleep(1000);  
        /* do something */  
    }  
  
    return NULL;    
}    
  
int main(void)    
{    
    pthread_t th_a, th_b;    
    int ret = 0;    
    
    ret = pthread_create(&th_a, NULL, process, "a");    
    if (ret != 0) fprintf(stderr, "create a failed %d\n", ret);    
    
    ret = pthread_create(&th_b, NULL, process, "b");    
    if (ret != 0) fprintf(stderr, "create b failed %d\n", ret);    
    
    while (1) {  
        /* 等待并检测某些资源就绪 */  
        /* something */  
        /* 解锁告知线程资源就绪 */  
        pthread_mutex_unlock(&lock);   
        fprintf(stderr, "Main Process unlock mutex\n");    
    }  
  
    return 0;    
}    


本示例程序中,main函数首先创建两个线程,然后主线程等待某些资源就绪(伪代码,程序中未体现),待就绪后解锁mutex lock以告知子线程可以执行相应的处理(在解锁后打印输出解锁成功),不断循环;创建出的两个线程均调用process函数,该函数会尝试加锁mutex lock,加锁成功则表示资源就绪可以处理(打印输出加锁成功),否则在锁上等待,亦往复循环。本程序中对mutex锁的用法特殊,并不对临界资源进行保护,而是作为线程间”生产---消费“同步功能的一个简化示例,加锁以等待资源就绪,解锁以通知资源就绪,加锁和解锁的操作分别在不同的线程中执行。
运行该程序后不到10s时间程序就会出错退出,并且触发SIG_ABRT信号,终端打印输出如下:
......
Main Process unlock mutex
Main Process unlock mutex
Main Process unlock mutex
Process b lock mutex
Process a lock mutex
Main Process unlock mutex
Main Process unlock mutex
Main Process unlock mutex
Main Process unlock mutex
Main Process unlock mutex
Main Process unlock mutex
Main Process unlock mutex
Process b lock mutex
pthread_test: pthread_mutex_lock.c:62: __pthread_mutex_lock: Assertion `mutex->__data.__owner == 0' failed.
Aborted
程序在Glibc库中的pthread_mutex_lock.c的第62行__pthread_mutex_unlock()函数中出错,程序ABRT退出。
下面先来分析对应的源码,首先是加锁流程:


加锁函数源码:

int  
__pthread_mutex_lock (mutex)  
     pthread_mutex_t *mutex;  
{  
  assert (sizeof (mutex->__size) >= sizeof (mutex->__data));  
  
  unsigned int type = PTHREAD_MUTEX_TYPE (mutex);  
  if (__builtin_expect (type & ~PTHREAD_MUTEX_KIND_MASK_NP, 0))  
    return __pthread_mutex_lock_full (mutex);  
  
  pid_t id = THREAD_GETMEM (THREAD_SELF, tid);  
  
  if (__builtin_expect (type, PTHREAD_MUTEX_TIMED_NP)  
      == PTHREAD_MUTEX_TIMED_NP)                                //1---判断锁类型  
    {  
    simple:  
      /* Normal mutex.  */  
      LLL_MUTEX_LOCK (mutex);                                   //2---加锁(原子操作)  
      assert (mutex->__data.__owner == 0);                       //3---Owner判断  
    }  
      
  ...  
  
  /* Record the ownership.  */  
  mutex->__data.__owner = id;                                    //4---Owner赋值  
#ifndef NO_INCR  
  ++mutex->__data.__nusers;  
#endif  
  
  return 0;  
}  


加锁函数的主要4步操作已经列出,首先会判断锁的类型,这里仅对PTHREAD_MUTEX_TIMED_NP类型的锁做出分析,该该类型的锁为默认的锁类型,当一个线程加锁后其余请求锁的线程会排入一个等待队列,并在锁解锁后按优先级获得锁。然后程序调用LLT_MUTEX_LOCK()宏执行底层加锁动作,这个加锁流程是原子的且不同的架构实现并不相同,然后会判断是否已经有线程获取了该锁(因为PTHREAD_MUTEX_TIMED_NP类型的锁是不允许嵌套加锁的),若已经有线程获取了锁则出错退出(示例程序中就是在此出错的),在函数的最后会把当前获得锁的线程号赋给__owner字段(线程与锁绑定)就结束了,此时当前线程进入临界区,其他对锁请求的线程将阻塞。下面来看一下解锁流程:


解锁函数源码:

int  
internal_function attribute_hidden  
__pthread_mutex_unlock_usercnt (mutex, decr)  
     pthread_mutex_t *mutex;  
     int decr;  
{  
  int type = PTHREAD_MUTEX_TYPE (mutex);  
  if (__builtin_expect (type & ~PTHREAD_MUTEX_KIND_MASK_NP, 0))  
    return __pthread_mutex_unlock_full (mutex, decr);  
  
  if (__builtin_expect (type, PTHREAD_MUTEX_TIMED_NP)               //1---判断锁类型  
      == PTHREAD_MUTEX_TIMED_NP)  
    {  
      /* Always reset the owner field.  */  
    normal:  
      mutex->__data.__owner = 0;                     //2---Owner解除  
      if (decr)  
    /* One less user.  */  
    --mutex->__data.__nusers;  
  
      /* Unlock.  */  
      lll_unlock (mutex->__data.__lock, PTHREAD_MUTEX_PSHARED (mutex));      //3---原子解锁  
      return 0;  
    }  
      
    ...  
}  


解锁函数的3步主要操作如上,首先依旧是判断锁类型,然后解除锁和线程的绑定关系,最后就调用lll_unlock()函数原子的解锁,此时若有加锁线程需要获取锁,相应线程会从
LLT_MUTEX_LOCK()函数返回继续执行。
以上就是调用pthread mutex函数加解锁函数的主要流程,其中需要关注的一点就是这两个函数的执行并不是原子的,是可能存在上下文切换动作的。在通常的用法中,我们加锁操作一般都是为了保护临界资源不被重入改写,一半都是严格按照“加锁-->写入/读取临界资源-->解锁”的流程执行(由加锁的线程负责解锁),而从前文中分析的__pthread_mutex_lock()和__pthread_mutex_unlock_usercnt()函数中也可以看到,只有在原子加锁期间才会改变这__owner值(该值也可认为是临界资源的一部分而被保护起来了),因此是不可能出现加锁已经加锁的线程的,所以也不会调用assert()函数而退出程序的。
但是本程序中对锁的用法显然并不这么“一般”,而是作为一种线程间的同步功能使用。其中主进程中不停的解锁,即是线程A和B没有加锁也同样如此,而线程A和B会竞争的每隔一定时间去加锁,那么就有可能出现如下图中所示的一种情况:1、


该图中主进程待资源就绪后正在解锁一个未被加锁的mutex_lock时发成了线程切换,线程A打断解锁流程完成了一整个加锁的流程,随后线程又且换回了主进程继续执行真正的解锁操作,这样线程A所加的锁就被莫名其妙的解掉了(关键的一点),此时若线程B在等待该锁,则会进入到加锁流程,从而在加锁成功后崩溃在这个__owner判断上。其实该程序出错的主要原因即是解了并未加锁的mutex_lock,如若主进程解得锁是已经上了锁的,则线程A是没有机会加锁的,主进程会原子的完成整个mutex_unlock动作。
另外,其实可以适当的调整程序再来看一下另外一种可能的情形(两个执行流),同样是“线程间同步”用法:2、



这种情况就是在资源就绪较慢且资源处理较快的情况容易出现崩溃,同样是概率性出现的。最后来看第三种可能的情况:3、


这种情况崩溃出现在线程A加锁的过程中被主进程解锁,然后线程A或其他线程又一次加锁的时候。其实不论上述哪一种同步的情况,其出错的原因有两点:(1)解了未被上锁的锁;(2)A线程加的锁由其他线程去解,进一步分析就是没有严格按照“加锁-->解锁”的流程使用mutex锁。

最后对于以上这种“线程间同步”的使用方法可以使用条件变量或者是信号量实现而不要使用mutex锁,mutex锁一般被用在保护线程间临界资源的情况下。


总结:

1、不要去解锁一个未被加锁的mutex锁;

2、不要一个线程中加锁而在另一个线程中解锁;

3、使用mutex锁用于保护临界资源,严格按照“加锁-->写入/读取临界资源-->解锁”的流程执行,对于线程间同步的需求使用条件变量或信号量实现。

【转自】:

Linux应用程序错误使用pthread_mutex_lock互斥锁触发SIG_ABRT信号的原因分析





本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux下错误使用pthread_mutex_lock导致程序奔溃问题分析 的相关文章

  • 何时用引号将 shell 变量括起来?

    我应该或不应该在 shell 脚本中用引号括住变量吗 例如 下列说法正确的是 xdg open URL eq 2 or xdg open URL eq 2 如果是这样 为什么 一般规则 如果它可以为空或包含空格 或实际上任何空格 或特殊字符
  • Apache 端口转发 80 到 8080 并访问 Apache (80) 中托管的应用程序,即 phpMyadmin 和 Tomcat (8080)

    我想访问托管在 tomcat 服务器 8080 中的应用程序 myapp 当前可以通过以下方式访问http example com 8080 myapp http example com 8080 myapp in http example
  • 如何在Linux上用C/C++编写Web服务器[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我正在考虑在 Linux 平台上开发一个小型 阅读 初级 Web 服务器 但我不知道从哪里开始 我希望它能够做的是 监听特定端口 接受
  • 如何将一个文本文件拆分为多个 *.txt 文件?

    我有一个文本文件file txt 12 MB 包含 something1 something2 something3 something4 有没有办法分开file txt分成 12 个 txt 文件 比方说file2 txt file3 t
  • Linux 内核使用的设备树文件 (dtb) 可视化工具? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个可以图形化表示Linux内核中使用的硬件设备树的工具 我正在尝试了解特定 Arm 芯片组
  • 如何获取与 shell 中的文件名模式匹配的所有文件的总文件大小?

    我正在尝试仅使用 shell 来计算与文件名模式匹配的所有文件 在目录树中 的总大小 以字节为单位 这是我到目前为止所拥有的 find name undo exec stat c s awk 总计 1 END 打印总计 有没有更简单的方法来
  • 使用 shell 脚本发送 HTML 邮件

    如何使用 shell 脚本发送 HTML 电子邮件 首先 您需要撰写消息 最低限度由这两个标头组成 MIME Version 1 0 Content Type text html 以及适当的消息正文 p Hello world p 获得后
  • bash 将输出重定向到文件,但结果不完整

    重定向命令输出的问题已经被问过很多次了 但是我有一个奇怪的行为 我使用的是 bash shell debian 版本 4 3 30 1 release 并尝试将输出重定向到文件 但并非所有内容都记录在文件中 我尝试运行的 bin 文件是 l
  • 更新Linux中的包含路径

    我的 my path to file 文件夹中有几个头文件 我知道如何将这些文件包含在新的 C 程序中 但每次我都需要在包含它之前输入头文件的完整路径 我可以在linux中设置一些路径变量 以便它自动查找头文件吗 您可以创建一个 makef
  • MySQL 与 PHP 的连接无法正常工作

    这是我的情况 我正在尝试使用 Apache 服务器上的 PHP 文件连接到 MySQL 数据库 现在 当我从终端运行 PHP 时 我的 PHP 可以连接到 MySQL 数据库 使用 php f file php 但是当我从网页执行它时 它只
  • Docker忽略limits.conf(试图解决“打开文件太多”错误)

    我正在运行一个 Web 服务器 该服务器正在处理数千个并发 Web 套接字连接 为了实现这一点 在 Debian linux 我的基本镜像是 google debian wheezy 在 GCE 上运行 上 打开文件的默认数量设置为 100
  • 使用 libusb 输出不正确

    我用libusb编写了一个程序 我怀疑输出是否正确 因为所有条目都显示相同的供应商和产品 ID 以下是代码 include
  • 如何在 Linux 上通过 FTP 递归下载文件夹 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 Locked 这个问题及其答案是locked help locked posts因为这个问题是题外话 但却具有历史意义 目前不接受新的答案
  • 如何才能将 TCP 连接返回到同一端口?

    机器是 RHEL 5 3 内核 2 6 18 有时我在 netstat 中注意到我的应用程序有连接 建立了 TCP 连接本地地址 and 国外地址是一样的 其他人也报告了同样的问题 症状与链接中描述的相同 客户端连接到本地运行的服务器的端口
  • 有没有一种快速方法可以从 Jar/war 中删除文件,而无需提取 jar 并重新创建它?

    所以我需要从 jar war 文件中删除一个文件 我希望有类似 jar d myjar jar file I donot need txt 的内容 但现在我能看到从 Linux 命令行执行此操作的唯一方法 不使用 WinRAR Winzip
  • 如何使用 JSch 将多行命令输出存储到变量中

    所以 我有一段很好的代码 我很难理解 它允许我向我的服务器发送命令 并获得一行响应 该代码有效 但我想从服务器返回多行 主要类是 JSch jSch new JSch MyUserInfo ui new MyUserInfo String
  • FileOutputStream.close() 中的设备 ioctl 不合适

    我有一些代码可以使用以下命令将一些首选项保存到文件中FileOutputStream 这是我已经写了一千遍的标准代码 FileOutputStream out new FileOutputStream file try BufferedOu
  • 为什么 Linux 没有 DirectX API?

    在考虑现代显卡的 Windows 系统上 DirectX API 的驱动程序端实现时 我想知道为什么此实现在非 Windows 系统 尤其是 Linux 上不可用 由于明显缺乏此功能 我只能假设有一个我无视的充分理由 但在我的原始理解中 我
  • linux下无法创建僵尸进程

    嗯 我有一个奇怪的问题 我无法在我的项目中创建僵尸进程 但我可以在其他文件中创建僵尸进程 有简单的说明 int main if fork 0 printf Some instructions n else sleep 10 wait 0 r
  • python获取上传/下载速度

    我想在我的计算机上监控上传和下载速度 一个名为 conky 的程序已经在 conky conf 中执行了以下操作 Connection quality alignr wireless link qual perc wlan0 downspe

随机推荐

  • 多层多输入的CNN-LSTM时间序列回归预测(卷积神经网络-长短期记忆网络)——附代码

    目录 摘要 卷积神经网络 CNN 的介绍 长短期记忆网络 LSTM 的介绍 CNN LSTM Matlab代码运行结果 本文Matlab代码 数据分享 摘要 本文使用CNN LSTM混合神经网络对时间序列数据进行回归预测 本模型的输入数据个
  • pandas set_index和reset_index

    mport pandas as pd df a pd DataFrame data A B C A1 B1 C1 columns first second third print df a df a set index first seco
  • [项目管理-28]:四象限法与任务的时间优先级管理

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 目录 前言 第1章 四象限法介绍 1 1 概述 1 2 四象限优先级顺序 1 3 详细说明 前言 在项目管理中 我们经常遇到对各种各样的任务
  • 让Layui的table模块支持动态表头

    layui自带的table js插件是不支持的 这里我们简单的加几个字符进去 就可以支持了 1 打开插件目录下的layui lay modules目录 用文本文件打开table js 然后搜索 item2 title 在table js将
  • idea中如何创建xml文件

    本人做java开发程序员已经三年多了 从进入企业至今一直使用idea 对此开发工具还算了解 本人使用的版本 我是一个比较守旧的人 在2020年的今天还是依然使用2017年的版本 还请光大网友见谅哈 今天想说的就是如何在idea中创建 xml
  • docker高级篇(mysql主从,redis主从搭建,Dockerfile解析及docker-compose编排)

    本篇主要是mysql主从 redis主从搭建 Dockerfile解析及docker compose编排 轻量化可视化工具Portainer 重量级工具CAdvisor InfluxDB Granfana的使用 docker的安装 配置 卸
  • 如何把swf的动画嵌入到ppt中_flash(.swf)嵌入ppt中

    利用ppt的控件工具箱中的shockwave flash object控件 添加一个swf文件到ppt 然后右击这个swf文件编辑其属性时 把EmbedMovie一项的值改为True 就表示将其嵌入ppt 你就可以删除swf源文件了 使用
  • ThinkPad E40 XP 安装SATA/AHCI驱动

    ThinkPad E40 XP 安装SATA AHCI驱动 2010 11 06 16 42 我的机子是ThinkPad E40 0578A59 安装XP之后准备打开SATA硬盘的AHCI模式并安装其驱动 但是总是安装失败 只要在BIOS里
  • 吃透这份“Java进阶核心手册”再战字节,直接把面试官按在地上摩擦

    前言 本人计算机本科 已经有两年Java开发经验 由于原来公司已经不能满足我的需求 辞去原来的工作准备跳槽大厂 在辞职之前也认为有做过一段时间的准备 2021年8月初 我满怀信心去字节跳动面试Java研发岗位 结果当场被字节面试官吊打 莫不
  • echarts后台获取数据,前台实现饼图显示并自定义图形的数据表展示格式

    1 实现效果图 切换前效果 切换后效果 2 关键的js代码 var myChart echarts init document getElementById aaa 为echarts对象加载数据 var opts post getUnitA
  • JavaScript DOM(二)查

    书接上回 节点 DOM中有许多不同类型的节点 接下来我们先看看其中的三种 元素节点 文本节点和属性节点 元素节点 指该html里面标签的名字就是元素的名字 例如 我们使用的 p p ul 和 div 之类的元素 p标签的名字是 p 无序列表
  • 安装Node(脚手架)

    目录 一 安装node 脚手架 1 1 配置vue config js 1 2 vue cli3x的目录介绍 1 3 package json 最后 一 安装node 脚手架 从官网直接下载安装即可 自带npm包管理工具 https nod
  • [1132]Flink与Kafka版本对应关系

    以下为Flink和Kafka的版本对照表 Flink版本 Kafka版本 1 12 X 2 4 1 1 11 X 2 4 1 1 10 X 2 2 1 1 9 X 2 2 0 1 8 X 2 0 1 1 7 X 2 0 1 0 10 x 0
  • 怎么把服务器安装系统还原,云服务器磁盘怎么安装系统还原

    云服务器磁盘怎么安装系统还原 内容精选 换一换 华为云帮助中心 为用户提供产品简介 价格说明 购买指南 用户指南 API参考 最佳实践 常见问题 视频帮助等技术文档 帮助您快速上手使用华为云服务 如果Windows操作系统弹性云服务器未安装
  • 程序源码分享-STM32的PID实验(含DAC和ADC)

    一 PID 算法原理 在工业应用中 PID 及其衍生算法是应用最广泛的算法之一 是当之无愧的万能算法 如果能够熟练掌握 PID 算法的设计与实现过程 对于一般的研发人员来讲 应该是足够应对一般研发问题了 在我所接触的控制算法当中 PID 控
  • python链接oracle数据库以及数据库的增删改查

    初次使用python链接oracle 所以想记录下我遇到的问题 便于向我这样初次尝试的朋友能够快速的配置好环境进入开发环节 1 首先 python链接oracle数据库需要配置好环境 我的相关环境如下 1 python Python 3 6
  • 推荐算法3—基于用户的协同过滤算法

    基于邻域的算法是推荐系统中最基本的算法 该算法分为两大类 一类是基于用户的协同过滤算法 另一类是基于物品的协同过滤算法 1 基础算法 在一个在线个性化推荐系统中 当一个用户A需要个性化推荐时 可以先找到和他有相似兴趣的其他用户 然后把那些用
  • 命令执行漏洞原理和测试

    PHP system exec shell exec passthru popen proc popen等称为高危漏洞 原理 只要程序可以调用系统命令的情况下都可以发生命令执行漏洞 条件 用户能够控制函数输入 存在可以执行代码的危险函数 命
  • Linux基础学习(下)——VMWare+CentOS7

    目录 Linux账号管理 用户组管理 磁盘管理 进程管理 Linux环境安装 三种软件安装方式 扩展 VMware使用 Linux账号管理 Linux系统是一个多用户多任务的分时操作系统 任何一个要使用系统资源的用户 都必须首先向系统管理员
  • linux下错误使用pthread_mutex_lock导致程序奔溃问题分析

    在进行程序开发过程中 错误使用了pthread mutex lock导致程序概率性的奔溃 奔溃时报如下错误 问题分析 本文分析在Linux应用程序中错误使用pthread mutex锁时会概率性触发SIG ABRT信号而导致程序崩溃 库打印