Linux-ARM 原子操作的实现
单 cpu 系统
在单个cpu的系统中,原子操作是通过关闭中断实现的。
关闭中断可以避免进程和中断,进程和进程之间的资源共享访问问题(进程的调度程序依靠中断实现调度)。linux怎么实现进程调度?linux进程调度的策略?
smp 系统
在smp系统中,仅仅关闭当前的 cpu中断无法避免两个cpu之间的资源竞争,而且关闭中断对系统的实时性有影响。smp系统中原子操作是通过一个程序“独占监测”机制来实现,在smp系统中原子操作是可以被同一个cpu中的线程和中断打断,也可以被其他cpu的线程打断,但是“独占监测机制保证的是两个所谓的原子操作成功之后的结果是确定的,操作结果不会是一个中间值”。“独占监测”机制的实现依赖于新增加的两条汇编指令(ldrex 和 strex)和两个monitor(Local monitor 和 Global monitor)。
ldrex : 将内存a中的数加载到寄存器中,并且对该内存的 Global monitor 标志为当前 cpu0 正在访问,将 cpu0 的 Local monitor 标志为 cpu0 正在访问内存a
strex : 检查当前 cpu0 的 Local monitor 是否在访问内存a,Global monitor 是否标志为内存a被cpu0访问。如果是则 strex 指令执行成功,否则 strex 指令执行失败。
内核对于arm v6及以上的 cpu 的 atomic_add()函数的实现如下:
static inline void atomic_add(int i, atomic_t *v)
{
unsigned long tmp;
int result;
__asm__ __volatile__("@ atomic_add\n"
"1: ldrex %0, [%3]\n"
" add %0, %0, %4\n"
" strex %1, %0, [%3]\n"
" teq %1, #0\n"
" bne 1b"
: "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
: "r" (&v->counter), "Ir" (i)
: "cc");
}
在smp系统中原子操作需要考虑:
- cpu0多个线程对同一个内存的访问
- 单个cpu中进程和中断对同一个内存的访问
- cpu0的中断服务程序和cpu1进程对同一个内存的访问
cpu0 中多个线程对同一个内存的访问
同一个cpu的线程0,线程1都执行atomic_add(a);
考虑以下情形:
- 线程0执行 ldrex 指令,将a的内存拷贝到 cpu0 的寄存器中,将cpu0 Local monitor 标记为cpu0 对a的内存访问,将 Global monitor 标记为被 cpu0 访问。
- 线程0被线程1抢占,线程1执行 ldrex ,清除 cpu0 的 Local monitor 的标记,将 Local monitor 标记为对内存a的访问,将 Global monitor 中内存a 标记为 cpu0 独占访问。将内存a中的数拷贝到寄存器。
- 线程1对自加寄存器执行自加操作。
- 线程1执行 strex 指令,strex 指令会检查当前 cpu Local monitor 是否对内存a访问,检查 Glocal monitor 中内存a是否被cpu0独占访问,检查为真,strex 指令执行成功,清除 cpu0 的 Local monitor,清除 Glocal monitor。
- 线程0 执行 strex 指令,strex 指令会检查当前 cpu0 Local monitor 是否对内存a进行访问,Global monitor 标记中内存a是否被 cpu0 独占访问。检查标记后判断为否,strex 指令执行失败。
- 线程0重新执行 atomic_add(a);
其他的两种情况与上面的其实是相同的。
有一个疑问???不是说原子操作是一个或者一系列不可被打断的操作吗?但是根据上面的分析,在smp系统中一个线程执行atomic_add(a)是可以被本cpu中的其他进程或者中断打断,也可以被其他cpu中的进程打断?