ACCESS_ONCE宏定义的解释

转载于: http://www.wowotech.net/process_management/access-once.html

原文中文字的设置字号比较小,转载于此.

一、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都是从内存中获取。

 

转载于:https://www.cnblogs.com/tureno/articles/6151363.html

SOC_SINGLE是ALSA(Advanced Linux Sound Architecture)中用于定义单通道控制元素的宏定义。它用于简化定义单通道控制元素的过程。 要使用SOC_SINGLE宏定义,你需要在ALSA驱动程序中定义一个snd_kcontrol_new结构体,并使用SOC_SINGLE进行设置。以下是一个示例: ```c #include <sound/soc.h> // 定义一个名为"Volume Control"的SOC_SINGLE控制 static const struct snd_kcontrol_new volume_control = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, // 接口类型为混音器 .name = "Volume Control", // 控制元素名称 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,// 访问权限为读写 .info = snd_soc_info_volsw, // 获取信息的回调函数 .get = snd_soc_get_volsw, // 获取值的回调函数 .put = snd_soc_put_volsw, // 设置值的回调函数 }; ``` 在上面的示例中,我们使用SOC_SINGLE宏定义来设置snd_kcontrol_new结构体的type字段,将其设置为SOC_SINGLE。 SOC_SINGLE控制类型适用于只有一个通道(Single Channel)的控制元素,通常用于控制音频设备的音量、平衡等参数。在示例中,我们创建了一个名为"Volume Control"的SOC_SINGLE控制,并设置了接口类型、访问权限以及相应的信息获取和设置回调函数。 请注意,以上示例仅演示了如何使用SOC_SINGLE宏定义来定义一个SOC_SINGLE控制元素,并设置相应的参数。在实际应用中,你需要根据具体需求编写相应的回调函数,并适配你的驱动程序和硬件平台。 同时,还需要进行适当的配置和注册,以便将该SOC_SINGLE控制添加到ALSA框架中,使其可以被应用程序调用和操作。具体配置和注册细节会因你的应用场景和硬件平台而有所不同。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值