码迷,mamicode.com
首页 > 系统相关 > 详细

Linux 驱动之并发控制 (信号量)

时间:2015-06-16 09:17:54      阅读:263      评论:0      收藏:0      [点我收藏+]

标签:linux驱动

摘要:并发控制,是多任务操作系统必须面临和解决的一个问题。并发与互斥,主要是用于保护临界资源,如果不站在操作系统进程调度的角度,就很难理解并发与互斥的概念和应用。无论是抢占式操作系统,还是分时操作系统,对于临界资源的保护,都必须采用互斥的机制。Linux内核中,有多种并发控制的机制:自旋锁、原子变量、信号量、读写锁等等。不同的并发机制对应于不同的应用场合,比如说,自旋锁可以应用到中断处理函数中,信号量则不可以。本文主要从一个globalmem_lock例子来阐述信号量的使用。注:该例子取自《Linux设备驱动开发详解》,并做了编译告警的一些改动。  

参考代码

/*======================================================================
    A globalmem driver as an example of char device drivers  
    This example is to introduce how to use locks to avoid race conditions
    
    The initial developer of the original code is ZP1015
    <[email]author@linuxdriver.cn[/email]>. All Rights Reserved.
======================================================================*/
#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 <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>

/*全局内存最大4K字节*/
#define GLOBALMEM_SIZE        0x1000        

/*清0全局内存*/
#define MEM_CLEAR 0x1  						

//#define GLOBALMEM_MAJOR 254    /*预设的globalmem的主设备号*/
#define GLOBALMEM_MAJOR 0 		//[color=Red]我这里设为0,主要是为了让系统自动分配主设备号,如果不这样,有些系统可能insmod不成功[/color]

static globalmem_major = GLOBALMEM_MAJOR;

/*globalmem设备结构体*/
struct globalmem_dev                                     
{                                                        
	struct cdev cdev; /*cdev结构体*/                       
	unsigned char mem[GLOBALMEM_SIZE]; /*全局内存*/     
	struct semaphore sem; /*并发控制用的信号量*/    
};

struct globalmem_dev *globalmem_devp; /*设备结构体指针*/

/*文件打开函数*/
int globalmem_open(struct inode *inode, struct file *filp)
{
	/*将设备结构体指针赋值给文件私有数据指针*/
	filp->private_data = globalmem_devp;
	return 0;
}

/*文件释放函数*/
int globalmem_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/* ioctl设备控制函数 */
static int globalmem_ioctl(struct inode *inodep, struct file *filp, unsigned
  int cmd, unsigned long arg)
{
	struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/

	switch (cmd)
	{
	case MEM_CLEAR:
		if (down_interruptible(&dev->sem))
		{
		return  - ERESTARTSYS;
		}
		memset(dev->mem, 0, GLOBALMEM_SIZE);
		up(&dev->sem); //释放信号量

		printk(KERN_INFO "globalmem is set to zero\n");
		break;

	default:
		return  - EINVAL;
	}
	return 0;
}

/*读函数*/
static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size,
  loff_t *ppos)
{
	unsigned long p =  *ppos;
	unsigned int count = size;
	int ret = 0;
	struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/

	/*分析和获取有效的写长度*/
	if (p >= GLOBALMEM_SIZE)
		return count ?  - ENXIO: 0;
	
	if (count > GLOBALMEM_SIZE - p)
		count = GLOBALMEM_SIZE - p;

	if (down_interruptible(&dev->sem))//获得信号量
	{
		return  - ERESTARTSYS;
	}

	/*内核空间->用户空间*/
	if (copy_to_user(buf, (void*)(dev->mem + p), count))
	{
		ret =  - EFAULT;
	}
	else
	{
		*ppos += count;
		ret = count;

		printk(KERN_INFO "read %u bytes(s) from %lu\n", count, p);
	}
	
	up(&dev->sem); //释放信号量

	return ret;
}

/*写函数*/
static ssize_t globalmem_write(struct file *filp, 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 = filp->private_data; /*获得设备结构体指针*/

	/*分析和获取有效的写长度*/
	if (p >= GLOBALMEM_SIZE)
		return count ?  - ENXIO: 0;
	if (count > GLOBALMEM_SIZE - p)
		count = GLOBALMEM_SIZE - p;

	if (down_interruptible(&dev->sem))//获得信号量
	{
		return  - ERESTARTSYS;
	}
	/*用户空间->内核空间*/
	if (copy_from_user(dev->mem + p, buf, count))
		ret =  - EFAULT;
	else
	{
		*ppos += count;
		ret = count;

		printk(KERN_INFO "written %u bytes(s) from %lu\n", count, p);
	}
	
	up(&dev->sem); //释放信号量
	return ret;
}

/* seek文件定位函数 */
static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
{
	loff_t ret = 0;
	switch (orig)
	{
	case 0:   /*相对文件开始位置偏移*/
		if (offset < 0)
		{
			ret =  - EINVAL;
			break;
		}
		
		if ((unsigned int)offset > GLOBALMEM_SIZE)
		{
			ret =  - EINVAL;
			break;
		}
		
		filp->f_pos = (unsigned int)offset;
		ret = filp->f_pos;
		break;
		
	case 1:   /*相对文件当前位置偏移*/
		if ((filp->f_pos + offset) > GLOBALMEM_SIZE)
		{
			ret =  - EINVAL;
			break;
		}
		
		if ((filp->f_pos + offset) < 0)
		{
			ret =  - EINVAL;
			break;
		}
		
		filp->f_pos += offset;
		ret = filp->f_pos;
		break;
		
	default:
		ret =  - EINVAL;
		break;
		}
	return ret;
}

/*文件操作结构体*/
static const struct file_operations globalmem_fops =
{
	.owner = THIS_MODULE,
	.llseek = globalmem_llseek,
	.read = globalmem_read,
	.write = globalmem_write,
	.ioctl = globalmem_ioctl,
	.open = globalmem_open,
	.release = globalmem_release,
};

/*初始化并注册cdev*/
static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)
{
	int err, devno = MKDEV(globalmem_major, index);

	cdev_init(&dev->cdev, &globalmem_fops);
	dev->cdev.owner = THIS_MODULE;
	dev->cdev.ops = &globalmem_fops;
	err = cdev_add(&dev->cdev, devno, 1);
	
	if (err)
		printk(KERN_NOTICE "Error %d adding LED%d", err, index);
}

/*设备驱动模块加载函数*/
int globalmem_init(void)
{
	int result;
	dev_t devno = MKDEV(globalmem_major, 0);

	/* 申请设备号*/
	if (globalmem_major)
		result = register_chrdev_region(devno, 1, "globalmem");
	else  /* 动态申请设备号 */
	{
		result = alloc_chrdev_region(&devno, 0, 1, "globalmem");
		globalmem_major = MAJOR(devno);
	}  
	
	if (result < 0)
		return result;

	/* 动态申请设备结构体的内存*/
	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));

	globalmem_setup_cdev(globalmem_devp, 0);
	init_MUTEX(&globalmem_devp->sem);   /*初始化信号量*/  
	printk("register globalmem success\n");
	return 0;

fail_malloc: unregister_chrdev_region(devno, 1);

	return result;
}

/*模块卸载函数*/
void globalmem_exit(void)
{
	cdev_del(&globalmem_devp->cdev);   /*注销cdev*/
	kfree(globalmem_devp);     /*释放设备结构体内存*/
	unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); /*释放设备号*/
	printk("unregister globalmem success\n");
}

MODULE_AUTHOR("ZP1015");
MODULE_LICENSE("Dual BSD/GPL");

module_param(globalmem_major, int, S_IRUGO);

module_init(globalmem_init);
module_exit(globalmem_exit);



并发与竞态
原因:SMP ,进程抢占,中断,
local_irq_disable()只能禁止本CPU的中断
local_irq_save(flags)还保存CPU中断位信息(?)
如果只想禁止中断的底半部分,local_bh_disable()


原子操作
void atomic_set(atomic_t *v, int i);
atomic_t v=ATOMIC_INIT(0);
atomic_read(atomic_t *v);
void atomic_add(int i, atomic_t *v);
void atomic_sub(int i, atomic_t *v);
void atomic_inc(atomic_t *v);
void atomic_dec(atomic_t *v);
int atomic_inc_and_test(atomic_t *v);
int atomic_dec_and_test(atomic_t *v);测试是否为0
int atomic_sub_and_test(int i, atomic_t *v);
int atomic_add_return(int i, atomic_t *v);返回新值
int atomic_sub_return(int i, atomic_t *v)
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);
void set_bit(nr, void *addr)设置第NR位
void clear_bit(nr, void *addr)
int test_bit(nr, void * addr);
int test_and_set_bit(nr, void *addr);
int test_and_clear_bit(nr, void *addr);
int test_and_change_bit(nr,void *addr);


自旋锁
主要针对SMP和内核可抢占情况
单CPU和内核可抢占情况,自旋锁持有期间,抢占将被禁止
自旋锁调用期间不能调用copy_to_user() copy_from_user kmalloc()等可能引起阻塞的函数
spinlock_t spin;
spin_lock_init(lock);
spin_lock(lock);
spin_try_lock(lock);
组合:spin_lock_irq()=spin_lock()+local_irq_disable()   spin_unlock_irq()
spin_lock_irqsave()spin_unlock_irqstore()
spin_lock_bh() spin_unlock_bh()

Linux 驱动之并发控制 (信号量)

标签:linux驱动

原文地址:http://blog.csdn.net/xy010902100449/article/details/46513831

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!