1.并发概念
多任务同时访问同一个共享数据段
例子:A、B、C任务,同时对共享数据段S进行访问
A任务对S写入3000个字符"a",B任务对S写入3000个字符"b",C任务负责读取所有数据,当执行顺序如下时,A将数据从用户态拷贝完成后,还未累加当前访问下标,B便对数据进行了访问,此后C获取数据,获取到的数据并不是3000个字符"a",而是3000个字符"b"。
2.原子操作
2.1概念
原子操作:不能进一步分割的操作
即使是一句简单的赋值操作,例如a=3,其实也并不是一个原子操作,对应的汇编代码是
ldr r0, =0X30000000 /* 变量 a 地址 */
ldr r1, = 3 /* 要写入的值 */
str r1, [r0] /* 将 3 写入到 a 变量中 */
而当两个任务,同时执行a=3时,可能出现如下的情况,变量最终被设置为20,而不是期望的10
而如果使用原子操作API,则不会出现上述情况。例如我们的代码中,需要用到一个int类型的标志位,当多线程都需要访问这个标志位时,就会有并发问题。如果使用锁较麻烦,而原子操作可以非常好的实现需求。
Linux定义atomic_t结构体完成整形数据的原子操作,定义在 **include/linux/types.h **文件中
typedef struct {
int counter;
} atomic_t;
2.2整形操作API
32位整型使用如下API,64位整型则使用atomic64_开头
ATOMIC_INIT(int i) //定义原子变量的时候对其初始化。
int atomic_read(atomic_t *v) //读取 v 的值,并且返回。
void atomic_set(atomic_t *v, int i) //向 v 写入 i 值。
void atomic_add(int i, atomic_t *v) //给 v 加上 i 值。
void atomic_sub(int i, atomic_t *v) //从 v 减去 i 值。
void atomic_inc(atomic_t *v) //给 v 加 1,也就是自增。
void atomic_dec(atomic_t *v) //从 v 减 1,也就是自减
int atomic_dec_return(atomic_t *v) //从 v 减 1,并且返回 v 的值。
int atomic_inc_return(atomic_t *v) //给 v 加 1,并且返回 v 的值。
int atomic_sub_and_test(int i, atomic_t *v) //从 v 减 i,如果结果为 0 就返回真,否则返回假
int atomic_dec_and_test(atomic_t *v) //从 v 减 1,如果结果为 0 就返回真,否则返回假
int atomic_inc_and_test(atomic_t *v) //给 v 加 1,如果结果为 0 就返回真,否则返回假
int atomic_add_negative(int i, atomic_t *v) //给 v 加 i,如果结果为负就返回真,否则返回假
示例代码:
atomic_t v = ATOMIC_INIT(0); /* 定义并初始化原子变零 v=0 */
atomic_set(&v, 10); /* 设置 v=10 */
atomic_read(&v); /* 读取 v 的值,肯定是 10 */
atomic_inc(&v); /* v 的值加 1,v=11 */
2.3位操作API
void set_bit(int nr, void *p) //将 p 地址的第 nr 位置 1。
void clear_bit(int nr,void *p) //将 p 地址的第 nr 位清零。
void change_bit(int nr, void *p) //将 p 地址的第 nr 位进行翻转。
int test_bit(int nr, void *p) //获取 p 地址的第 nr 位的值。
int test_and_set_bit(int nr, void *p) //将 p 地址的第 nr 位置 1,并且返回 nr 位原来的值。
int test_and_clear_bit(int nr, void *p) //将 p 地址的第 nr 位清零,并且返回 nr 位原来的值。
int test_and_change_bit(int nr, void *p) //将 p 地址的第 nr 位翻转,并且返回 nr 位原来的值。