linux 驱动之原子操作

Table of Contents

一、原子操作介绍

二、什么情况下选择使用原子操作?

三、原子操作编程步骤

1、定义并初始化原子变量 atomic_t

2、对原子变量进行原子操作(计数)

1)、原子变量自增/自减 

2)、原子变量加/减

3)、设置原子变量的值

4)、获取原子变量的值

5)、操作并测试

6)、操作并返回

四、参考代码


 

一、原子操作介绍

什么叫原子操作?

就是对原子变量(区别于普通变量)进行加减(和普通的加减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");
发布了137 篇原创文章 · 获赞 106 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/shenlong1356/article/details/103326004