并发与竞态(原子操作)

农村四月闲人少,勤学苦攻把名扬。这篇文章主要讲述并发与竞态(原子操作)相关的知识,希望能为你提供帮助。
简介百度百科:
"原子操作(atomic operation)是不需要synchronized",这是多线程编程的老生常谈了。所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。




由以上介绍对原子的概念应该有一个大致的了解,总结一下就是在运行原子操作的时候,当前的执行内容不会被任何线程或者中断打断。原子操作只是一个概念,所有能实现以上功能的操作,都可以被称为原子操作。
实例分析为什么要设计原子操作呢?有因必有果,由于linux是多进程抢占式的操作系统,因此在一段程序运行时很可能就被另一个进程访问,或者被中断打断。假设一段如下伪代码:

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,一般人根本不可能搞定。于是有了原子操作的引进。
这里介绍一下对他的使用:

头文件:#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) 声明:

struct sdriver_dev{……atomic_t lock; };
struct sdriver_dev plat_dev;



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

atomic_set(& plat_dev.lock, 1);



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

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



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

atomic_inc(& plat_dev.lock);





实例:

#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_NUM1
#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{intmajor; intgpio_num; dev_tdevid; 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 \\r\\n", __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! \\r\\n", __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! \\r\\n", __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! \\r\\n", __func__, __LINE__); }else {printk("%s:%d: 100ask_test node find! \\r\\n", __func__, __LINE__); }
#endif
plat_property = of_find_property(plat_dev.nd, "compatible", NULL); if(NULL == plat_property ) {printk("Failed:%s:%d: compatible propertynot find! \\r\\n", __func__, __LINE__); }else {printk("%s:%d: compatible property value is %s. \\r\\n", __func__, __LINE__, (char *)plat_property-> value); }sprintf(all_properties, "%s \\r\\n", (char *)plat_dev.nd); printk("Print allpropertise 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 \\r\\n", __FILE__, __LINE__, __func__); platform_driver_register(& imx6ull_platform_driver);
return 0; }
static void __exit imx6ull_driver_exit(void){printk("%s:%d: Entry %s \\r\\n", __FILE__, __LINE__, __func__); platform_driver_unregister(& imx6ull_platform_driver); }module_exit(imx6ull_driver_exit); module_init(imx6ull_driver_init);
MODULE_LICENSE("GPL");



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


    推荐阅读