- 什么是并发,竞态
- 竞态的解决方式
- 实际中如何灵活应用
## 什么是并发,竞态
- 并发(concurrency): 并发是指多个执行单元同时,并行的执行
- 竞态(race conditions): 并发的执行单元对共享资源(硬件资源(比如只有一个i2c,一个uart),全局变量等)的访问容易发生数据混乱
- 竞态发生的情况
1 对称多处理器(SMP)的多个CPU,smp是一种紧耦合,共享存储的系统模型,它的特点是多个CPU使用共同给的系统总线,因此可以访问共同的外设和存储器。(多个CPU可以粗略理解为多个线程)
2 单CPU内进程与抢占它的进程--2.6的内核支持抢占调度,一个进程在内核执行的时候可能被另一高优先级进程打断
3中断(硬中断,软中断, tasklet,底半部)与进程之间:中断可以打断正在执行的进程,处理中断的程序和被打断的进程间也可能发生竞态,此外中断也可以被新的更高优先级的中断打断,因此,多个中断之间本身也可能引起并发而导致竞态。
## 竞态的解决方法
解决竞态问题的途径是保证对共享资源的互斥访问,访问共享资源的代码区称为临界区,临界区要互斥机制保护,linux设备驱动中常见的互斥机制有以下方式:中断屏蔽,原子操作,自旋锁,信号量
## 实际中如何灵活应用
1 中断屏蔽
1 基本概念:在单CPU中避免竞态的一种简单方法是在进入临界区之前屏蔽系统的中断,由于linux的异步I/O,进程调度等很多内容都依靠中断,所以我们应该尽快的执行完临界区的代码,换句话就是临界区代码应该尽量少
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#define GLOBALMEM_SIZE 0x1000 /* 全局内存大小:4KB */
#define MEM_CLEAR 0x01 /* clear mem */
#define GLOBALMEM_MAJOR 254 /* 预设的globalmem的主设备号*/
static dev_t globalmem_major = GLOBALMEM_MAJOR;
static struct globalmem_dev {
struct cdev cdev; /* cdev 结构体 */
unsigned char mem[GLOBALMEM_SIZE]; /* 全局内存 */
};
struct globalmem_dev *globalmem_devp; /* 设备结构体指针 */
// open
static int globalmem_open(struct inode *inode,struct file *flip)
{
/* 将设备结构体指针赋值给文件私有数据指针 */
flip->private_data = globalmem_devp;
return 0;
}
// release
static int globalmem_release(struct inode *inode, struct file *flip)
{
return 0;
}
// ioctl
static long globalmem_ioctl(struct file *flip,unsigned int cmd,unsigned long arg)
{
struct globalmem_dev *dev = flip->private_data; /* 获得设备结构体指针 */
switch (cmd) {
case MEM_CLEAR:
memset(dev->mem,0,GLOBALMEM_SIZE);
printk(KERN_INFO"globalmem is set to zero\n");
break;
default:
return -EINVAL;
break;
}
return 0;
}
// read
static int globalmem_read(struct file *flip, char __user *buf,size_t size,loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct globalmem_dev *dev = flip->private_data; /* 获得设备结构体指针 */
printk("--%s--\n",__FUNCTION__);
/* */
if (p > GLOBALMEM_SIZE)
return count ? -ENXIO:0;
if (count > GLOBALMEM_SIZE - p)
count = GLOBALMEM_SIZE-p;
/* 内核空间-> 用户空间 */
if (copy_to_user(buf,(void *)(dev->mem + p),count)) {
ret = -EFAULT;
}
else {
*ppos += count;
ret = count;
printk(KERN_INFO "read %d bytes(s)from %ld\n",count,p);
}
return ret;
}
// write
static ssize_t globalmem_write(struct file *flip,const char __user *buf,size_t size,loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
struct globalmem_dev *dev = flip->private_data;
/* 分析和获取有效的写长度 */
if (p >= GLOBALMEM_SIZE)
return count ? -ENXIO:0;
if (count > GLOBALMEM_SIZE - p)
count = GLOBALMEM_SIZE - p;
/* 内核空间-> 用户空间 */
if (copy_from_user(dev->mem + p,buf,count))
ret = -EFAULT;
else {
*ppos += count;
ret = count;
printk(KERN_INFO"write %d bytes from %ld\n",count,p);
}
return ret;
}
static const struct file_operations globalmem_fops = {
.owner = THIS_MODULE,
//.llseek = globalmem_llseek,
.read = globalmem_read,
.write = globalmem_write,
.unlocked_ioctl = globalmem_ioctl,
.open = globalmem_open,
.release = globalmem_release,
};
/* 初始化cdev对象和其行为 并注册
* cdev_init
* cdev_add
*/
static void globalmem_setup_cdev(struct globalmem_dev *dev,int index)
{
int err,devno = MKDEV(globalmem_major,index);
cdev_init(&dev->cdev,&globalmem_fops); /* cdev对象 + 行为*/
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &globalmem_fops;
err = cdev_add(&dev->cdev,devno,1); /* cdev对象 + devno */
if (err) {
printk(KERN_NOTICE "Error %d adding LED%d",err,index);
}
printk("setup_cdev success%d\n",err);
}
static int __init globalmem_init(void)
{
int result;
dev_t devno = MKDEV(globalmem_major,0);
/* 1 申请设备号 */
#if 0
if (globalmem_major > 0)
result = register_chrdev_region(devno,1,"globalmem"); /* 静态分配房间 */
else {
result = alloc_chrdev_region(&devno,0,1,"globalmem"); /* 动态分配设备号 */
globalmem_major = MAJOR(devno); /* 动态申请后,分配设备号 */
}
#else
result = alloc_chrdev_region(&devno,0,1,"globalmem"); /* 动态分配设备号 */
globalmem_major = MAJOR(devno); /* 动态申请后,分配设备号 */
#endif
if (result < 0){
printk("result = %d\n",result);
return result;
}
printk("globalMajor= %d,result=%d\n",globalmem_major,result);
/* 2 动态申请设备结构体的内存 */
globalmem_devp = kmalloc(sizeof(struct globalmem_dev),GFP_KERNEL);
if (!globalmem_devp){ /* 申请失败 */
result = -ENOMEM;
goto fail_malloc;
}
memset(globalmem_devp,0,sizeof(struct globalmem_dev));
/* 3 字符设备申请 */
globalmem_setup_cdev(globalmem_devp,0);
return 0;
fail_malloc:
unregister_chrdev_region(devno,1);
return result;
}
static void __exit globalmem_exit(void)
{
cdev_del(&globalmem_devp->cdev); /* 注销字符设备 */
kfree(globalmem_devp); /* 内存清空 */
unregister_chrdev_region(MKDEV(globalmem_major,0),1); /* 房间号收回,释放设备号 */
}
MODULE_AUTHOR("xiao wei");
MODULE_LICENSE("GPL");
module_param(globalmem_major,int,S_IRUGO); /* S_IRUGO ? */
module_init(globalmem_init);
module_exit(globalmem_exit);