0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux中的伤害/等待互斥锁介绍

Linux阅码场 来源:Linux驿站 作者:szyhb1981 2021-11-06 17:27 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

序言:近期读Linux 5.15的发布说明,该版本合并了实时锁机制,当开启配置宏CONFIG_PREEMPT_RT的时候,这些锁被基于实时互斥锁的变体替代:mutex、ww_mutexrw_semaphorespinlock和rwlock。第一次听说ww_mutex,在百度上查找的时候发现介绍文档很少,于是自己学习,写成笔记。在某些场合必须同时持有多个锁,并且获取锁的顺序可能不同,为了避免死锁,应该使用伤害/等待互斥锁(Wound/Wait Mutexes)。获取一个锁集合称为一个事务(transaction),每个事务关联一张门票(ticket),门票也称为序列号,根据门票判断哪个事务年轻。有2种处理死锁的方法,如下。(1)等待-死亡(Wait-Die)算法:一个事务申请另一个事务已经获取的锁的时候,如果持有锁的事务年轻,那么申请锁的事务等待(wait);如果持有锁的事务年老,那么申请锁的事务退并且死亡(die)

(2) 4.19版本开始支持伤害-等待(Wound-Wait)算法:一个事务申请另一个事务已经获取的锁的时候,如果持有锁的事务年轻,那么申请锁的事务伤害(wound)持有锁的事务,请求它去死亡;如果持有锁的事务年老,那么申请锁的事务等待(wait)

假设进程1和进程2分别在2个处理器上运行,进程1获取锁A,进程2获取锁B,然后进程1申请锁B,进程2申请锁A。假设进程1的门票编号比进程2的门票编号小,也就是进程1年老,进程2年轻。假设选择等待-死亡算法。年老的进程1申请锁B,发现持有锁B的进程2年轻,那么年老的进程1等待。年轻的进程2申请锁A,发现持有锁A的进程1年老,那么年轻的进程2死亡(即申请锁的函数返回“-EDEADLK”),接着回滚(即释放已经获取的锁B),然后重新开始:先申请锁A然后申请锁B(必须改变申请顺序,如果先申请锁B,那么会把刚释放的锁B抢回来)。假设选择伤害-等待算法。年老的进程1申请锁B,发现持有锁B的进程2年轻,那么伤害年轻的进程2,请求它死亡。年轻的进程2申请锁A,发现持有锁A的进程1年老,那么年轻的进程2等待,在收到进程1的死亡请求以后,年轻的进程2死亡(即申请锁的函数返回“-EDEADLK”),接着回滚(即释放已经获取的锁B),然后重新开始:先申请锁A然后申请锁B。两种算法都是公平的,因为其中一个事务最终会成功。和等待-死亡算法相比,伤害-等待算法生成的退避少,但是从一次退避恢复的时候要做更多的工作。伤害-等待算法是一种抢占性的算法(因为事务被其它事务伤害),需要一种可靠的方法来选择受伤状态和抢占正在运行的事务。在伤害-等待算法中,一个事务在受伤后死亡(返回“-EDEADLK”),就认为这个事务被抢占。如果竞争锁的进程少,并且希望减少回滚的次数,那么应该选择伤害-等待算法。 和普通的互斥锁相比,伤害/等待互斥锁增加了下面2个概念。

(1)获取上下文acquire context):一个获取上下文表示一个事务,关联一张门票(ticket),门票也称为序列号,门票编号小表示年老,门票编号大表示年轻。获取上下文跟踪调试状态,捕获对伤害/等待互斥锁接口的错误使用。

(2)伤害/等待类初始化获取上下文的时候需要指定锁类,锁类会给获取上下文分配门票。锁类也指定算法:等待-死亡(Wait-Die)或伤害-等待(Wound-Wait)。当多个进程竞争同一个锁集合的时候,它们必须使用相同的锁类。

3种获取伤害/等待互斥锁的函数,如下。

(1) 普通的获取锁函数ww_mutex_lock(),带有获取上下文。

(2) 进程在回滚(即释放所有已经获取的锁)以后,使用慢路径获取锁函数ww_mutex_lock_slow()获取正在竞争的锁。带有“_slow”后缀的函数不是必需的,因为可以调用函数ww_mutex_lock()获取正在竞争的锁。带有“_slow”后缀的函数的优点是接口安全,如下。

  • 函数ww_mutex_lock()有一个整数返回值,而函数ww_mutex_lock_slow()没有返回值。
  • 当开启调试的时候,函数ww_mutex_lock_slow()检查所有已经获取的锁已经被释放,并且确保进程阻塞在正在竞争的锁上面。

(3) 只获取一个伤害/等待互斥锁,和获取普通的互斥锁完全相同。调用函数ww_mutex_lock(),把获取上下文指定为空指针。

伤害/等待互斥锁的使用方法如下。

(1) 定义一个锁类,锁类在初始化获取上下文的时候需要,锁类也指定算法:等待-死亡(Wait-Die)或伤害-等待(Wound-Wait)。

/* 指定等待-死亡算法 */static DEFINE_WD_CLASS(my_class);
/* 指定伤害-等待算法 */staticDEFINE_WW_CLASS(my_class);

(2) 初始化一个获取上下文,锁类会给获取上下文分配一张门票。

void ww_acquire_init(struct ww_acquire_ctx *ctx, struct ww_class *ww_class);
(3) 获取锁,返回0表示获取成功,返回“-EDEADLK”表示检测出死锁。
int ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx);
(4) 获取需要的所有锁以后,标记获取阶段结束。目前这个函数没有执行任何操作,但是将来可能改变。
void ww_acquire_done(struct ww_acquire_ctx *ctx);
(5) 释放锁。
void ww_mutex_unlock(struct ww_mutex *lock);
(6) 释放所有锁以后,释放获取上下文。
void ww_acquire_fini(struct ww_acquire_ctx *ctx);

下面是一个例子,注意:调用函数ww_mutex_lock()申请锁失败以后,应该先释放已经获取的锁,然后调用慢路径函数ww_mutex_lock_slow()获取正在竞争的锁,最后获取其它锁。重新开始申请锁的时候必须改变申请顺序,因为如果按照原来的顺序申请锁,那么会把刚释放的锁抢回来。
/* 第1步:定义锁类,指定伤害-等待算法。*/static DEFINE_WW_CLASS(ww_class);
struct obj {  struct ww_mutex lock;  /* obj data */};
struct obj_entry {  struct list_head head;  struct obj *obj;};
int lock_objs(struct list_head *list, struct ww_acquire_ctx *ctx){  struct obj *res_obj = NULL;  struct obj_entry *contended_entry = NULL;  struct obj_entry *entry;  int ret;
  /* 第2步:初始化获取上下文。*/  ww_acquire_init(ctx, &ww_class);
  /* 第3步:获取锁。*/retry:  list_for_each_entry(entry, list, head) {    if (entry->obj == res_obj) {      res_obj = NULL;      continue;    }
    ret = ww_mutex_lock(&entry->obj->lock, ctx);    if (ret < 0) {      contended_entry = entry;      goto err;    }  }
  /* 第4步:标记获取阶段结束。*/  ww_acquire_done(ctx);  return 0;
err:  /* 回滚,释放已经获取的锁。*/  list_for_each_entry_continue_reverse(entry, list, head) {    ww_mutex_unlock(&entry->obj->lock);  }
  if (res_obj) {    ww_mutex_unlock(&res_obj->lock);  }
  if (ret == -EDEADLK) {    /* 使用慢路径获取锁函数获取正在竞争的锁。*/    ww_mutex_lock_slow(&contended_entry->obj->lock, ctx);    res_obj = contended_entry->obj;    /* 获取其它锁。*/    goto retry;  }  ww_acquire_fini(ctx);
  return ret;}
void unlock_objs(struct list_head *list, struct ww_acquire_ctx *ctx){  struct obj_entry *entry;
  /* 第5步:释放锁。*/  list_for_each_entry (entry, list, head) {    ww_mutex_unlock(&entry->obj->lock);  }
  /* 第6步:释放获取上下文。*/  ww_acquire_fini(ctx);}
责任编辑:haq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 锁相环
    +关注

    关注

    36

    文章

    633

    浏览量

    90802
  • Linux
    +关注

    关注

    88

    文章

    11627

    浏览量

    217895

原文标题:伤害/等待互斥锁

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    Linux-RT特点及简单应用

    Scheduling),允许内核中断正在执行的任务以执行更高优先级的任务。这可以降低任务响应时间,提高实时性能。 内核优化:Linux-RT对内核机制进行了优化,减少了
    发表于 12-05 07:37

    【书籍评测活动NO.67】成为硬核Linux开发者:《Linux 设备驱动开发(第 2 版)》

    ,解析模块的构建逻辑,重点介绍树外构建与树内构建,讲解Linux内核编程技巧。系统讲解并发与同步、延迟与中断处理等核心辅助函数,包括自旋互斥
    发表于 11-17 17:52

    一个硬件SPI两个CS操作两个norflash,怎么互斥操作两个norflash?

    一个硬件SPI两个CS操作两个norflash,怎么互斥操作两个norflash,有一个norflash被模拟成U盘,会在中断操作spi。
    发表于 09-26 06:18

    五大认证加冕!德施曼以“首创AI智能”重构智能竞争格局

    近日,智能领军品牌德施曼获得尚普咨询及研世纪两大权威机构颁发的中国“首创AI智能”、“AI智能领导者”、“AI智能全国销量第一”、
    的头像 发表于 09-20 10:43 642次阅读
    五大认证加冕!德施曼以“首创AI智能<b class='flag-5'>锁</b>”重构智能<b class='flag-5'>锁</b>竞争格局

    五大认证加冕!德施曼以“首创AI智能”重构智能竞争格局

    近日,智能领军品牌德施曼获得尚普咨询及研世纪两大权威机构颁发的中国“首创AI智能”、“AI智能领导者”、“AI智能全国销量第一”、
    的头像 发表于 09-20 10:41 929次阅读

    使用lv_label_set_text释放内存没对齐是什么原因导致的?

    (guider_ui.monitor_label_pressure_now, "1"); rt_mutex_release(lv_mutex); // 释放互斥 使用lv_label_set_text导致释放内存没对齐是什么问题 已经加了
    发表于 09-16 06:44

    官网nrf24l01的例程demo会出现互斥报错是为什么?

    我在用nrf24L01官网下载的例程包,用stm32进行开发,串口助手显示互斥错误,有朋友遇到这个问题吗
    发表于 09-10 06:05

    如何在Linux配置DNS服务器

    本文详细介绍了如何在Linux配置DNS服务器,包括DNS工作原理、本地缓存、DNS查询过程,以及正向和反向查询的配置。步骤包括服务器配置、编辑BIND配置文件、添加解析信息和客户端测试,同时提到了注意事项和常见问题解决方法。
    的头像 发表于 05-09 13:38 2273次阅读
    如何在<b class='flag-5'>Linux</b><b class='flag-5'>中</b>配置DNS服务器

    突然短路 对电机绕组有什么伤害

    突然短路对电机绕组产生的伤害是极其严重的,可能表现为匝间、相间或对地故障,这些故障形式都可能对绕组造成重大损害。以下是对这些伤害的详细分析: 1. 电磁力冲击:     ● 短路发生时,会产生巨大
    的头像 发表于 03-27 16:48 897次阅读

    电路工作原理 自电路与常开电路的区别

    一、自电路工作原理 自电路是电路的一种特殊设计,一旦按下开关,电路就能自动保持持续通电状态,直到按下其他开关使之断路为止。这种特性使得自电路在需要长时间保持电路接通的场景
    的头像 发表于 01-31 10:07 5389次阅读

    电路使用的元器件介绍

    电路在工业自动化、家用电器和各种电子设备中有着广泛的应用。它们的核心功能是在接收到一个短暂的触发信号后,能够保持电路的开启或关闭状态,直到接收到另一个信号。以下是自电路中常用的一些元器件
    的头像 发表于 01-18 10:12 1512次阅读

    电路在工业自动化的应用

    包含一个触发器(如双稳态触发器)和一个或多个传感器,用于检测外部条件并触发电路状态的改变。 2. 自电路在电机控制的应用 在电机控制领域,自电路可以用来实现电机的启动和停止控制。例如,当操作员按下启动按钮时,自
    的头像 发表于 01-18 10:07 1321次阅读

    电路在家居控制的应用

    随着智能家居技术的发展,家庭自动化系统越来越受到人们的欢迎。自电路作为自动化系统的一个关键组件,它通过保持电路状态来实现自动化控制,无需持续的输入信号。 一、自电路的基本原理 自
    的头像 发表于 01-18 09:58 1092次阅读

    24路电磁主板在智能存储系统的作用

    在无人值守场景,如自助服务机、智能生鲜柜、共享储物柜等,使用24路电磁主板可以集成身份识别技术,将用户的验证结果转化为相应的开锁动作,提升用户体验和运营效率,是实现智能存储系统高效、安全和自动化
    的头像 发表于 12-30 14:20 925次阅读
    24路电磁<b class='flag-5'>锁</b>主板在智能存储系统<b class='flag-5'>中</b>的作用

    Linux的用户与创建

    Linux的用户与创建 用户的类型 超级管理用户: 权限最高的用户(uid:0) #uid:是用户的身份证号,Linux系统只认uid 普通用户: 权限受限的用户(uid:1000-60000
    的头像 发表于 12-20 14:24 1181次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>中</b>的用户与创建