Table of Contents
一、原子操作介绍
什么叫原子操作?
就是对原子变量(区别于普通变量)进行加减(和普通的加减add sub是有区别的)操作时不会被CPU打断的操作,
原子变量分类:
整形原子atomic_t和位原子
原子操作的优点是什么?
保证一个变量在并发访问时产生的竞争,编程简单
原子变量的缺点是什么?
功能太简单,只能做计数操作(对某个变量加减),保护的东西太少
二、什么情况下选择使用原子操作?
如果我们有一个全局变量,中断和普通函数,或者中断与中断之间都需要访问这个变量,或者是一个用于进程互斥的全局变量(比如防止串口驱动重复打开的互斥变量),并且对这个变量只是简单的加减计数操作,我们考虑把普通变量换成原子变量,再利用相应的一套原子加减操作
三、原子操作编程步骤
1、定义并初始化原子变量 atomic_t
atomic_t btn_tv = ATOMIC_INIT(1);
2、对原子变量进行原子操作(计数)
1)、原子变量自增/自减
#define atomic_inc(v) atomic_add(1, v);
void atomic_inc(atomic_t *v); /* 原子变量自增 1 */
#define atomic_dec(v) atomic_sub(1, v);
void atomic_dec(atomic_t *v); /* 原子变量自减 1 */
2)、原子变量加/减
void atomic_add(int i, atomic_t *v); /* 原子变量加 i */
void atomic_sub(int i, atomic_t *v); /* 原子变量减 i */
3)、设置原子变量的值
#define atomic_set(v,i) ((v)->counter = (i))
void atomic_set(atomic_t *v, int i); /* 设置原子变量的值为i */
#define ATOMIC_INIT(i) ( (atomic_t) { (i) } )
atomic_t v = ATOMIC_INIT(0); /* 定义原子变量 v 并初始化为 0 (该宏只支持初始为 0)*/
4)、获取原子变量的值
#define atomic_read(v) ((v)->counter + 0)
atomic_read(atomic_t *v); /* 返回原子变量的值 */
5)、操作并测试
#define atomic_inc_and_test(v) (atomic_add_return(1, (v)) == 0)static inline int atomic_inc_and_test(atomic_t *v); /* 原子变量自增 1 并判断结果是否为 0 */
int atomic_dec_and_test(atomic_t *v); /* 原子变量自减 1 并判断结果是否为 0 */
int atomic_sub_and_teset(int i, atomic_t *v); /* 原子变量减 i 并判断结果是否为 0 */
/* 上述测试结果为 0 返回 true 否者返回 false */
6)、操作并返回
int atomic_add_and_return(int i, atomic_t *v); /* 原子变量加 i 并返回新值 */
int atomic_sub_and_return(int i, atomic_t *v); /* 原子变量减 i 并返回新值 */
int atomic_inc_and_return(atomic_t *v); /* 原子变量自增 1 并返回新值 */
int atomic_dec_and_return(atomic_t *v); /* 原子变量自减 1 并返回新值 */
四、参考代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <asm/gpio.h>
#include <plat/gpio-cfg.h>
static int major;
static struct cdev btn_cdev;
static struct class *cls;
static atomic_t btn_tv; //分配整型原子变量btn_tv
static int btn_open(struct inode *inode,
struct file *file)
{
if (!atomic_dec_and_test(&btn_tv)) {
printk("button can be open only once!\n");
atomic_inc(&btn_tv);
return -EBUSY;
}
printk("open buttton successfully!\n");
return 0;
}
static int btn_close(struct inode *inode,
struct file *file)
{
atomic_inc(&btn_tv);
return 0;
}
static struct file_operations btn_fops = {
.owner = THIS_MODULE,
.open = btn_open,
.release = btn_close
};
static int btn_init(void)
{
dev_t dev;
//1.申请设备号
alloc_chrdev_region(&dev, 0, 1, "buttons");
major = MAJOR(dev);
//2.初始化注册cdev
cdev_init(&btn_cdev, &btn_fops);
cdev_add(&btn_cdev, dev, 1);
//3.自动创建设备节点:/dev/mybuttons
cls = class_create(THIS_MODULE, "buttons");
device_create(cls, NULL, dev, NULL, "mybuttons");
//初始化整型原子变量的值为1
atomic_inc(&btn_tv); //btn_tv = 1
return 0;
}
static void btn_exit(void)
{
dev_t dev = MKDEV(major, 0);
//1.删除设备节点
device_destroy(cls, dev);
class_destroy(cls);
//2.卸载cdev
cdev_del(&btn_cdev);
//3.释放设备号
unregister_chrdev_region(dev, 1);
}
module_init(btn_init);
module_exit(btn_exit);
MODULE_LICENSE("GPL");