Linux内核同步原语之原子操作
避免对同一数据的并发访问(通常由中断、对称多处理器、内核抢占等引起)称为同步。
——题记
内核源码:Linux-2.6.38.8.tar.bz2
目标平台:ARM体系结构
原子操作确保对同一数据的“读取-修改-写入”操作在它的执行期间不会被打断,要么全部执行完成,要么根本不会执行。例如在ARM上对全局变量的++运算至少要经历以下三步:
- ldr r3, [r3, #0]
- add r2, r3, #1
- str r2, [r3, #0]
ldr r3, [r3, #0]
add r2, r3, #1
str r2, [r3, #0]
这就给并发访问制造了可能,所以在编写内核代码时需要给有可能被并发访问的变量加上原子操作。
Linux内核提供了两组原子操作接口,一组用于整数,一组用于位操作。
1、原子整数操作
针对整数的原子操作只能对atomic_t类型的数据进行处理。atomic_t定义如下:
- /* linux-2.6.38.8/include/linux/types.h */
- typedef struct {
- int counter;
- } atomic_t;
/* linux-2.6.38.8/include/linux/types.h */
typedef struct {
int counter;
} atomic_t;
下面将详细分析所有原子整数操作函数在ARMv6之前各种CPU上的实现(定义在linux-2.6.38.8/arch/arm/include/asm/atomic.h文件中)。
- #define ATOMIC_INIT(i) { (i) }
#define ATOMIC_INIT(i) { (i) }
ATOMIC_INIT用于把atomic_t变量初始化为i,如下例把a初始化为2:
- atomic_t a = ATOMIC_INIT(2);
atomic_t a = ATOMIC_INIT(2);
- #define atomic_read(v) (*(volatile int *)&(v)->counter)
- #define atomic_set(v,i) (((v)->counter) = (i))
#define atomic_read(v) (*(volatile int *)&(v)->counter)
#define atomic_set(v,i) (((v)->counter) = (i))
atomic_read(v)用于读取atomic_t变量*v的值,而atomic_set(v,i)用于把atomic_t变量*v设置为i。
- static inline int atomic_add_return(int i, atomic_t *v)
- {
- unsigned long flags;
- int val;
- raw_local_irq_save(flags);
- val = v->counter;
- v->counter = val += i;
- raw_local_irq_restore(flags);
- return val;
- }
- #define atomic_add(i, v) (void) atomic_add_return(i, v)
static inline int atomic_add_return(int i, atomic_t *v)
{
unsigned long flags;
int val; raw_local_irq_save(flags);
val = v->counter;
v->counter = val += i;
raw_local_irq_restore(flags); return val;
}
#define atomic_add(i, v) (void) atomic_add_return(i, v)
atomic_add(i, v)用于把atomic_t变量*v加i。这里的原子性实现只是简单地通过raw_local_irq_save函数来禁止中断。
- /* linux-2.6.38.8/include/linux/irqflags.h */
- #define raw_local_irq_save(flags) \
- do { \
- typecheck(unsigned long, flags); \
- flags = arch_local_irq_save(); \
- } while (0)
- /* linux-2.6.38.8/arch/arm/include/asm/irqflags.h */
- static inline unsigned long arch_local_irq_save(void)
- {
- unsigned long flags, temp;
- asm volatile(
- " mrs %0, cpsr @ arch_local_irq_save\n"
- " orr %1, %0, #128\n"
- " msr cpsr_c, %1"
- : "=r" (flags), "=r" (temp)
- :
- : "memory", "cc");
- return flags;
- }
/* linux-2.6.38.8/include/linux/irqflags.h */
#define raw_local_irq_save(flags) \
do { \
typecheck(unsigned long, flags); \
flags = arch_local_irq_save(); \
} while (0)
/* linux-2.6.38.8/arch/arm/include/asm/irqflags.h */
static inline unsigned long arch_local_irq_save(void)
{
unsigned long flags, temp; asm volatile(
" mrs %0, cpsr @ arch_local_irq_save\n"
" orr %1, %0, #128\n"
" msr cpsr_c, %1"
: "=r" (flags), "=r" (temp)
:
: "memory", "cc");
return flags;
}
typecheck函数用来确保参数flags的数据类型为unsigned long,arch_local_irq_save函数通过内嵌汇编设置当前程序状态寄存器的I位为1,从而禁止IRQ。待加操作完成后,再通过raw_local_irq_restore函数使能中断。
- static inline int atomic_sub_return(int i, atomic_t *v)
- {
- unsigned long flags;
- int val;
- raw_local_irq_save(flags);
- val = v->counter;
- v->counter = val -= i;
- raw_local_irq_restore(flags);
- return val;
- }
- #define atomic_sub(i, v) (void) atomic_sub_return(i, v)
static inline int atomic_sub_return(int i, atomic_t *v)
{
unsigned long flags;
int val; raw_local_irq_save(flags);
val = v->counter;
v->counter = val -= i;
raw_local_irq_restore(flags); return val;
}
#define atomic_sub(i, v) (void) atomic_sub_return(i, v)
atomic_sub(i, v)用于把atomic_t变量*v减i。
- #define atomic_inc(v) atomic_add(1, v)
- #define atomic_dec(v) atomic_sub(1, v)
- #define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0)
- #define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0)
- #define atomic_inc_return(v) (atomic_add_return(1, v))
- #define atomic_dec_return(v) (atomic_sub_return(1, v))
- #define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0)
- #define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0)
#define atomic_inc(v) atomic_add(1, v)
#define atomic_dec(v) atomic_sub(1, v) #define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0)
#define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0)
#define atomic_inc_return(v) (atomic_add_return(1, v))
#define atomic_dec_return(v) (atomic_sub_return(1, v))
#define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0) #define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0)
这些函数只是上面所讲函数的封装。
- static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
- {
- int ret;
- unsigned long flags;
- raw_local_irq_save(flags);
- ret = v->counter;
- if (likely(ret == old))
- v->counter = new;
- raw_local_irq_restore(flags);
- return ret;
- }
- static inline int atomic_add_unless(atomic_t *v, int a, int u)
- {
- int c, old;
- c = atomic_read(v);
- while (c != u && (old = atomic_cmpxchg((v), c, c + a)) != c)
- c = old;
- return c != u;
- }
- #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
{
int ret;
unsigned long flags; raw_local_irq_save(flags);
ret = v->counter;
if (likely(ret == old))
v->counter = new;
raw_local_irq_restore(flags); return ret;
}
static inline int atomic_add_unless(atomic_t *v, int a, int u)
{
int c, old; c = atomic_read(v);
while (c != u && (old = atomic_cmpxchg((v), c, c + a)) != c)
c = old;
return c != u;
}
#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
atomic_inc_not_zero(v)用于将atomic_t变量*v加1,并测试加1后的*v是否不为零,如果不为零则返回真。
注意,这里所讲函数的参数v都是atomic_t类型数据的地址。
2、原子位操作
在CONFIG_SMP和__ARMEB__都未定义的情况下,原子位操作相关函数的定义如下:
- /* linux-2.6.38.8/arch/arm/include/asm/bitops.h */
- #define ATOMIC_BITOP_LE(name,nr,p) \
- (__builtin_constant_p(nr) ? \
- ____atomic_##name(nr, p) : \
- _##name##_le(nr,p))
- #define set_bit(nr,p) ATOMIC_BITOP_LE(set_bit,nr,p)
- #define clear_bit(nr,p) ATOMIC_BITOP_LE(clear_bit,nr,p)
- #define change_bit(nr,p) ATOMIC_BITOP_LE(change_bit,nr,p)
- #define test_and_set_bit(nr,p) ATOMIC_BITOP_LE(test_and_set_bit,nr,p)
- #define test_and_clear_bit(nr,p) ATOMIC_BITOP_LE(test_and_clear_bit,nr,p)
- #define test_and_change_bit(nr,p) ATOMIC_BITOP_LE(test_and_change_bit,nr,p)
/* linux-2.6.38.8/arch/arm/include/asm/bitops.h */
#define ATOMIC_BITOP_LE(name,nr,p) \
(__builtin_constant_p(nr) ? \
____atomic_##name(nr, p) : \
_##name##_le(nr,p)) #define set_bit(nr,p) ATOMIC_BITOP_LE(set_bit,nr,p)
#define clear_bit(nr,p) ATOMIC_BITOP_LE(clear_bit,nr,p)
#define change_bit(nr,p) ATOMIC_BITOP_LE(change_bit,nr,p)
#define test_and_set_bit(nr,p) ATOMIC_BITOP_LE(test_and_set_bit,nr,p)
#define test_and_clear_bit(nr,p) ATOMIC_BITOP_LE(test_and_clear_bit,nr,p)
#define test_and_change_bit(nr,p) ATOMIC_BITOP_LE(test_and_change_bit,nr,p)
相对应的非原子位操作函数定义在linux-2.6.38.8/include/asm-generic/bitops/non-atomic.h文件中,它们的名字前都多带有两个下划线,如set_bit的非原子操作函数为__set_bit。
原子位操作函数根据参数nr是否为常量分为两种实现方式:
(1)、nr为常量时
- /* linux-2.6.38.8/arch/arm/include/asm/bitops.h */
- static inline void ____atomic_set_bit(unsigned int bit, volatile unsigned long *p)
- {
- unsigned long flags;
- unsigned long mask = 1UL << (bit & 31);
- p += bit >> 5;
- raw_local_irq_save(flags);
- *p |= mask;
- raw_local_irq_restore(flags);
- }
- static inline void ____atomic_clear_bit(unsigned int bit, volatile unsigned long *p)
- {
- unsigned long flags;
- unsigned long mask = 1UL << (bit & 31);
- p += bit >> 5;
- raw_local_irq_save(flags);
- *p &= ~mask;
- raw_local_irq_restore(flags);
- }
- static inline void ____atomic_change_bit(unsigned int bit, volatile unsigned long *p)
- {
- unsigned long flags;
- unsigned long mask = 1UL << (bit & 31);
- p += bit >> 5;
- raw_local_irq_save(flags);
- *p ^= mask;
- raw_local_irq_restore(flags);
- }
/* linux-2.6.38.8/arch/arm/include/asm/bitops.h */
static inline void ____atomic_set_bit(unsigned int bit, volatile unsigned long *p)
{
unsigned long flags;
unsigned long mask = 1UL << (bit & 31); p += bit >> 5; raw_local_irq_save(flags);
*p |= mask;
raw_local_irq_restore(flags);
}
static inline void ____atomic_clear_bit(unsigned int bit, volatile unsigned long *p)
{
unsigned long flags;
unsigned long mask = 1UL << (bit & 31); p += bit >> 5; raw_local_irq_save(flags);
*p &= ~mask;
raw_local_irq_restore(flags);
}
static inline void ____atomic_change_bit(unsigned int bit, volatile unsigned long *p)
{
unsigned long flags;
unsigned long mask = 1UL << (bit & 31); p += bit >> 5; raw_local_irq_save(flags);
*p ^= mask;
raw_local_irq_restore(flags);
}
在32位机上,参数bit的理想取值为0到31,例如取值为1,就表示对*p的bit[1]进行操作。
当bit取值大于31时,函数操作的就不是你想要操作的那个变量*p了(通过p += bit>> 5;语句实现),所以在实际的应用中应该要确保bit的取值在合理的范围内。
*p |= mask;语句实现bit位的置位。
*p &= ~mask;语句实现bit位的清零。
*p ^= mask;语句实现bit位的翻转,即*p的bit位为0时被置位,为1时则被清零。
- static inline int
- ____atomic_test_and_set_bit(unsigned int bit, volatile unsigned long *p)
- {
- unsigned long flags;
- unsigned int res;
- unsigned long mask = 1UL << (bit & 31);
- p += bit >> 5;
- raw_local_irq_save(flags);
- res = *p;
- *p = res | mask;
- raw_local_irq_restore(flags);
- return (res & mask) != 0;
- }
- static inline int
- ____atomic_test_and_clear_bit(unsigned int bit, volatile unsigned long *p)
- {
- unsigned long flags;
- unsigned int res;
- unsigned long mask = 1UL << (bit & 31);
- p += bit >> 5;
- raw_local_irq_save(flags);
- res = *p;
- *p = res & ~mask;
- raw_local_irq_restore(flags);
- return (res & mask) != 0;
- }
- static inline int
- ____atomic_test_and_change_bit(unsigned int bit, volatile unsigned long *p)
- {
- unsigned long flags;
- unsigned int res;
- unsigned long mask = 1UL << (bit & 31);
- p += bit >> 5;
- raw_local_irq_save(flags);
- res = *p;
- *p = res ^ mask;
- raw_local_irq_restore(flags);
- return (res & mask) != 0;
- }
static inline int
____atomic_test_and_set_bit(unsigned int bit, volatile unsigned long *p)
{
unsigned long flags;
unsigned int res;
unsigned long mask = 1UL << (bit & 31); p += bit >> 5; raw_local_irq_save(flags);
res = *p;
*p = res | mask;
raw_local_irq_restore(flags); return (res & mask) != 0;
}
static inline int
____atomic_test_and_clear_bit(unsigned int bit, volatile unsigned long *p)
{
unsigned long flags;
unsigned int res;
unsigned long mask = 1UL << (bit & 31); p += bit >> 5; raw_local_irq_save(flags);
res = *p;
*p = res & ~mask;
raw_local_irq_restore(flags); return (res & mask) != 0;
}
static inline int
____atomic_test_and_change_bit(unsigned int bit, volatile unsigned long *p)
{
unsigned long flags;
unsigned int res;
unsigned long mask = 1UL << (bit & 31); p += bit >> 5; raw_local_irq_save(flags);
res = *p;
*p = res ^ mask;
raw_local_irq_restore(flags); return (res & mask) != 0;
}
这三个函数增加了return (res & mask) != 0;语句,用来判断*p的bit位原值是否为1,如果为1则函数返回1,否则返回0。
(2)、nr不为常量时
当nr不为常量时,原子位操作函数的定义如下:
- /* linux-2.6.38.8/arch/arm/include/asm/bitops.h */
- extern void _set_bit_le(int nr, volatile unsigned long * p);
- extern void _clear_bit_le(int nr, volatile unsigned long * p);
- extern void _change_bit_le(int nr, volatile unsigned long * p);
- extern int _test_and_set_bit_le(int nr, volatile unsigned long * p);
- extern int _test_and_clear_bit_le(int nr, volatile unsigned long * p);
- extern int _test_and_change_bit_le(int nr, volatile unsigned long * p);
/* linux-2.6.38.8/arch/arm/include/asm/bitops.h */
extern void _set_bit_le(int nr, volatile unsigned long * p);
extern void _clear_bit_le(int nr, volatile unsigned long * p);
extern void _change_bit_le(int nr, volatile unsigned long * p);
extern int _test_and_set_bit_le(int nr, volatile unsigned long * p);
extern int _test_and_clear_bit_le(int nr, volatile unsigned long * p);
extern int _test_and_change_bit_le(int nr, volatile unsigned long * p);
它们都是通过汇编语言来实现的,定义如下:
- /* linux-2.6.38.8/arch/arm/lib/setbit.S */
- ENTRY(_set_bit_le)
- bitop orr
- ENDPROC(_set_bit_le)
- /* linux-2.6.38.8/arch/arm/lib/clearbit.S */
- ENTRY(_clear_bit_le)
- bitop bic
- ENDPROC(_clear_bit_le)
- /* linux-2.6.38.8/arch/arm/lib/changebit.S */
- ENTRY(_change_bit_le)
- bitop eor
- ENDPROC(_change_bit_le)
- /* linux-2.6.38.8/arch/arm/lib/testsetbit.S */
- ENTRY(_test_and_set_bit_le)
- testop orreq, streqb
- ENDPROC(_test_and_set_bit_le)
- /* linux-2.6.38.8/arch/arm/lib/testclearbit.S */
- ENTRY(_test_and_clear_bit_le)
- testop bicne, strneb
- ENDPROC(_test_and_clear_bit_le)
- /* linux-2.6.38.8/arch/arm/lib/testchangebit.S */
- ENTRY(_test_and_change_bit_le)
- testop eor, strb
- ENDPROC(_test_and_change_bit_le)
/* linux-2.6.38.8/arch/arm/lib/setbit.S */
ENTRY(_set_bit_le)
bitop orr
ENDPROC(_set_bit_le) /* linux-2.6.38.8/arch/arm/lib/clearbit.S */
ENTRY(_clear_bit_le)
bitop bic
ENDPROC(_clear_bit_le) /* linux-2.6.38.8/arch/arm/lib/changebit.S */
ENTRY(_change_bit_le)
bitop eor
ENDPROC(_change_bit_le) /* linux-2.6.38.8/arch/arm/lib/testsetbit.S */
ENTRY(_test_and_set_bit_le)
testop orreq, streqb
ENDPROC(_test_and_set_bit_le) /* linux-2.6.38.8/arch/arm/lib/testclearbit.S */
ENTRY(_test_and_clear_bit_le)
testop bicne, strneb
ENDPROC(_test_and_clear_bit_le) /* linux-2.6.38.8/arch/arm/lib/testchangebit.S */
ENTRY(_test_and_change_bit_le)
testop eor, strb
ENDPROC(_test_and_change_bit_le)
使用ENTRY和ENDPROC两个宏来定义一个名为name的函数,如下所示:
- /* linux-2.6.38.8/include/linux/linkage.h */
- #define ENTRY(name) \
- .globl name; \
- ALIGN; \
- name:
- #define ALIGN __ALIGN
- #define END(name) \
- .size name, .-name
- /* linux-2.6.38.8/arch/arm/include/asm/linkage.h */
- #define __ALIGN .align 0
- #define ENDPROC(name) \
- .type name, %function; \
- END(name)
/* linux-2.6.38.8/include/linux/linkage.h */
#define ENTRY(name) \
.globl name; \
ALIGN; \
name: #define ALIGN __ALIGN #define END(name) \
.size name, .-name /* linux-2.6.38.8/arch/arm/include/asm/linkage.h */
#define __ALIGN .align 0 #define ENDPROC(name) \
.type name, %function; \
END(name)
而汇编代码实现的宏bitop和testop被相应函数所调用,并传递给它们相应的参数,代码如下所示:
- /* linux-2.6.38.8/arch/arm/lib/bitops.h */
- .macro bitop, instr
- and r2, r0, #7
- mov r3, #1
- mov r3, r3, lsl r2
- save_and_disable_irqs ip
- ldrb r2, [r1, r0, lsr #3]
- \instr r2, r2, r3
- strb r2, [r1, r0, lsr #3]
- restore_irqs ip
- mov pc, lr
- .endm
- .macro testop, instr, store
- add r1, r1, r0, lsr #3
- and r3, r0, #7
- mov r0, #1
- save_and_disable_irqs ip
- ldrb r2, [r1]
- tst r2, r0, lsl r3
- \instr r2, r2, r0, lsl r3
- \store r2, [r1]
- moveq r0, #0
- restore_irqs ip
- mov pc, lr
- .endm
Linux内核同步原语之原子操作的更多相关文章
- Linux内核同步原语之原子操作【转】
转自:http://blog.csdn.net/npy_lp/article/details/7262388 避免对同一数据的并发访问(通常由中断.对称多处理器.内核抢占等引起)称为同步. ——题记 ...
- Linux内核同步机制之原子操作
1.前言 原子操作指的是该操作不会在执行完毕之前被任何其它任务或事件打断,它是最小的执行单位,不会有比它更小的执行单位,原子实际上使用了物理学中物质微粒的概念,在Linux内核中,原子操作需要硬件的支 ...
- Linux内核同步机制--转发自蜗窝科技
Linux内核同步机制之(一):原子操作 http://www.wowotech.net/linux_kenrel/atomic.html 一.源由 我们的程序逻辑经常遇到这样的操作序列: 1.读一个 ...
- Linux内核同步
Linux内核剖析 之 内核同步 主要内容 1.内核请求何时以交错(interleave)的方式执行以及交错程度如何. 2.内核所实现的基本同步机制. 3.通常情况下如何使用内核提供的同步机制. 内核 ...
- Linux内核同步 - spin_lock
一.前言 在linux kernel的实现中,经常会遇到这样的场景:共享数据被中断上下文和进程上下文访问,该如何保护呢?如果只有进程上下文的访问,那么可以考虑使用semaphore或者mutex的锁机 ...
- Linux内核同步机制之(四):spin lock【转】
转自:http://www.wowotech.net/kernel_synchronization/spinlock.html 一.前言 在linux kernel的实现中,经常会遇到这样的场景:共享 ...
- [内核同步]浅析Linux内核同步机制
转自:http://blog.csdn.net/fzubbsc/article/details/37736683?utm_source=tuicool&utm_medium=referral ...
- Linux内核同步机制
http://blog.csdn.net/bullbat/article/details/7376424 Linux内核同步控制方法有很多,信号量.锁.原子量.RCU等等,不同的实现方法应用于不同的环 ...
- 浅析Linux内核同步机制
非常早之前就接触过同步这个概念了,可是一直都非常模糊.没有深入地学习了解过,最近有时间了,就花时间研习了一下<linux内核标准教程>和<深入linux设备驱动程序内核机制>这 ...
随机推荐
- 转---秒杀多线程第十二篇 多线程同步内功心法——PV操作上 (续)
PV操作的核心就是 PV操作可以同时起到同步与互斥的作用. 1.同步就是通过P操作获取信号量,V操作释放信号量来进行. 2.互斥其实就是,同时操作P操作,结束后进行V操作即可做到. Java上实现PV ...
- 深入理解JVM一类加载器原理
我们知道我们编写的java代码,会经过编译器编译成字节码文件(class文件),再把字节码文件装载到JVM中,映射到各个内存区域中,我们的程序就可以在内存中运行了.那么字节码文件是怎样装载到JVM中的 ...
- BZOJ2716:[Violet 3]天使玩偶——题解
http://www.lydsy.com/JudgeOnline/problem.php?id=2716 样例输入 2 31 12 32 1 21 3 32 4 2 样例输出 1 2 ———————— ...
- 洛谷 P4027 [NOI2007]货币兑换 解题报告
P4027 [NOI2007]货币兑换 题目描述 小 \(Y\) 最近在一家金券交易所工作.该金券交易所只发行交易两种金券:\(A\) 纪念券(以下简称 \(A\) 券)和 \(B\) 纪念券(以下简 ...
- golang字符串常用函数
package utils import "fmt" import "strconv" import "strings" var str s ...
- go的web框架beego
安装 beego 包含一些示例应用程序以帮您学习并使用 beego 应用框架. 您需要安装 Go 1.1+ 以确保所有功能的正常使用. 你需要安装或者升级 Beego 和 Bee 的开发工具: $ g ...
- Codeforces Round #416 (Div. 2)A B C 水 暴力 dp
A. Vladik and Courtesy time limit per test 2 seconds memory limit per test 256 megabytes input stand ...
- python 分享文件
http://note.youdao.com/noteshare?id=1787e8bf3a71fca16005ece3e7fffb6c
- 题解【bzoj2440 [中山市选2011]完全平方数】
Description 求第 \(k\) 个不含平方因子的正整数.多组询问.\(k \leq 10^9, T \leq 50\) Solution 网上的题解几乎都是容斥,这里给一个简单的也挺快的做法 ...
- update condition 字段报错
mysql> update tf_user_present set condition="0" where id=1;ERROR 1064 (42000): You have ...