1. 基本作用
在各类操作系统提供的接口中,除了我们之前学习的互斥锁和信号量类接口用于实现互斥或者同步的目的外,也基本都会提供一类原子操作的接口,一般接口都带有atomic_xxx字样。原子操作类接口一般用于某个变量需要改变的情况,比如变量值需要增加、减少或者逻辑运算等等,但是如果这些操作是用高级语言编写的,比如C语言等,一句简单的“count++”被编译器编译后可能会生成n条指令来完成这一个操作,那么在这n条指令执行过程中是不能被打断的,如果被打断就有可能造成程序上的错误,这时我们就可以用之前学习的互斥锁和自旋锁来保护这个“count++”过程,保证其不被打断。现代处理器针对这些运算都基本提供了专门的指令,可以保证指令在执行过程中不会被打断,也就是效果是“原子性”的,因为在以前的物理认识中,原子是不可再分的最小物质,当然现在我们知道有比原子更小的物质,但是由于历史原因,atomic这个叫法就这么一直延续下来了。
对于初学者而言,可能会对原子操作产生困扰,因为完全可以用互斥锁和自旋锁实现和原子操作相同的效果,那为啥还要单独再提供这么个接口,这也是笔者刚学习时的疑问。其实在软件实现上,的确可以完全用互斥锁和自旋锁实现和原子操作相同的效果,但是使用原子接口没有互斥锁带来的调度开销和自旋锁带来的cpu“死等”浪费,也就是性能比前两者要好。但是原子操作可以运用的场景也比较有限,也就是针对一些变量的数学计算和逻辑运算这类场景,而互斥锁和自旋锁可以用于其他很多要资源保护的场景。
2.原子操作相关接口
2.1 原子量设置接口
在SylixOS下,原子量都是用atomic_t 类型来定义的,原子量初始值一般都是通过API_AtomicSet 接口来设置的,如下所示。
LW_API VOID API_AtomicSet(INT iVal, atomic_t *patomic);
- iVal:要设置的原子量值。
- patomic:原子量变量地址。
该接口没有返回值,调用完毕之后原子量就变为要设置的数值了。
2.2 原子量获取接口
通过API_AtomicGet 接口可以获取当前原子量的值,如下所示。
LW_API INT API_AtomicGet(atomic_t *patomic);
- patomic:原子量变量地址。
成功返回原子量当前值,失败返回-1。
2.3 原子量增加数值接口
如果想将原子量的值加上一个数值,可以使用API_AtomicAdd 接口,如下所示。
LW_API INT API_AtomicAdd(INT iVal, atomic_t *patomic);
- iVal:要增加的数值。
- patomic:原子量变量地址。
成功返回原子量原来的值,失败返回-1。如果只想将原子量的数值加一的话,可以使用API_AtomicInc 接口,如下所示。
LW_API INT API_AtomicInc(atomic_t *patomic);
- patomic:原子量变量地址。
成功返回原子量原来的值,失败返回-1。
2.4 原子量减少数值接口
如果想将原子量的值减少一个数值,可以使用API_AtomicSub 接口,如下所示。
LW_API INT API_AtomicSub(INT iVal, atomic_t *patomic);
- iVal:要增加的数值。
- patomic:原子量变量地址。
成功返回原子量原来的值,失败返回-1。如果只想将原子量的数值减一的话,可以使用API_AtomicDec 接口,如下所示。
LW_API INT API_AtomicDec(atomic_t *patomic);
- patomic:原子量变量地址。
成功返回原子量原来的值,失败返回-1。
2.5 原子量交换接口
如果想将原子量设置为新数值同时还要获取其原来的值,可以使用API_AtomicSwp 接口,如下所示。
LW_API INT API_AtomicSwp(INT iVal, atomic_t *patomic);
- iVal:要设置的新数值。
- patomic:原子量变量地址。
成功返回原子量原来的值,失败返回-1。
3. 接口使用示例
由于原子量的接口比较简单,基本看接口名就能知道是咋用的了,这里就不在举例子了。关于原子量还有其他一些接口,具体可以参见内核源码SylixOS/kernel/include/k_api.h。
4. 注意事项
- 原子量操作在移植Linux下的驱动时会经常遇到,但是Linux下原子量操作接口的返回值和SylixOS下相应接口返回值可能代表的意义并不相同,这个在实际移植时要注意。