驱动程序之同步阻塞互斥

1. 非共享设备

目的:使得同时只有一个应用程序可以打开驱动程序。

基于 buttons_irq 驱动程序的基础上进行修改。

1.1 使用原子操作

原子操作指的是在执行过程中,不会被别的代码路径所中断的操作。

常用的原子操作函数:

atomic_t v = ATOMIC_INIT(0);		// 定义原子变量v并初始化为0
atomic_read(atomic_t *v);			// 返回原子变量的值
atomic_inc(atomic_t *v);			// 原子变量增加1
atomic_dec(atomic_t *v);			// 原子变量减少1
atomic_dec_and_test(atomic_t *v);	// 自减操作后测试其是否为0,为0则返回true,
									// 否则返回false

驱动程序

  1. 初始化一个原子变量:

    static unsigned int major;
    static atomic_t canopen = ATOMIC_INIT(1);	/* 新添加 */
    static struct class *s3c2440_single_class;
    static struct class_device *s3c2440_single_dev;
    
  2. 修改 open 函数:

    static ssize_t s3c2440_single_open(struct inode *inode, struct file *file)
    {
    	if (atomic_dec_and_test(&canopen))
    	{
    		atomic_inc(&canopen);
    		return -EBUSY;
    	}
    	
    	/* 配置按键:GPF0\2和GPG3的引脚为中断引脚
    	 * 注册irqaction处理函数 
    	 */
    	request_irq(IRQ_EINT0,  s3c2440_single_handler, IRQT_BOTHEDGE, "s2", &pins_desc[0]);
    	request_irq(IRQ_EINT2,  s3c2440_single_handler, IRQT_BOTHEDGE, "s3", &pins_desc[1]);
    	request_irq(IRQ_EINT11, s3c2440_single_handler, IRQT_BOTHEDGE, "s4", &pins_desc[2]);
    	request_irq(IRQ_EINT19, s3c2440_single_handler, IRQT_BOTHEDGE, "s5", &pins_desc[3]);
    	
    	return 0;
    }
    
  3. 修改 close 函数:

    static ssize_t s3c2440_single_close(struct inode *inode, struct file *file)
    {
    	atomic_inc(&canopen);
    	free_irq(IRQ_EINT0,  &pins_desc[0]);
    	free_irq(IRQ_EINT2,  &pins_desc[1]);
    	free_irq(IRQ_EINT11, &pins_desc[2]);
    	free_irq(IRQ_EINT19, &pins_desc[3]);
    	return 0;
    }
    

1.2 使用信号量

信号量(semaphore)是用于保护临界区的一种常用方法,只有得到信号量的进程才能执行临界区代码。当获取不到信号量时,进程进入休眠等待状态。

定义信号量/互斥锁

struct sempahore sem;					/* 信号量 */
static DECLEARE_MUTEX(button_lock);		/* 互斥锁 */

互斥锁与信号量的区别:

/* 互斥锁是基于等待队列实现的 */
struct semaphore {
	atomic_t count;
	int sleepers;
	wait_queue_head_t wait;
};

#define __SEMAPHORE_INIT(name, cnt)				\
{								\
	.count	= ATOMIC_INIT(cnt),				\
	.wait	= __WAIT_QUEUE_HEAD_INITIALIZER((name).wait),	\
}

#define __DECLARE_SEMAPHORE_GENERIC(name,count)	\
	struct semaphore name = __SEMAPHORE_INIT(name,count)

#define DECLARE_MUTEX(name)		__DECLARE_SEMAPHORE_GENERIC(name,1)
#define DECLARE_MUTEX_LOCKED(name)	__DECLARE_SEMAPHORE_GENERIC(name,0)

这个 Linux-2.6.22 版本 arm 架构的互斥锁与信号量的区别在于,互斥锁是 sleepers 为1的特殊情况下的信号量。

初始化信号量

void sema_init(struct sempahore *sem, int val);
void init_MUTEX(struct sempahore *sem);		// 初始化为0

获取信号量

void down(struct sempahore *sem);
int down_interruptible(struct sempahore *sem);
int down_trylock(struct sempahore *sem);

三者的区别:

  1. down:尝试获取信号量,获取失败则会阻塞;
  2. down_interruptible:尝试获取信号量,获取失败会阻塞,但这个阻塞状态可以被打断;
  3. down_trylock:尝试获取信号量,如果成功获取了信号量,则返回0,如果不能获取信号量,则返回1。

释放信号量

void up(struct sempahore *sem);

驱动程序

  1. 初始化一个信号量变量:

    static DECLARE_MUTEX(button_lock);
    
  2. 在 open 函数处获取信号量:

    static ssize_t s3c2440_buttons_irq_open(struct inode *inode, struct file *file)
    {
    	/* 获取信号量 */
    	down(&button_lock);
    	
    	/* 配置按键:GPF0\2和GPG3的引脚为中断引脚
    	 * 注册irqaction处理函数 
    	 */
    	request_irq(IRQ_EINT0,  s3c2440_buttons_irq_handler, IRQT_BOTHEDGE, "s2", &pins_desc[0]);
    	request_irq(IRQ_EINT2,  s3c2440_buttons_irq_handler, IRQT_BOTHEDGE, "s3", &pins_desc[1]);
    	request_irq(IRQ_EINT11, s3c2440_buttons_irq_handler, IRQT_BOTHEDGE, "s4", &pins_desc[2]);
    	request_irq(IRQ_EINT19, s3c2440_buttons_irq_handler, IRQT_BOTHEDGE, "s5", &pins_desc[3]);
    	
    	return 0;
    }
    
  3. 在 close 函数处释放信号量:

    static ssize_t s3c2440_buttons_irq_close(struct inode *inode, struct file *file)
    {
    	up(&button_lock);
    	free_irq(IRQ_EINT0,  &pins_desc[0]);
    	free_irq(IRQ_EINT2,  &pins_desc[1]);
    	free_irq(IRQ_EINT11, &pins_desc[2]);
    	free_irq(IRQ_EINT19, &pins_desc[3]);
    	return 0;
    }
    

2. 阻塞与非阻塞

阻塞操作:是指在执行设备操作时,若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。

非阻塞操作:进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直到可以进行操作为止。

修改驱动程序

修改 open 函数的获取信号量方式:

static ssize_t s3c2440_buttons_irq_open(struct inode *inode, struct file *file)
{
	/* 获取信号量 */
	if (file->f_flags & O_NONBLOCK)
	{
		if (down_trylock(&button_lock) == 1)
			return -EBUSY;
	}
	else 
	{
		down(&button_lock);
	}
	
	/* 配置按键:GPF0\2和GPG3的引脚为中断引脚
	 * 注册irqaction处理函数 
	 */
	request_irq(IRQ_EINT0,  s3c2440_buttons_irq_handler, IRQT_BOTHEDGE, "s2", &pins_desc[0]);
	request_irq(IRQ_EINT2,  s3c2440_buttons_irq_handler, IRQT_BOTHEDGE, "s3", &pins_desc[1]);
	request_irq(IRQ_EINT11, s3c2440_buttons_irq_handler, IRQT_BOTHEDGE, "s4", &pins_desc[2]);
	request_irq(IRQ_EINT19, s3c2440_buttons_irq_handler, IRQT_BOTHEDGE, "s5", &pins_desc[3]);
	
	return 0;
}

修改 read 函数的读操作:

static ssize_t s3c2440_sempahore_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	unsigned long ret;
	if (count < sizeof(key_val))
		return -EINVAL;

	if (file->f_flags & O_NONBLOCK)
	{
		if (!ev_press)
			return -EAGAIN;
	}
	else
	{
		/* 如果没有按键按下,就休眠 */
		wait_event_interruptible(button_waitq, ev_press);
	}

	/* 如果没有按键按下,就休眠 */
	wait_event_interruptible(button_waitq, ev_press);

	/* 如果有按键被按下,从休眠状态唤醒 */
	ret = copy_to_user(buf, &key_val, sizeof(key_val));
	ev_press = 0;
	return ret;
}

3. 完整的驱动程序与测试程序

驱动程序之同步阻塞互斥的源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值