并发与竞态(原子操作)

2020-07-23 17:15:55 浏览数 (1)

简介

百度百科:

"原子操作(atomic operation)是不需要synchronized",这是多线程编程的老生常谈了。所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。

由以上介绍对原子的概念应该有一个大致的了解,总结一下就是在运行原子操作的时候,当前的执行内容不会被任何线程或者中断打断。原子操作只是一个概念,所有能实现以上功能的操作,都可以被称为原子操作。

实例分析

为什么要设计原子操作呢?有因必有果,由于linux是多进程抢占式的操作系统,因此在一段程序运行时很可能就被另一个进程访问,或者被中断打断。假设一段如下伪代码:

代码语言:javascript复制
static int val = 1;

void driver_only_one() //共享资源,同时仅允许一个进程访问。
{
  if (--val) {
    val  ;
    return -EBUSY;
  } 
  …… //操作共享资源代码
}

void func_A()
{
  driver_only_one();
}

void interrupt_handle()
{
  driver_only_one();
}

如上,driver_only_one的要求是当val值为1时,只能有一个进程调用 driver_only_one操作共享资源。 (首先要明确一点,--val在最底层汇编中,会分为三步:①读取数据 ②val减1 ③将val值写入内存)

因此会存在一种bug情况:当func_A第一次调用driver_only_one,且--val刚执行完第①步时,突然被interrupt_handle中断打断,此时val值还未 来得及修改仍为1,中断执行完后,成功访问了driver_only_one操作共享代码。在中断退出后,func_A该执行--val的第②步,因为第①步已经读取val的值为1,所以此时func_A也可以顺利执行完driver_only_one。与初衷不符,存在隐患!

虽然以上情况发生概率很小,但是存在这种隐患,一旦发生后果可能很严重,且是概率事件又称“玄学”事件,极难排查!

原子操作使用

在linux中,并发事件无时无刻不在发生,因此大大提升了上述事件发生的概率。而且这种bug,一般人根本不可能搞定。于是有了原子操作的引进。

这里介绍一下对他的使用:

代码语言:javascript复制
头文件:#include <linux/types.h>
初始化:atomic_t lock;
操作API:
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,如果结果为负就返回真,否则返回假

注:原子操作最底层的实现(汇编级实现大致了解):在对值进行修改过程中,会检测是否被其他进程打断访问,如果被打断就重新来过。直到能一次顺利的读、改、写。

使用流程:

原子操作使用步骤: (1) 声明:

代码语言:javascript复制
struct sdriver_dev{
……
    atomic_t lock;
};

struct sdriver_dev plat_dev;

(2) 初始化时:原子量赋值为1

代码语言:javascript复制
atomic_set(&plat_dev.lock, 1);

对外接口open:先减一检测 atomic_dec_and_test(&plat_dev.lock),当返回值为真时,当前无进程使用,可以进行访问。如果返回值为假,说明当前已经被其他进程使用,恢复刚刚的减1操作并退出。

代码语言:javascript复制
    if (!atomic_dec_and_test(&plat_dev.lock)) {
        atomic_inc(&plat_dev.lock); /* 小于0的话就加1,恢复刚刚的减1操作 */
        return -EBUSY;              /* LED被使用,返回忙 */
    }

对外接口release:当前进程退出对驱动的使用,并加一操作恢复原子量。

代码语言:javascript复制
atomic_inc(&plat_dev.lock);

实例:

代码语言:javascript复制
#include <linux/cdev.h>
#include <linux/circ_buf.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/genalloc.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mxc_mlb.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/poll.h>
#include <linux/regulator/consumer.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/types.h>

#define DEV_MAJOR 201
#define DEV_MINOR 0
#define DEV_NUM   1

#define DRIVER_CLASS_NAME "plat_case_class"
#define DRIVER_DEVICE_NAME  "plat1"
#define PLATDRV_NAME "driver_case"
#define PLATFORM_NAME "platform_keys"
#define COMPATABLE_NAME "100as,test"
#define PLATDRV_NUM 1

struct sdriver_dev{
    int    major;
    int    gpio_num;
    dev_t  devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node *nd;
    atomic_t lock;
};

struct sdriver_dev plat_dev;

static int platdrv_open(struct inode *inode, struct file *filp)
{
    if (!atomic_dec_and_test(&plat_dev.lock)) {
        atomic_inc(&plat_dev.lock); /* 小于0的话就加1,恢复刚刚的减1操作 */
        return -EBUSY;              /* LED被使用,返回忙 */
    }

    return 0;
}

static ssize_t platdrv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    return 0;
}

static int platdrv_release(struct inode *inode, struct file *filp)
{
    /* 关闭驱动文件的时候释放原子变量 */
    atomic_inc(&plat_dev.lock);

    return 0;
}

/* 驱动结构体 */
static struct file_operations platdrv_fops = {
    .owner = THIS_MODULE,
    .open  = platdrv_open,
    .write = platdrv_write,
    .release = platdrv_release,
};

static int imx6ull_probe(struct platform_device *pdev)
{
    //struct device_node *node = NULL;
    struct property *plat_property;
    char all_properties[100];
    atomic_set(&plat_dev.lock, 1);
    printk("%s:%d: Entry %s rn", __FILE__, __LINE__, __func__);
#if 1   
    /* 1. 设置设备号 
     * 主设备号已知, 静态注册;未知, 动态注册。
     */
    if (plat_dev.major){
        plat_dev.devid = MKDEV(plat_dev.major, 0);
        register_chrdev_region(plat_dev.devid, PLATDRV_NUM, PLATDRV_NAME);
    } else {
        alloc_chrdev_region(&plat_dev.devid, 0, PLATDRV_NUM, PLATDRV_NAME);
        plat_dev.major = MAJOR(plat_dev.devid); 
    }

    /* 2. 注册驱动结构体 */
    plat_dev.cdev.owner = THIS_MODULE;
    cdev_init(&plat_dev.cdev, &platdrv_fops);
    cdev_add(&plat_dev.cdev, plat_dev.devid, PLATDRV_NUM);

    /* 3. 创建类 */
    plat_dev.class = class_create(THIS_MODULE, DRIVER_CLASS_NAME);  
    if(IS_ERR(plat_dev.class)) {
        printk("Failed:%s:%d: %s under class created failed! rn", __func__, __LINE__, DRIVER_DEVICE_NAME);
    }
    /* 4.创建设备 */
    plat_dev.device = device_create(plat_dev.class, NULL, plat_dev.devid,
                                NULL, DRIVER_DEVICE_NAME);
    if(NULL == plat_dev.device) {
        printk("Failed:%s:%d: %s under class created failed! rn", __func__, __LINE__,  DRIVER_DEVICE_NAME);   
    }
#endif 
    /* 5.硬件初始化 读取设备数 */

    /* 读取设备树  */
#if 0
#if 1
    /* 通过compatible值匹配设备节点 */
    plat_dev->nd = of_find_compatible_node(NULL, NULL, );
#else
    /* 通过绝对路径查找设备节点 */
    plat_dev.nd = of_find_node_by_path("/100ask_test");
#endif

    if(NULL == plat_dev.nd ) {
        printk("Failed:%s:%d: 100ask_test node not find! rn", __func__, __LINE__);
    }  else {
        printk("%s:%d: 100ask_test node find! rn", __func__, __LINE__);
    }

#endif

    plat_property = of_find_property(plat_dev.nd, "compatible", NULL);
    if(NULL == plat_property ) {
        printk("Failed:%s:%d: compatible property  not find! rn", __func__, __LINE__);
    }  else {
        printk("%s:%d: compatible property value is %s. rn", __func__, __LINE__, (char *)plat_property->value);
    }
    sprintf(all_properties, "%s rn", (char *)plat_dev.nd);
    printk("Print all  propertise of 100_test :  %s ", plat_dev.nd->child->name );
    return 0;
}

int imx6ull_remove(struct platform_device *pdev)
{
    cdev_del(&plat_dev.cdev);
    unregister_chrdev_region(plat_dev.devid, PLATDRV_NUM);  
    device_destroy(plat_dev.class, plat_dev.devid);
    class_destroy(plat_dev.class);
    
    return 0;
}

const struct of_device_id platform_table[] = {
    {
        .compatible = COMPATABLE_NAME,
    },
    {
    },
};

static struct platform_driver imx6ull_platform_driver = {
    .probe  = imx6ull_probe,
    .remove = imx6ull_remove,
    .driver = {
        .name = PLATFORM_NAME,
        .owner = THIS_MODULE,
        .of_match_table = platform_table,
    },
};


static int __init imx6ull_driver_init(void)
{
    printk("%s:%d: Entry %s rn", __FILE__, __LINE__, __func__);
    platform_driver_register(&imx6ull_platform_driver);

    return 0;
}

static void __exit imx6ull_driver_exit(void)
{
    printk("%s:%d: Entry %s rn", __FILE__, __LINE__, __func__);
    platform_driver_unregister(&imx6ull_platform_driver);
}
    
module_exit(imx6ull_driver_exit);
module_init(imx6ull_driver_init);

MODULE_LICENSE("GPL");

参考:《驱动大全之同步与互斥》

0 人点赞