【Linux kernel】ACCESS_ONCE宏定义的解释

ACCESS_ONCE 是 Linux 内核中的一个宏,它可以用来保护内核代码中的共享数据。它可以保证在多处理器环境下对共享数据的访问是原子的,即不会被其他处理器中断或破坏。在多处理器环境下,对共享数据的并发访问可能会导致数据不一致或者数据丢失等问题,使用 ACCESS_ONCE 可以有效避免这些问题。

一、ACCESS_ONCE解决什么样的问题

我们首先来看一个代码片段(来自__mutex_lock_common函数,位于linux/kernel/locking/mutex.c文件):

例子1

for (;;) {
    struct task_struct *owner;

    owner = ACCESS_ONCE(lock->owner);
    if (owner && !mutex_spin_on_owner(lock, owner))
        goto slowpath;
     /* ... */
}

这段代码的逻辑比较简单,主要是为临界区加上mutext锁的操作,当已经有其他的thread持有锁的时候,调用本次加锁操作的进程有两个选择:

  1. spin直到获取到mutex锁
  2. sleep

不同的场景走不同的分支。如果该锁没有pending waiter并且持有锁的thread在另外一个CPU上运行。这种情况下,spin方案会比较好,因此该锁很可能在随后的短时间内被释放。否则,就需要走到slow path去了。因此,在spin的情况下,代码会不断的轮询lock->owner并判断是否owner还持有锁资源,如果当前的owner还占用该资源,那么继续polling,如果锁被释放了,那么程序可以获得锁资源并继续向下执行。c代码虽然好理解,不过c代码和CPU真正执行的代码指令之间还有一个编译器,有些编译器会不择手段的进行优化。例如,对于上面的代码,lock->owner这个变量在for循环中并没有被修改,那么编译器就认为CPU不需要在每一个循环中访问该变量,因此这段代码有可能被编译器优化成:

struct task_struct *owner;

owner = ACCESS_ONCE(lock->owner);

for (;;) {
    if (owner && !mutex_spin_on_owner(lock, owner))
        break;
     /* ... */
}

编译器其实没有那么智能,如果不告诉它,它在编译c代码的时候总是缺省认为只有一个thread在该地址空间中执行。因此,编译器并不知道lock->owner有可能被其他的thread修改的这个事实,因此作出错误的优化(对于single thread,这个优化无疑是正确的)。

在这个例子中,ACCESS_ONCE被c code用来告知编译器在编译的时候,不要因为优化而将多次内存访问合并称为一次。对lock->owner这个memory的访问要确保每个循环都access once。

我们再看另外一个代码片段的例子(来自Linus的邮件):

例子2 

  if (a > MEMORY) {
        do1;
        do2;
        do3;
    } else {
        do2;
    }

这段代码可能被编译器优化成:

    if (a > MEMORY)
        do1;
    do2;
    if (a > MEMORY)
        do3;

但是,对于优化后的代码,如果有其他的thread在执行do2的时候修改了MEMORY的值,这时候,CPU执行的代码逻辑和c code是不一致的,或许就会导致问题。

在这个例子中,ACCESS_ONCE这个名字解释就更好理解。也就是说,编译器在编译的时候,不要因为优化而将内存访问拆成多次,确保该内存访问是access once而不是access multi times。

综上所述,ACCESS_ONCE就是用来让c code告诉编译器,这里的内存访问不要优化(不要将多次内存访问合并,对应例子1,也不要将一次内存访问分拆成多次,对应例子2)。

二、代码实现

本身ACCESS_ONCE非常简单,如下:

#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))

ACCESS_ONCE本身的实现就是增加了volatile这个关键字,它确保编译器每次访问变量x都是从内存中获取。

ACCESS_ONCE宏定义的解释

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值