在应用层学习时,我们学习过多个进程处理共享资源的情况。实际上在驱动中也有类似的情况,并且相对于应用层,并发的情况会更多。
并发(concurrency)指的是多个执行单元同时、并行被执行,而并发的执行单元对共享资源。(硬件资源和软件上的全局变量、静态变量等)的访问则很容易导致竞态(race conditions)
竞态发生的原因主要有以下几点:
1,对称多处理器的cpu
2,单CPU内进程与抢占它的进程
3,中断(硬中断、软中断、Tasklet、底半部)与进程之间
我们在应用层的学习中,对临界资源的保护主要有信号量、互斥量等等。内核中的并发处理也有类似的机制,并且除此之外还有其他的一些机制。我们来详细看一看;
1、中断屏蔽
local_irq_disable() /* 屏蔽中断 */
. . .
critical section /* 临界区*/
. . .
local_irq_enable() /* 开中断*/
中断屏蔽的使用很简单,进入临界区使用屏蔽中断函数,出临界区再打开中断函数。但是有一点要注意,屏蔽的中断只是该CPU中断,其他CPU的中断是无法屏蔽,所以在多核的CPU中起到的作用有限。
2、原子操作
原子操作可以保证对一个整型数据(注意只有整型数据)的修改是排他性的。Linux提供了一系列API来实现内核的原子操作。这些API分为两类,一类是对整型数据的操作。一类是对位的原子操作。原子操作最终都是靠硬件保证的。因此与CPU的架构有密切关系。在ARM架构中,底层最终使用LDREX和STREX指令。
2.1 整型原子操作
atomic_inc_and_test(v) 原子变量加1并测试
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);
上述操作对原子变量进行加/减和自增/自减操作,并返回新的值
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <asm/atomic.h>
MODULE_LICENSE("GPL");
dev_t devno;
int major = 0;
int minor = 0;
int count = 1;
struct cdev *pdev;
struct class * pclass;
struct device * pdevice;
atomic_t v = ATOMIC_INIT(1); //初始化一个原子变量+++++++++++++++++
int demo_open(struct inode * inodep, struct file * filep)
{
if(!atomic_sub_and_test(1, &v)) //获取原子变量+++++++++++++++++
{
printk("v:%d\n", atomic_read(&v));
atomic_add(1, &v);
return -EBUSY;
}
printk("%s,%d\n", __func__, __LINE__);
return 0;
}
int demo_release(struct inode *inodep, struct file *filep)
{
printk("%s,%d\n", __func__, __LINE__);
atomic_inc(&v); //释放原子变量++++++++++++++
return 0;
}
struct file_operations fops = {
.owner =THIS_MODULE,
.open = demo_open,
.release = demo_release,
};
static int __init demo_init(void)
{
int ret = 0;
printk("%s,%d\n", __func__, __LINE__);
ret = alloc_chrdev_region(&devno,minor,count, "xxx");
if(ret)
{
printk("Failed to alloc_chrdev_region.\n");
return ret;
}
printk("devno:%d , major:%d minor:%d\n", devno, MAJOR(devno), MINOR(devno));
pdev = cdev_alloc();
if(pdev == NULL)
{
printk("Failed to cdev_alloc.\n");
goto err1;
}
cdev_init(pdev, &fops);
ret = cdev_add(pdev, devno, count);
if(ret < 0)
{
printk("Failed to cdev_add.");
goto err2;
}
pclass = class_create(THIS_MODULE, "myclass");
if(IS_ERR(pclass))
{
printk("Failed to class_create.\n");
ret = PTR_ERR(pclass);
goto err3;
}
pdevice = device_create(pclass, NULL, devno, NULL, "hello");
if(IS_ERR(pdevice))
{
printk("Failed to device_create.\n");
ret = PTR_ERR(pdevice);
goto err4;
}
return 0;
err4:
class_destroy(pclass);
err3:
cdev_del(pdev);
err2:
kfree(pdev);
err1:
unregister_chrdev_region(devno, count);
return ret;
}
static void __exit demo_exit(void)
{
printk("%s,%d\n", __func__, __LINE__);
device_destroy(pclass, devno);
class_destroy(pclass);
cdev_del(pdev);
kfree(pdev);
unregister_chrdev_region(devno, count);
}
module_init(demo_init);
module_exit(demo_exit);
2.2 位原子操作
3、自旋锁
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/device.h>
MODULE_LICENSE("GPL");
dev_t devno;
int major = 0;
int minor = 0;
int count = 1;
int open_count =0;
struct cdev *pdev;
struct class * pclass;
struct device * pdevice;
static spinlock_t open_lock; //+++++++++++++++++++++++
int demo_open(struct inode * inodep, struct file * filep)
{
spin_lock(&open_lock); //+++++++++++++++++++++
if(open_count){ //++++++++++++++++++++
spin_unlock(&open_lock);//++++++++++++++++
return -EBUSY; //++++++++++++++++++
} //+++++++++++++++++++
open_count++; //++++++++++++++++++++++
spin_unlock(&open_lock); //+++++++++++++++++++++
printk("%s,%d\n", __func__, __LINE__);
return 0;
}
int demo_release(struct inode *inodep, struct file *filep)
{
spin_lock(&open_lock); //++++++++++++++++++++
open_count--; //+++++++++++++++++++
spin_unlock(&open_lock); //++++++++++++++++++
printk("%s,%d\n", __func__, __LINE__);
return 0;
}
struct file_operations fops = {
.owner =THIS_MODULE,
.open = demo_open,
.release = demo_release,
};
static int __init demo_init(void)
{
int ret = 0;
printk("%s,%d\n", __func__, __LINE__);
ret = alloc_chrdev_region(&devno,minor,count, "xxx");
if(ret)
{
printk("Failed to alloc_chrdev_region.\n");
return ret;
}
printk("devno:%d , major:%d minor:%d\n", devno, MAJOR(devno), MINOR(devno));
pdev = cdev_alloc();
if(pdev == NULL)
{
printk("Failed to cdev_alloc.\n");
goto err1;
}
cdev_init(pdev, &fops);
ret = cdev_add(pdev, devno, count);
if(ret < 0)
{
printk("Failed to cdev_add.");
goto err2;
}
pclass = class_create(THIS_MODULE, "myclass");
if(IS_ERR(pclass))
{
printk("Failed to class_create.\n");
ret = PTR_ERR(pclass);
goto err3;
}
pdevice = device_create(pclass, NULL, devno, NULL, "hello");
if(IS_ERR(pdevice))
{
printk("Failed to device_create.\n");
ret = PTR_ERR(pdevice);
goto err4;
}
return 0;
err4:
class_destroy(pclass);
err3:
cdev_del(pdev);
err2:
kfree(pdev);
err1:
unregister_chrdev_region(devno, count);
return ret;
}
static void __exit demo_exit(void)
{
printk("%s,%d\n", __func__, __LINE__);
device_destroy(pclass, devno);
class_destroy(pclass);
cdev_del(pdev);
kfree(pdev);
unregister_chrdev_region(devno, count);
}
module_init(demo_init);
module_exit(demo_exit);
3、信号量
4、互斥体