- 浏览: 494709 次
- 性别:
- 来自: 广州
文章分类
- 全部博客 (502)
- Java (70)
- Linux (10)
- 数据库 (38)
- 网络 (10)
- WEB (13)
- JSP (4)
- 互联网 (71)
- JavaScript (30)
- Spring MVC (19)
- HTML (13)
- CSS (3)
- AngularJS (18)
- Redis (5)
- Bootstrap CSS (1)
- ZooKeeper (4)
- kafka (6)
- 服务器缓存 (4)
- Storm (1)
- MongoDB (9)
- Spring boot (16)
- log4j (2)
- maven (3)
- nginx (5)
- Tomcat (2)
- Eclipse (4)
- Swagger (2)
- Netty (5)
- Dubbo (1)
- Docker (7)
- Hadoop (12)
- OAuth (1)
- webSocket (4)
- 服务器性能 (7)
- Session共享 (1)
- tieye修改 (1)
- 工作 (1)
- 有用的语录 (0)
- https (2)
- common (5)
- 产品开发管理 (1)
- CDN 工作原理 (1)
- APNS、GCM (1)
- 架构图 (3)
- 功能实现分析 (1)
- JMX (1)
- 服务器相关操作命令 (1)
- img02 (0)
- 服务器环境搭建 (9)
- goodMenuBook (1)
- CEInstantPot (0)
- 有用数据 (1)
- 百度地图WEB API (2)
- 正则表达式 (1)
- 样式例子 (2)
- staticRecipePressureCooker.zip (1)
- jCanvas (1)
- 网站攻击方法原理 (1)
- 架构设计 (3)
- 物联网相关 (3)
- 研发管理 (7)
- 技术需求点 (1)
- 计划 (1)
- spring cloud (11)
- 服务器开发的一些实用工具和方法 (1)
- 每天学到的技术点 (4)
- Guava (1)
- ERP 技术注意要点 (2)
- 微信小程序 (1)
- FineRepor (1)
- 收藏夹 (1)
- temp (5)
- 服务架构 (4)
- 任职资格方案 (0)
- osno_test (1)
- jquery相关 (3)
- mybatis (4)
- ueditor (1)
- VueJS (7)
- python (10)
- Spring EL (1)
- shiro (1)
- 前端开发原理与使用 (7)
- YARN (1)
- Spark (1)
- Hbase (2)
- Pig (2)
- 机器学习 (30)
- matplotlib (1)
- OpenCV (17)
- Hystrix (1)
- 公司 (1)
- miniui (4)
- 前端功能实现 (3)
- 前端插件 (1)
- 钉钉开发 (2)
- Jenkins (1)
- elasticSearch使用 (2)
- 技术规范 (4)
- 技术实现原理 (0)
最新评论
Linux的原子操作与同步机制
并发问题
例如C语言语句“count++;”在未经编译器优化时生成的汇编代码为多条机器指令来实现的。
例子:
假设count变量初始值为0。进程1执行完“mov eax, [count]”后,寄存器eax内保存了count的值0。此时,
进程2被调度执行,抢占了进程1的CPU的控制权。进程2执行“count++;”的汇编代码,将累加后的count值
1写回到内存。然后,进程1再次被调度执行,CPU控制权回到进程1。进程1接着执行,计算count的累加
值仍为1,写回到内存。虽然进程1和进程2执行了两次“count++;”操作,但是count实际的内存值为1,而
不是2!
单处理器原子操作
解决这个问题的方法是,将“count++;”语句翻译为单指令操作。
Intel x86指令集支持内存操作数的inc操作,这样“count++;”操作可以在一条指令内完成。因为进程的上下文
切换是在总是在一条指令执行完成后,所以不会出现上述的并发问题。对于单处理器来说,一条处理器指令就
是一个原子操作。
多处理器原子操作
但是在多处理器的环境下,例如SMP架构,这个结论不再成立。我们知道“inc [count]”指令的执行过程分为三步:
1)从内存将count的数据读取到cpu。
2)累加读取的值。
3)将修改的值写回count内存。
这又回到前面并发问题类似的情况,只不过此时并发的主题不再是进程,而是处理器。
Intel x86指令集提供了指令前缀lock用于锁定前端串行总线(FSB),保证了指令执行时不会受到其他处理器的干扰。
使用lock指令前缀后,处理器间对count内存的并发访问(读/写)被禁止,从而保证了指令的原子性。
arm原子操作实现
在arm的指令集中,不存在指令前缀lock,那如何完成原子操作呢?
在ARMv6之前,swp指令就是通过锁定总线的方式完成原子的数据交换,但是影响系统性能。ARMv6之后,一般使用ldrex
和strex指令对代替swp指令的功能。
自旋锁中的原子操作
其中lock->slock字段初始值为1,执行原子操作decb后值为0。符号位为0,执行jns指令跳转到3,完成自旋锁的加锁。
当再次申请自旋锁时,执行原子操作decb后lock->slock值为-1。符号位为1,不执行jns指令。进入标签2,执行一组
nop指令后比较lock->slock是否小于等于0,如果小于等于0回到标签2进行循环(自旋)。否则跳转到标签1重新申请
自旋锁,直到申请成功。
自旋锁释放时会将lock->slock设置为1,这样保证了其他进程可以获得自旋锁。
信号量中的原子操作
信号量的申请操作由函数down实现
信号量的sem->count一般初始化为一个正整数,申请信号量时执行原子操作decl,将sem->count减1。如果该值减为负数
(符号位为1)则跳转到另一个段内的标签2,否则申请信号量成功。
标签2被编译到另一个段内,进入标签2后,执行lea指令取出sem->count的地址,放到eax寄存器作为参数,然后调用函数
__down_failed表示信号量申请失败,进程加入等待队列。最后跳回标签1结束信号量申请。
信号量的释放操作由函数up实现。
释放信号量时执行原子操作incl将sem->count加1,如果该值小于等于0,则说明等待队列有阻塞的进程需要唤醒,跳转到标签2,
否则信号量释放成功。
标签2被编译到另一个段内,进入标签2后,执行lea指令取出sem->count的地址,放到eax寄存器作为参数,然后调用函数__up_wakeup
唤醒等待队列的进程。最后跳回标签1结束信号量释放。
例子:
代码1(低优先级)(线程1)
代码1down
代码1down处理代码
代码1up
代码2(高优先级)(线程2)
代码2down
代码2down处理代码
代码2up
分析:
代码1down成功就运行代码1down处理代码,(假如代码2刚运行到代码2down,因没有得到信号量,所以被挂起了),代码1处理完
代码1down处理代码,到代码1up时会看有没有其它线程等待信号量(线程2等待着),所以系统恢复线程2进行运行,运行
代码2down处理代码,再运行到代码2up,发现没有就退出直到线程2不再运行,系统恢复线程1进行运行,运行到代码1up时被
系统挂起时下一条代码,再运行代码1其他代码直到再次被挂起。
原文参考:http://www.cnblogs.com/fanzhidongyzby/p/3654855.html
并发问题
例如C语言语句“count++;”在未经编译器优化时生成的汇编代码为多条机器指令来实现的。
例子:
假设count变量初始值为0。进程1执行完“mov eax, [count]”后,寄存器eax内保存了count的值0。此时,
进程2被调度执行,抢占了进程1的CPU的控制权。进程2执行“count++;”的汇编代码,将累加后的count值
1写回到内存。然后,进程1再次被调度执行,CPU控制权回到进程1。进程1接着执行,计算count的累加
值仍为1,写回到内存。虽然进程1和进程2执行了两次“count++;”操作,但是count实际的内存值为1,而
不是2!
单处理器原子操作
解决这个问题的方法是,将“count++;”语句翻译为单指令操作。
Intel x86指令集支持内存操作数的inc操作,这样“count++;”操作可以在一条指令内完成。因为进程的上下文
切换是在总是在一条指令执行完成后,所以不会出现上述的并发问题。对于单处理器来说,一条处理器指令就
是一个原子操作。
多处理器原子操作
但是在多处理器的环境下,例如SMP架构,这个结论不再成立。我们知道“inc [count]”指令的执行过程分为三步:
1)从内存将count的数据读取到cpu。
2)累加读取的值。
3)将修改的值写回count内存。
这又回到前面并发问题类似的情况,只不过此时并发的主题不再是进程,而是处理器。
Intel x86指令集提供了指令前缀lock用于锁定前端串行总线(FSB),保证了指令执行时不会受到其他处理器的干扰。
使用lock指令前缀后,处理器间对count内存的并发访问(读/写)被禁止,从而保证了指令的原子性。
arm原子操作实现
在arm的指令集中,不存在指令前缀lock,那如何完成原子操作呢?
在ARMv6之前,swp指令就是通过锁定总线的方式完成原子的数据交换,但是影响系统性能。ARMv6之后,一般使用ldrex
和strex指令对代替swp指令的功能。
自旋锁中的原子操作
1: lock decb [lock->slock] jns 3 2: rep nop cmpb $0, [lock->slock] jle 2 jmp 1 3:
其中lock->slock字段初始值为1,执行原子操作decb后值为0。符号位为0,执行jns指令跳转到3,完成自旋锁的加锁。
当再次申请自旋锁时,执行原子操作decb后lock->slock值为-1。符号位为1,不执行jns指令。进入标签2,执行一组
nop指令后比较lock->slock是否小于等于0,如果小于等于0回到标签2进行循环(自旋)。否则跳转到标签1重新申请
自旋锁,直到申请成功。
自旋锁释放时会将lock->slock设置为1,这样保证了其他进程可以获得自旋锁。
信号量中的原子操作
信号量的申请操作由函数down实现
lock decl [sem->count] js 2 1: <========== another section ==========> 2: lea [sem->count], eax call __down_failed jmp 1
信号量的sem->count一般初始化为一个正整数,申请信号量时执行原子操作decl,将sem->count减1。如果该值减为负数
(符号位为1)则跳转到另一个段内的标签2,否则申请信号量成功。
标签2被编译到另一个段内,进入标签2后,执行lea指令取出sem->count的地址,放到eax寄存器作为参数,然后调用函数
__down_failed表示信号量申请失败,进程加入等待队列。最后跳回标签1结束信号量申请。
信号量的释放操作由函数up实现。
lock incl sem->count jle 2 1: <========== another section ==========> 2: lea [sem->count], eax call __up_wakeup jmp 1
释放信号量时执行原子操作incl将sem->count加1,如果该值小于等于0,则说明等待队列有阻塞的进程需要唤醒,跳转到标签2,
否则信号量释放成功。
标签2被编译到另一个段内,进入标签2后,执行lea指令取出sem->count的地址,放到eax寄存器作为参数,然后调用函数__up_wakeup
唤醒等待队列的进程。最后跳回标签1结束信号量释放。
例子:
代码1(低优先级)(线程1)
代码1down
代码1down处理代码
代码1up
代码2(高优先级)(线程2)
代码2down
代码2down处理代码
代码2up
分析:
代码1down成功就运行代码1down处理代码,(假如代码2刚运行到代码2down,因没有得到信号量,所以被挂起了),代码1处理完
代码1down处理代码,到代码1up时会看有没有其它线程等待信号量(线程2等待着),所以系统恢复线程2进行运行,运行
代码2down处理代码,再运行到代码2up,发现没有就退出直到线程2不再运行,系统恢复线程1进行运行,运行到代码1up时被
系统挂起时下一条代码,再运行代码1其他代码直到再次被挂起。
原文参考:http://www.cnblogs.com/fanzhidongyzby/p/3654855.html
发表评论
-
windowx 命令
2020-10-23 17:35 283windowx 端口占用 netstat -aon|finds ... -
linux常用命令
2020-04-08 08:21 319zip压缩目录 zip -qr test.zip /usr/t ... -
Linux 是如何实现阻塞进程功能的原理
2019-01-12 11:42 1679Linux 是如何实现阻塞进程功能的原理 linux等 ... -
shell 语法
2017-09-09 10:59 425shell 语法 #!/bin/bash //#!告诉 ... -
Linux 命令使用集
2017-01-06 16:40 409Linux 命令使用集 //====== ... -
select、poll、epoll之间的区别总结
2016-09-06 14:46 1264select、poll、epoll之间的 ... -
IO - 同步,异步,阻塞,非阻塞,AIO
2016-09-06 14:43 705IO - 同步,异步,阻塞,非阻塞 阻塞与非阻塞(进程有没 ... -
日志文件系统
2016-09-06 10:14 551日志文件系统 日志文件系统当然要记录日志,而日志也需要占存储 ... -
Linux线程同步 读写锁 rwlock
2016-08-17 11:54 841读写锁比mutex有更高的适用性,可以多个线程同时占用读模式的 ...
相关推荐
在 Linux 下,内核中常用的同步机制包括:原子操作、自旋锁和信号量。 1. 原子操作 原子操作就是该操作绝不会在执行完毕前被任何其他任务或事件打断,也就说,它的最小的执行单位,不可能有比它更小的执行单位。...
2. Linux 内核同步机制:Linux 内核中有多种同步机制,包括信号量、自旋锁、原子操作和等待队列等。这些机制可以用于实现系统的同步,确保系统的正确性和可靠性。 3. 等待队列和异步信号:等待队列是一个基本的功能...
在Linux操作系统中,内核同步机制是确保多任务环境下内核数据结构一致性的重要手段。本文将深入探讨Linux内核2.4版本中的同步机制,包括其基本原理和实现的各种锁机制。 Linux操作系统支持并发执行多个任务,这在...
Linux 内核使用 atomic_t 数据结构来实现原子操作,atomic_add() 和 atomic_sub() 函数用于实现原子加法和原子减法操作。 在 semaphore 机制中,down() 函数的执行过程可以分为几步: 1. 将 semaphore 的 count ...
在Linux操作系统中,锁、原子操作和自旋锁是内核并发控制的重要机制,用于确保多线程环境下的数据一致性与正确性。这些概念在构建高效、可靠的并发程序时至关重要。 **1. Linux锁** Linux内核中的锁主要用于保护...
Linux内核同步机制是操作系统设计中的一个核心部分,它确保了多任务环境下对共享资源的安全访问。本文档详细介绍了Linux内核中常用的同步方法,特别是自旋锁(spinlock)在不同场景下的应用。 #### 二、自旋锁...
通过对Linux进程同步机制,尤其是信号量机制的研究,我们可以看到Linux在解决优先级翻转问题方面所做的努力。优先级继承协议的引入极大地改善了Linux的实时性能,使其能够更好地支持各种实时应用场景。随着技术的...
在Linux系统中,有多种同步机制,如原子操作、自旋锁和信号量,它们各有特点,用于不同的场景。这里我们将重点讨论自旋锁和信号量。 自旋锁是专为多处理器环境设计的一种轻量级锁定机制,它不允许持锁任务睡眠。...
对于任何希望深入理解Linux内核同步机制的开发者来说,这都是不可或缺的资源。 #### 二、两种主要类型的内核锁:自旋锁与信号量 在Linux内核中,有两种常用的锁机制:自旋锁(Spinlocks)和信号量(Semaphores)。 - ...
《【正点原子】I.MX6U 嵌入式 Linux C 应用编程指南 V1.4》 本资源为嵌入式 Linux C 应用编程指南,基于 I.MX6U 微处理器,旨在帮助开发者快速上手嵌入式 Linux 应用开发。该指南涵盖了嵌入式 Linux 的基本概念、...
总结,Linux上的多进程和多线程编程涉及复杂的同步互斥操作,开发者需要理解各种同步机制,根据具体需求选择合适的方法。通过阅读和学习提供的源代码,可以加深对这些概念的理解,提升实际开发能力。
Linux内核的同步机制在设计和实现中至关重要,因为它们确保了在多任务和多线程环境下,对共享数据的访问是安全和有序的。在Linux内核中,多种同步机制协同工作,以应对不同场景下的同步需求。下面将详细讨论其中的...
在深入讨论Linux操作系统编程,特别是多线程的互斥与同步控制及实践之前,首先需要了解Linux操作系统的基本概念以及多线程编程的基础知识。Linux是一种类Unix的操作系统,它支持多用户、多任务、多线程和多种硬件...
本文将详细介绍其中四种重要的同步机制——原子操作、信号量、读写信号量和自旋锁的API,这些机制对于开发高效稳定的多线程或多处理器系统非常关键。 #### 二、原子操作 **定义**: 原子操作是指一个不可中断的...
本文主要介绍Linux内核同步机制的概述,讨论了内核同步的必要性、竞争条件、临界区的概念、保护临界区的措施、原子操作等重要概念。 内核同步机制的必要性 在Linux内核中,存在多个任务并发执行,包括进程、中断、...
#### 三、信号量结构与操作 ##### 1. 信号量结构 信号量的内部结构在Linux内核中是通过`struct semaphore`来表示的,主要包括以下几个字段: - `atomic_t count`: 信号量的计数值,使用原子操作以确保线程安全。 - `...
Linux 提供了多种互斥机制,包括中断屏蔽、原子操作、信号量和自旋锁等。本文将详细介绍这些互斥机制的优缺点和使用方法。 1. 中断屏蔽 中断屏蔽是避免竞态的简单方法,即在进入临界区之前屏蔽系统的中断。这可以...
- `atomic_xchg()`: 用于交换操作,它会原子地将变量的值与提供的新值交换,并返回旧值。 2. **实现MIO资源竞争保护**: - 创建一个`atomic_t`变量,表示MIO资源的状态(例如,未使用或正在使用)。 - 在进程...