计算机操作系统同步互斥

1 背景

在计算机系统里面, 多道程序设计是现代操作系统的重要特征, 且并行起到了很大的作用, 所以操作系统抽象出来了线程/进程的概念用来支持多道程序设计, 同时, 各个进程之间需要进行交互, CPU也需要进行调度来支持多进程. 多进程会涉及到共享资源访问的问题, 如果操作系统调度不当, 就可能出现饥饿, 死锁等问题. 以下是独立进程/线程与合作进程/线程(进程/线程间会有交互, 并共享资源等)的对比:

独立的进程/线程:

  • 不和其他进程/线程共享资源或状态
  • 确定性: 输入状态决定结果
  • 可重现: 能够重现起始条件, I/O
  • 调度顺序不重要

合作进程/线程:

  • 在多个线程中共享状态
  • 不确定性
  • 不可重现

不确定性和不可重现意味着bug可能是间歇性发生的, 但是计算机/设备需要合作, 其优点如下:

  • 共享资源
  • 一台电脑, 多个用户
  • 一个银行存款余额, 多台ATM
  • 嵌入式系统(机器人控制: 手臂与手的协调)
  • 加速
  • I/O操作和计算可以重叠
  • 多处理器 - 将程序分成多个部分并行执行
  • 优化
  • 将大程序分解成小程序(以编译为例, gcc会调用cpp, cc1, cc2, as, ld)
  • 使系统易于扩展

 而我们希望的多进程/线程的运行方式为:

  • 无论多进程/线程的指令序列怎样交替执行, 程序都必须正常工作
  • 多线程程序具有不确定性和不可重现的特点
  • 不经过专门设计, 调试难度很高
  • 不确定性要求并行程序的正确性
  • 先思考清楚问题, 把程序的行为设计清楚
  • 切忌急于着手编写代码, 碰到问题再调试

2 一些概念

竞态条件:

 系统缺陷: 结果依赖于并发执行或者事件的顺序/时间

  • 不确定性
  • 不可重现

如何避免竞态条件? 可以让执行指令不被打断

原子操作:

原子操作是指一次不存在任何中断或者失败的执行:

  • 该执行成功结束
  • 或者根本没有执行
  • 并且不应该发现任何部分执行的状态

实际上操作往往不是原子的

  • 有些看上去是原子操作, 实际上不是
  • 连 x++这样的简单语句, 实际上是由3条指令构成的
  • 有时候甚至连单条机器指令都不是原子的

临界区:

临界区是指进程中的一段需要访问共享资源并且当另一个进程处于相应代码区域时便不会被执行的代码区域.

互斥:

当一个进程处于临界区并访问共享资源时, 没有其他进程会处于临界区并且访问任何相同的共享资源.

死锁:

两个或以上的进程, 在相互等待完成特定任务, 而最终没法将自身任务进行下去.

饥饿:

一个可执行的进程, 被调度器持续忽略, 以至于虽然处于可执行状态却不被执行.

3 临界区

3.1 临界区中执行的属性

临界区中执行需要有一些属性保证临界区的顺利执行:

  • 互斥: 同一时间临界区中最多存在一个线程
  • progress: 如果一个线程想要进入临界区, 那么它最终会成功(也就是说不会让一个想要进入临界区的线程一直等待, 总会进入的)
  • 有限等待: 如果一个线程 i 处于入口区, 那么在 i 的请求被接受之前, 其他线程进入临界区的时间是有限制的(只会等待有限制的时间)
  • 无忙等待(可选): 如果一个进程在等待进入临界区, 那么在它可以进入之前会被挂起(就是不要用while循环一直在检查是否能进入, 会消耗CPU资源)

3.2 三种针对临界区属性的保护方法

3.2.1 禁用硬件中断

在临界区里面进程执行是屏蔽中断的: 没有中断, 没有上下文切换, 因此没有并发.

  • 硬件将中断处理延迟到中断被启用之后
  • 大多数现代计算机体系结构都提供指令来完成

进入临界区

  • 禁用中断

离开临界区

  • 开启中断

利用中断来确保临界区顺利执行的缺点:

  • 一旦中断被禁用, 线程就无法被停止
  • 整个系统都会为你停下来
  • 可能导致其他线程处于饥饿状态
  • 要是临界区可以任意长怎么办
  • 无法限制相应中断所需的时间(可能存在硬件影响)
  • 在多CPU的情况下, 只禁用一个CPU的中断无法解决问题 

因此需要小心使用

3.2.2 基于软件的解决方法

满足互斥, 但是有时候不满足progress.

3.2.3 更高级的抽象

更高级的抽象方法是需要一定的基础的, 这个基础就是硬件要提供一些原语支持:

  • 比如像中断禁用, 原子操作指令等
  • 大多数现代体系结构都这样

有了上面的基础, 操作系统就可以利用这些硬件支持来提供更高级的编程抽象来简化多进程/多线程的并行编程:

  • 比如像锁, 信号量
  • 这些高级的用于并行编程的抽象概念是通过上面说的硬件原语构建的

下面用锁来举例:

锁是一个抽象的数据结构, 也就是上面说的高级的用于并行编程的抽象概念, 具有以下特点:

  • 一个二进制状态(锁定/解锁), 两种方法
  • Lock::Acquire() --- 锁被释放前一直等待, 然后得到锁
  • Lock::Release() --- 释放锁, 唤醒任何等待的进程

如果使用锁来处理临界区的话, 可以如下:

lock_next_pid->Acquire();
new_pid = next_pid++;
lock_next_pid->Release();

这样就保证了临界区的顺利执行.

那么lock的两个接口是怎么实现的呢, 前面说过, 是通过现代计算机的体系结构提供的硬件支持, 大索数现代体系结构都提供特殊的原子操作指令:

  • 通过特殊的内存访问电路
  • 针对单处理器和多处理器

比如硬件提供了以下两种原子操作指令:

1. Test - and - Set测试和置位

  1. 从内存中读取值
  2. 测试该值是否为1(然后返回真或假)
  3. 该内存值置为1

其简单实现如下:

 boolean TestAndSet(boolean *target) {boolean rv = *target;*target = TRUE;return rv;
}

2. 交换

  • 交换内存中的两个值

简单实现如下:

void Exchange(boolean *a, boolean *b) {boolean temp = *a;*a = *b;*b = temp;
}

有了以上两种硬件提供的原子操作指令: TestAndSet以及Exchange之后, 我们就可以利用这两条指令来设计锁的Acquire和Release接口了:

1. 用TestAndSet实现

class Lock {int value = 0;
}Lock::Acquire(){while(TestAndSet(&value)){}
}Lock::Release() {value = 0;
}

设计思路:

  • Acquire: 初始化的时候value为0, 代表没有其他进程/线程进入临界区, 这时候Acquire里面调用TestAndSet返回值是0, 但是value被设置成了1, 这样就可以直接退出, 从而进入临界区执行, 这时候如果其他进程调用这个lock的Acquire, 这时候value是1了, TestAndSet返回值是1, value设置为1, 这样就会一直在while条件里面循环.(忙等)
  • Release: 把value置为0, 这样就会让其他调用Acquire的线程中TestAndSet的返回值置为0从而退出while循环从而继续执行

方法评价:

这种方法是忙等, 会占用CPU资源. 可以使用在等待过程中挂起该线程的方式, 并唤醒其他等待进程的方式. 但是这种方式需要上下文切换, 如果预计等待时间较长的话, 可以选择这种方式.

2. 用Exchange实现

简单实现如下:

int lock = 0;                //设置一个全局初始变量
//以下是线程Ti想要进入临界区的操作
int key;
do {key = 1;while (key == 1)exchange(&lock, &key);//然后执行临界区代码lock = 0;//执行剩余代码
}

设计思路:

  • 当第一个线程想要进入临界区之前, 先把key设置为1, 然后判断key = 1的话, 就执行交换key和lock, 这时lock被置为1, 因为是第一个线程, 所以此时key为0, 退出循环, 执行临界区代码.
  • 当其他线程在第一个线程执行临界区代码过程中想要进入临界区, 执行exchange之后, 因为lock值在有线程在临界区执行时都会是1, 所以key一直都是1, 也就会一直在while中循环.
  • 在临界区线程执行完毕后, 会把lock置为0, 让其他线程进入. 

优缺点:

  • 优点: 适用于单处理器或者共享主存的多处理器中任意数量的进程, 简单并且容易证明, 可以用于支持多临界区.
  • 缺点: 忙等待消耗处理器时间, 当进程离开临界区并且多个进程在等待的时候可能导致饥饿. 死锁: 如果一个低优先级进程拥有临界区并且一个高优先级进程也需求, 那么高优先级进程会获得处理器并等待临界区, 最终结果是高优先级进程忙等占用CPU, 低优先级进程占用临界区却得不到CPU资源进而无法释放锁, 导致两个进程的临界区代码都无法正常执行.

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

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

相关文章

conda安装tensorflow-gpu简洁版_win10 tensorflow2.2 安装注意事项

学习新技术有两座大山,一座是安装配置环境,另一座是调试bug。对于想学习人工智能开发的人来说,安装配置tensorflow是必不可少的一步,这个过程对于初次接触的人来说,到处都是火坑。下面大家跟我一起,看看都会…

操作系统中死锁避免算法 --- 银行家算法

1. 背景 在银行系统中, 客户完成项目需要申请贷款的数量是有限的, 每个客户在第一次申请贷款时要声明完成该项目所需的最大资金量, 在满足所有贷款要求并完成项目时, 客户应及时归还. 银行家在客户申请的贷款数量不超过自己拥有的最大值时, 都应尽量满足客户的需要. 在这样的描…

python字符串对齐_Python - 字符串对齐

字符串对齐 本文地址: http://blog.csdn.net/caroline_wendy/article/details/20463231 Python中, 字符串对齐, 使用ljust(), 左对齐;rjust(), 右对齐; center(), 中间对齐; 也可以修改第三个参数, 修改填充数据, 默认使用空格; 代码如下: # -*- coding: utf-8 -*- # #File: Te…

操作系统中的死锁问题

1 死锁问题概述 一组阻塞的进程持有一种资源等待获取另一个进程所占有的一个资源.例子: 系统有两个磁带驱动器, P1和P2各有一个, 都需要另外一个. 2 系统模型 资源类型: , , ..., 包括CPU cycles, memory space, I/O devices 每个资源类型有实例. 每个进程使用资源过程如下…

chrome控制台如何把vw显示成px_你可能不知道的chrome调试技巧

本文是对常用的chrome调试技巧进行总结整理,如果你没有深入了解过chrome调试工具,此处总有你不知道的惊喜!从 Chrome 说起对于大部分人来说,Chrome 可能只是个浏览器,但是对于开发人员来说,它更是一个强大无…

gns3中两个路由器分别连接主机然后分析ip数据转发报文arp协议_ARP协议在同网段及跨网段下的工作原理...

前言:ARP协议是在链路层通讯中,已知目标IP地址,但是,仅有IP 地址是不够的,因为IP数据报必须封装成帧才能通过数据链路进行发送,而数据帧必须要有目的MAC地址,每一个网络设备在数据封装前都需要获取下一跳的…

操作系统文件系统

1 基本概念 1.1 文件系统和文件 1.1.1 简述 文件系统: 一种用于持久性存储的系统抽象 在存储器上: 组织, 控制, 导航, 访问和检索数据大多数计算机系统包含文件系统个人电脑, 服务器, 笔记本电脑ipod, Tivo/机顶盒, 手机/掌上电脑google可能是由一个文件系统构成的 文件: 文…

c++代码整洁之道pdf_别再问如何用python提取PDF内容了

作者:陈熹 来源:早起Python大家好,在之前的办公自动化系列文章中我们已经详细介绍了如何使用python批量处理PDF文件,包括合并、拆分、水印、加密等操作。今天我们再次回到PDF,详细讲解如何使用python从PDF提取指定的信…

查看文章影响因子的插件_Scholarscope--在新版PubMed中实现基于影响因子的文献筛选...

小编之前介绍过如何在Pubmed上直接显示杂志影响因子的方法,这个方法主要是依托Scholarscope插件,其实除了显示影响因子,这个插件还可以帮助大家根据影响因子筛选文献哦,操作也很简单,只要生成自定义过滤器即可&#xf…

leetcode32 --- longestValidParentheses

1 题目 给你一个只包含 ( 和 ) 的字符串,找出最长有效(格式正确且连续)括号子串的长度。 2 解法 2.1 动态规划方法 维护一个字符串长度的数组cur_max_len, 第i个元素代表以当前(或者)结束的最长有效括号的长度. 这样就会利用动态规划递推…

armitage识别不了漏洞_Shiro RememberMe 漏洞检测的探索之路

前言Shiro 是 Apache 旗下的一个用于权限管理的开源框架,提供开箱即用的身份验证、授权、密码套件和会话管理等功能。该框架在 2016 年报出了一个著名的漏洞——Shiro-550,即 RememberMe 反序列化漏洞。4年过去了,该漏洞不但没有沉没在漏洞的…

css响应式布局_用 CSS Grid 布局制作一个响应式柱状图

最新一段时间比较喜欢玩弄图表,出于好奇,我想找出比较好的用 CSS 制作图表的方案。开始学习网上开源图表库,它对我学习新的和不熟悉的前端技术很有帮助,比如这个:CSS Grid。今天和大家分享我学到的新知识:如…

vs code 插件_[VSCode插件开发] 由浅入深,带你了解如何打造百万级产品

去年,笔者有幸在微软技术暨生态大会上做了个演讲,主题是“从零开始开发一款属于你的 Visual Studio Code 插件”。演讲内容主要覆盖了VS Code插件开发的四个方面:设计、实现、推广和维护。作为一个开发者,我们往往会把大多数的时间…

leetcode45 --- jump

1 题目 给定一个非负整数数组,你最初位于数组的第一个位置。 数组中的每个元素代表你在该位置可以跳跃的最大长度。 你的目标是使用最少的跳跃次数到达数组的最后一个位置。 假设你总是可以到达数组的最后一个位置。 2 解法 2.1 从终点遍历的方法(时间复杂度)…

python怎么查看网页编码格式_怎么用python爬取网页文字?

用Python进行爬取网页文字的代码:#!/usr/bin/python# -*- coding: UTF-8 -*-import requestsimport re# 下载一个网页url htt用python进行爬取网页文字的代码:#!/usr/bin/python# -*- coding: UTF-8 -*-import requestsimport re# 下载一个网页url htt…

leetcode41 --- firstMissingPositive

1 题目 给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 2 解法 最笨的方法是从1开始试, 看1在数组里面是否出现过, 2, 3, ....不过时间复杂度是. 2.1 hash 可以考虑…

计算机网络中的数据链路层

概述 结点: 主机, 路由器 链路: 网络中两个结点之间的物理通道, 链路的传输介质主要有双绞线, 光纤和微波, 分为有线链路和无线链路. 数据链路: 网络中两个结点之间的逻辑通道, 把实现控制数据传输协议的硬件和软件加到链路上就构成数据链路. 帧: 链路层的协议数据单元…

jnativecpp.dll一定要放到系统目录下吗_电脑硬盘有必要分区吗,分几个区最好?...

购买电脑后,必不可少的一个步骤就是对电脑硬盘进行分区了,其实硬盘分区也是有讲究的,并不是随意分的,下面租物租就给大家介绍下电脑分区的知识。硬盘分区可以提高数据的安全,防止数据丢失,但是防止数据丢失…

数据结构中的树

1. 树 即是以层次化方式组织和存放数据的特定数据结构 边: 节点与节点之间的连线 根节点: 叶子节点: 度: 节点的度数即为其分叉数, 即其子节点个数. 整棵树的度数是所有节点中度数的最大值 节点深度: 根节点到该节点的唯一路径长(即边的数量) 树高: 所有节点中深度的最大…

排序算法 --- 堆排序

根据大顶堆的描述, 父节点的值始终大于子节点(如果有的话)的值, 再加上堆是完全二叉树, 可以用数组表示, 那么就可以用来进行排序. 具体做法就是, 对于随机排列的数组: 1. 首先将其构建成一个大顶堆, 根据堆的性质, 此时堆顶就是最大值. 2. 把堆顶元素与数组最后一个元素进行…