并发与竞态(原子操作)
Posted H₂O₂
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并发与竞态(原子操作)相关的知识,希望对你有一定的参考价值。
简介百度百科:
"原子操作(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_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 \\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 property not 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 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 \\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");
以上是关于并发与竞态(原子操作)的主要内容,如果未能解决你的问题,请参考以下文章