如何面对并发下的bug

server/2024/9/23 1:25:20/

整理总结自蒋炎岩老师的b站课程,https://jyywiki.cn/OS/2022/index.html

bug_1">并发bug与应对

  • 应对bug的方法

    • 在代码里边增加很多检查(加断言)

      • #include "thread.h"unsigned long balance = 100;void Alipay_withdraw(int amt) {if (balance >= amt) {usleep(1); // unexpected delaysbalance -= amt;}assert(balance <= 10000000);
        }void Talipay(int id) {Alipay_withdraw(100);
        }int main() {create(Talipay);create(Talipay);join();printf("balance = %lu\n", balance);
        }
        
    • 防御性编程:把程序需要满足的条件用 assert 表达出来。

      • 在这里插入图片描述

      • assert()

      • 在这里插入图片描述

  • 并发bug:死锁 (Deadlock)

    • A deadlock is a state in which each member of a group is waiting for another member, including itself, to take action.

    • ABBA-Deadlock

      • void swap(int i, int j) {spin_lock(&lock[i]);spin_lock(&lock[j]);arr[i] = NULL;arr[j] = arr[i];spin_unlock(&lock[j]);spin_unlock(&lock[i]);
        }
        
      • 上锁的顺序很重要……

        • swap 本身看起来没有问题,swap(1, 2); swap(2, 3), swap(3, 1) → 死锁
    • 避免死锁

      • 死锁产生的四个必要条件

        • 互斥:一个资源每次只能被一个进程使用
        • 请求与保持:一个进程请求资阻塞时,不释放已获得的资源
        • 不剥夺:进程已获得的资源不能强行剥夺
        • 循环等待:若干进程之间形成头尾相接的循环等待资源关系
      • AA-Deadlock

        • 在临界条件检测
        • spinlock-xv6.c中的各种防御性编程
          • if (holding(lk)) panic();
      • ABBA-Deadlock

        • 任意时刻系统中的锁都是有限的

        • 严格按照固定的顺序获得所有锁 (lock ordering; 消除 “循环等待”)

        • class LockOrdering:locks = [ '', '', '' ]def tryacquire(self, lk):self.locks[lk], seen = '🔒', self.locks[lk]return seen == ''def release(self, lk):self.locks[lk] = ''@threaddef t1(self):while True:while not self.tryacquire(0): passwhile not self.tryacquire(1): passwhile not self.tryacquire(2): passself.release(0), self.release(1), self.release(2)@threaddef t2(self):while True:while not self.tryacquire(1): passwhile not self.tryacquire(2): passself.release(1), self.release(2)
          
  • 并发bug:数据竞争 (Data Race)

    • 不同的线程同时访问同一段内存,且至少有一个是写。

    • 用互斥锁保护好共享数据,消灭一切数据竞争

    • 两种经典错误

      • // Case #1: 上错了锁
        void thread1() { spin_lock(&lk1); sum++; spin_unlock(&lk1); }
        void thread2() { spin_lock(&lk2); sum++; spin_unlock(&lk2); }
        
      • // Case #2: 忘记上锁
        void thread1() { spin_lock(&lk1); sum++; spin_unlock(&lk1); }
        void thread2() { sum++; }
        
  • 更多类型的并发 Bug

    • 忘记上锁——原子性违反 (Atomicity Violation, AV)

      • 我以为一段代码没啥事呢,但被人强势插入了

      • 在这里插入图片描述

      • 在这里插入图片描述

    • 忘记同步——顺序违反 (Order Violation, OV)

      • 在这里插入图片描述

      • 由于S4在S2之后执行,当S3执行时,造成了错误的结果

  • 应对并发 Bug 的方法

    • Lockdep: 运行时的死锁检查:lock/unlock

      • 为每一个锁确定唯一的 “allocation site”
        • lock-site.c
        • 为所分配了一个唯一性id,然后程序运行时存储上锁的顺序
        • assert: 同一个 allocation site 的锁存在全局唯一的上锁顺序
      • 检查方法:printf
        • 记录所有观察到的上锁顺序,例如[x,y,z]⇒xy,xz,yz
        • 检查是否存在 xyyx
    • ThreadSanitizer: 运行时的数据竞争检查:内存访问 + lock/unlock

      • 为所有事件建立 happens-before 关系图
        • Program-order + release-acquire
        • 对于发生在不同线程且至少有一个是写的 x,y 检查
        • 在这里插入图片描述
    • 动态分析工具:Sanitizers

    • 防御性编程:低配版 Lockdep

      • 给程序获取锁一个不可能超过的超时时间,当超过后打印日志,配合调试器和线程backtrace一秒锁定死锁

      • int spin_cnt = 0;
        while (xchg(&locked, 1)) {//自选100000000次,即打印日志if (spin_cnt++ > SPIN_LIMIT) {printf("Too many spin @ %s:%d\n", __FILE__, __LINE__);}
        }
        

http://www.ppmy.cn/server/34173.html

相关文章

上位机图像处理和嵌入式模块部署(树莓派4b和mcu的分工与配合)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 树莓派4b从广义上来说&#xff0c;它本身就是一个mini小电脑。我们需要什么软件&#xff0c;就可以apt install去下载即可。这和之前嵌入式linux开…

AI新突破:多标签预测技术助力语言模型提速3倍

DeepVisionary 每日深度学习前沿科技推送&顶会论文分享&#xff0c;与你一起了解前沿深度学习信息&#xff01; 引言&#xff1a;多标签预测的新视角 在人工智能领域&#xff0c;尤其是在自然语言处理&#xff08;NLP&#xff09;中&#xff0c;预测模型的训练方法一直在…

[微信小程序] 入门笔记1-滚动视图组件

[微信小程序] 入门笔记1-滚动视图组件 1.页面&组件&渲染 在小程序是由一个个页面page组成, 而页面又是由一个个组件component组成.和网页类似,这里的组件指的就是输入框<input>,按钮<button>,文本<text>,图片<image>等元素.如果你学过网页一…

RK3568 学习笔记 : 精简 u-boot env 默认复杂的多种引导启动设置

前言 环境&#xff1a; 正点原子 Atompi-CA1 RK3568 开发板、正点原子 DLRK3568 开发板&#xff0c;&#xff08;一时脑热买了两块 RK3568 开发板&#xff09;&#xff0c;Atompi-CA1 RK3568 开发板比较小巧&#xff0c;利于一些前期的嵌入式 Linux 开发学习与实践。 RK3568 开…

以太网基础-IP、ICMP、ARP协议

一、IP协议 参考&#xff1a;rfc791.txt.pdf (rfc-editor.org) IP协议&#xff08;Internet Protocol&#xff09;是TCP/IP协议族中最核心的协议&#xff0c;提供不可靠的、无连接的、尽力而为的数据报传输服务。 IP报文数据头如下 Version&#xff1a;4bit&#xff0c;4表示…

小白入门 CSS 语法规则

文章导读&#xff1a;AI 辅助学习前端&#xff0c;包含入门、进阶、高级部分前端系列内容&#xff0c;当前是 CSS 的部分&#xff0c;瑶琴会持续更新&#xff0c;适合零基础的朋友&#xff0c;已有前端工作经验的可以不看&#xff0c;也可以当作基础知识回顾。 这篇文章给大家…

【银角大王——Django课程——靓号页面的基本操作(列表,新建,删除)】

靓号管理 靓号列表显示表结构根据表结构的需求&#xff0c;在models.py中创建类URL.py编写views.py中 函数编写列表显示HTML页面效果 新建靓号编写&#xff08;添加&#xff09;URL.py编写views.py中 函数编写新建靓号HTML数据校验方式一&#xff1a;&#xff08;正则表达式&am…

用python画一个正八边形

1 问题 使用turtle库的turtle.fd()函数和turtle.seth()函数绘制一个边长100的正八边形。 2 方法 1、利用for循环解决如何画出图形中相同的八条边的问题。 2、再利用turtle.fd()函数和turtle.seth()函数画出完整的图形。 代码清单 1 import turtleturtle.pensize(2)d0for i in r…