简介
百度百科:
"原子操作(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");