menu

多核抢占式linux原子操作atomic详解

1. 什么是原子操作

原子(atom)指化学反应不可再分的基本微粒,原子在化学反应中不可分割。

在操作系统中,用原子的这一特性形象生动的描述了一种操作——–原子操作,顾名思义,原子操作是不可分割的, 在执行完毕之前不被任何其它任务或事件中断或即便被中断但不影响最终结果的操作都可以称之为原子操作。

在单处理器系统(UniProcessor)中,能够在单条指令中完成的操作都可以认为是” 原子操作”,因为中断只能发生于指令之间;那么多条指令可不可以构成原子操作呢,当然可以,只要确保这几条指令执行时不被抢占就好了,关闭总中断就可以做到。

在多处理器SMP系统(Symmetric Multi-Processor)中,关中断可就不好使了,因为A处理器无法关闭它处理器的中断,A处理器只能关闭自己的中断。那么此时多条指令的执行还可以是” 原子 “的吗,其实也能,虽然被抢占是再所难免的,但只要保证被抢占者不会影响原执行者的最终结果不就好了。

2. 为什么要引入原子操作

先明确两个概念:同步和互斥

同步,又称直接制约关系,是指多个线程(或进程)为了合作完成任务,必须严格按照规定的 某种先后次序来运行。 互斥,又称间接制约关系,是指系统中的某些共享资源,一次只允许一个线程访问。当一个线程正在访问该临界资源时,其它线程必须等待。

3. 处理器实现的指令操作互斥

首先处理器会自动保证基本的内存操作的原子性。处理器保证从系统内存当中读取或者写入一个字节是原子的,意思是当一个处理器读取一个字节时,其他处理器不能访问这个字节的内存地址。

对于复杂的内存操作,处理器提供了总线锁定和缓存锁定两种机制来保证其原子性。 使用总线锁保证原子性:如果多个处理器同时对共享变量进行读该写(同时执行i++),其最终结果可能与预期不一样。i=1,两个CPU同时执行 i++操作,结果可能为 3,也可能为 2。对于”读改写”操作如果需要做到原子操作,必须保证 CPU1 操作共享变量时,CPU2 不能操作该变量。 处理器使用总线锁来解决这个问题,就是使用处理器提供的一个 LOCK# 信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞住,此时该处理器就可以独占共享内存。总线锁把 CPU 和内存之前通信锁住了,其代价比较大。

4. 使用缓存锁保证原子性:

在同一时刻我们只需要保证对某个内存地址的操作是原子的即可。频繁使用的内存会缓存在 L1,L2,L3高速缓存中,原子操作可以直接在处理器内部缓存中进行,并不需要声明总线锁。”缓存锁定”指如果缓存在处理器缓存行的内存区域在 LOCK 操作期间被锁定,当它执行所操作回写内存时,并不是在总线上声明 LOCK# 信号,而是修改内部的内存地址。缓存一致性机制获取该修改的内存地址,使用其保证内存区域数据不会同时被多个处理器缓存锁定。从而实现处理器对内存区域数据的互斥访问。

5. 有两种情况不会使用缓存锁定:

a、操作的数据不能被缓存在处理器内部,或操作的数据跨多个缓存行。b、有的处理器不支持缓存锁定。

6. Futex

Fast userspace mutex (快速用户空间互斥体)。Futex 由一块能够被多个进程共享的内存空间(一个对齐后的整型变量)组成,这个整型变量可以通过原子操作来增加或减少,并且一个进程可以等待直到正值出现。Futex 的作用在于减少系统调用的次数,来提高性能。

线程互斥锁 pthread_mutex_t 的实现原理:

pthread_mutex_lock:
    atomic_dec(pthread_mutex_t.value);  /* 初始 value 为 1 */
    if (pthread_mutex_t.value!=0)  /* 减为 0,表示没有使用者和竞争者 */
        futex(WAIT);
    else
        success;

pthread_mutex_unlock:
    atomic_inc(pthread_mutex_t.value)
    if (pthread_mutex_t.value != 1)
        futex(WAKEUP);  /* 将睡眠的线程都唤醒后,需要别的方式来知道谁获取到锁? */
    else
        success;

信号量 sem_t 的实现原理:

sem_wait(sem_t *sem)
{
    for(;;){
        if (atomic_decrement_if_positive(sem->count))
            break;
        futex_wait(&sem->count, 0);
    }
}

sem_post(sem_t *sem)
{
    n = atomic_increment(sem->count);
    futex_wake(&sem->count, n+1);
}

更多文章 请访问havte官网

havte官网

关注微博 微信公众号

随时随地 想看就看

微信搜索公众号: havte
wechat 微信公众号:havte
微博搜索: havte官方微博
微博账号:havte官方微博