字符设备驱动同步之互斥阻塞

先了解几个概念:
临界资源: 一次只允许一个执行单元使用的资源称为临界资源。比如多台电脑都可以使用同一台打印机,但是,一个时刻只能有一台电脑来控制他进行打印,所以打印机在这里就是临界资源。
临界区: 访问共享资源的代码区域。就是执行单元访问共享资源的那段代码就对啦。
并发: 就是几个进程一起执行。
竞态: 几个进程同时访问共享资源时发生
(上面之所以说是执行单元,不说进程是因为还有线程的存在)
什么叫进程的同步?
百度一下:我们把异步环境下的一组并发进程因直接制约而互相发送消息、进行互相合作、互相等待,使得各进程按一定的速度执行的过程称为进程间的同步。
我的理解就是: 进程之间步伐一致,他们之间能够有配合的工作。
进程之间同步的实现需要一些规则来协调,通常比如:互斥、阻塞。
一、互斥:同一时刻只能有一个App打开某个设备或访问某个资源
实现互斥的技巧:
(1)原子操作
驱动程序的任务:
①设置原子变量的值
atomic_t v = ATOMIC_INIT(1);//定义原子变量v初始化为1
②在即将访问临界资源前设定“使用中”标记,通常是:
if (!atomic_dec_and_test(&canopen))
{
atomic_inc(&canopen);
printk("Using now!\n");
return -EBUSY;
}
③在不使用资源时要将标记改成“未使用”
atomic_inc(&canopen);//释放原子变量
代码实现:

atomic.c文件

#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/module.h>
#include <linux/device.h> 		//class_create
#include <mach/regs-gpio.h>		//S3C2410_GPF1 
#include <mach/hardware.h>
#include <linux/interrupt.h>  //wait_event_interruptible
#include <linux/fs.h>
#include <arch/arm/include/asm/atomic.h>

/* 定义并初始化等待队列头 */
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);


static atomic_t canopen = ATOMIC_INIT(1);     //定义原子变量并初始化为1


static struct class *buttondev_class;
static struct device *buttons_device;

static struct pin_desc{
	unsigned int pin;
	unsigned int key_val;
};

static struct pin_desc pins_desc[4] = {
		{S3C2410_GPF1,0x01}, //S3C2410_GPF1是对GPF1引脚这种“设备”的编号dev_id
		{S3C2410_GPF4,0x02},
		{S3C2410_GPF2,0x03},
		{S3C2410_GPF0,0x04},
}; 
static int ev_press = 0;
static unsigned char key_val;
int major;

/* 中断处理函数 */
static irqreturn_t handle_irq(int irq, void *dev_id)
{
	struct pin_desc *irq_pindesc = (struct pin_desc *)dev_id;//
	unsigned int pinval;
		
	pinval = s3c2410_gpio_getpin(irq_pindesc->pin);//获取按键值:有按键按下返回按键值0
	/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
	/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
	if(pinval)
	{
		/* 松开 */
		key_val = 0x80 | (irq_pindesc->key_val);
	}
	else
	{
		/* 按下 */
		key_val = irq_pindesc->key_val;
	}

	ev_press = 1;							/* 表示中断已经发生 */
	wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */
	return IRQ_HANDLED;
}

static int buttons_dev_open(struct inode * inode, struct file * filp)
{
	///
	//实现互斥:原子操作
	if (!atomic_dec_and_test(&canopen))
	{
		atomic_inc(&canopen);
		printk("dev is using now!\n");
		return -EBUSY;
	}
	///
	request_irq(IRQ_EINT1, handle_irq, IRQ_TYPE_EDGE_FALLING, "K1",&pins_desc[0]);
	request_irq(IRQ_EINT4, handle_irq, IRQ_TYPE_EDGE_FALLING, "K2",&pins_desc[1]);
	request_irq(IRQ_EINT2, handle_irq, IRQ_TYPE_EDGE_FALLING, "K3",&pins_desc[2]);
	request_irq(IRQ_EINT0, handle_irq, IRQ_TYPE_EDGE_FALLING, "K4",&pins_desc[3]);
	return 0;
}

static int buttons_dev_close(struct inode *inode, struct file *file)
{
	//
	atomic_inc(&canopen);//释放原子变量
	//
	free_irq(IRQ_EINT1,&pins_desc[0]);
	free_irq(IRQ_EINT4,&pins_desc[1]);
	free_irq(IRQ_EINT2,&pins_desc[2]);
	free_irq(IRQ_EINT0,&pins_desc[3]);
	
	return 0;
}

static ssize_t buttons_dev_read(struct file *file, char __user *user, size_t size,loff_t *ppos)
{
	if (size != 1)
			return -EINVAL;
	wait_event_interruptible(button_waitq, ev_press);//没有按键就进入休眠
	copy_to_user(user, &key_val, 1);
	/* 将ev_press清零 */
	ev_press = 0;
	return 1;
}

/* File operations struct for character device */
static const struct file_operations buttons_dev_fops = {
	.owner		= THIS_MODULE,
	.open		= buttons_dev_open,
	.read		= buttons_dev_read,
	.release    = buttons_dev_close,
};

/* 驱动入口函数 */
static int buttons_dev_init(void)
{
	/* 主设备号设置为0表示由系统自动分配主设备号 */
	major = register_chrdev(0, "buttons_dev", &buttons_dev_fops);

	/* 创建buttondev类 */
	buttondev_class = class_create(THIS_MODULE, "buttondev");

	/* 在buttondev类下创建buttons设备,供应用程序打开设备*/
	buttons_device = device_create(buttondev_class, NULL, MKDEV(major, 0), NULL, "buttons");//

	return 0;
}

/* 驱动出口函数 */
static void buttons_dev_exit(void)
{
	unregister_chrdev(major, "buttons_dev");
	device_unregister(buttons_device);  //卸载类下的设备
	class_destroy(buttondev_class);		//卸载类
}

/* 模块加载和卸载函数的修饰 */
module_init(buttons_dev_init);  
module_exit(buttons_dev_exit); 

MODULE_AUTHOR("CLBIAO");
MODULE_DESCRIPTION("Just for Demon");
MODULE_LICENSE("GPL");  //遵循GPL协议
测试程序:

app_atomic.c

/* 文件的编译指令是arm-linux-gcc -static -o app_irq app_atomic.c */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>   //sleep 
#include <stdio.h>

int main(int argc, char **argv)
{
	int flag,fd; 
	unsigned char key_val;
	
	fd = open("/dev/buttons", O_RDWR); //申请外部引脚中断服务
	if (fd < 0)
	{
		printf("can't open!\n");//打开失败
		return -1;//返回
	}
	while(1)
	{
		read(fd,&key_val,1); 
   		printf("key_val = 0x%x\n",key_val); 
		sleep(3);
	}
	return 0;
}

运行结果:

(2)信号量: 只有得到信号量的进程才能执行临界区的代码。得不到的进程进入休眠状态,直到有条件获取信号量,或者被其他进程终止。
驱动程序的任务:
①定义信号量&初始化,通常使用下面的宏
DECLARE_MUTEX(name);//定义一个名为name的信号量,并初始化为1
②获取信号量

void down(struct semaphore * sem);//获得信号量sem,如果获取不到,这个函数会导致休眠,休眠 不可以被信号打断,即使是“kill -9 pid”也不能将他退出,不要在中断上下文中使用,因为它太固执了!

void down_interruptible(struct semaphore * sem);//进入睡眠状态的进程能被信号打断
void down_trylock(struct semaphore * sem);//尝试去获取信号量,成功返回0,失败返回非零,不会导致调用进程休眠,可用于中断上下文
“down”往下,让我想到:在资源上插一支旗子,获取他的人就把它拿下来,其他人就呵呵了^_^
③释放信号量
int up(struct semaphore * sem);//把信号插回去,然后唤醒沉睡的人
代码实现:
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/module.h>
#include <linux/device.h> 		//class_create
#include <mach/regs-gpio.h>		//S3C2410_GPF1 
#include <mach/hardware.h>
#include <linux/interrupt.h>  //wait_event_interruptible
#include <linux/fs.h>
#include <linux/semaphore.h>

/* 定义并初始化等待队列头 */
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);


static DECLARE_MUTEX(button_lock);//(1)获取信号量量并初始化为1


static struct class *buttondev_class;
static struct device *buttons_device;

static struct pin_desc{
	unsigned int pin;
	unsigned int key_val;
};

static struct pin_desc pins_desc[4] = {
		{S3C2410_GPF1,0x01}, //S3C2410_GPF1是对GPF1引脚这种“设备”的编号dev_id
		{S3C2410_GPF4,0x02},
		{S3C2410_GPF2,0x03},
		{S3C2410_GPF0,0x04},
}; 
static int ev_press = 0;
static unsigned char key_val;
int major;

/* 中断处理函数 */
static irqreturn_t handle_irq(int irq, void *dev_id)
{
	struct pin_desc *irq_pindesc = (struct pin_desc *)dev_id;//
	unsigned int pinval;
		
	pinval = s3c2410_gpio_getpin(irq_pindesc->pin);//获取按键值:有按键按下返回按键值0
	/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
	/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
	if(pinval)
	{
		/* 松开 */
		key_val = 0x80 | (irq_pindesc->key_val);
	}
	else
	{
		/* 按下 */
		key_val = irq_pindesc->key_val;
	}

	ev_press = 1;							/* 表示中断已经发生 */
	wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */
	return IRQ_HANDLED;
}

static int buttons_dev_open(struct inode * inode, struct file * filp)
{
	/
	down(&button_lock);//(2)获取信号量
	
	request_irq(IRQ_EINT1, handle_irq, IRQ_TYPE_EDGE_FALLING, "K1",&pins_desc[0]);
	request_irq(IRQ_EINT4, handle_irq, IRQ_TYPE_EDGE_FALLING, "K2",&pins_desc[1]);
	request_irq(IRQ_EINT2, handle_irq, IRQ_TYPE_EDGE_FALLING, "K3",&pins_desc[2]);
	request_irq(IRQ_EINT0, handle_irq, IRQ_TYPE_EDGE_FALLING, "K4",&pins_desc[3]);
	return 0;
}

static int buttons_dev_close(struct inode *inode, struct file *file)
{
	/
	up(&button_lock);//(3)释放信号量
	
	free_irq(IRQ_EINT1,&pins_desc[0]);
	free_irq(IRQ_EINT4,&pins_desc[1]);
	free_irq(IRQ_EINT2,&pins_desc[2]);
	free_irq(IRQ_EINT0,&pins_desc[3]);
	
	return 0;
}

static ssize_t buttons_dev_read(struct file *file, char __user *user, size_t size,loff_t *ppos)
{
	if (size != 1)
			return -EINVAL;
	wait_event_interruptible(button_waitq, ev_press);//没有按键就进入休眠
	copy_to_user(user, &key_val, 1);
	/* 将ev_press清零 */
	ev_press = 0;
	return 1;
}

/* File operations struct for character device */
static const struct file_operations buttons_dev_fops = {
	.owner		= THIS_MODULE,
	.open		= buttons_dev_open,
	.read		= buttons_dev_read,
	.release    = buttons_dev_close,
};

/* 驱动入口函数 */
static int buttons_dev_init(void)
{
	/* 主设备号设置为0表示由系统自动分配主设备号 */
	major = register_chrdev(0, "buttons_dev", &buttons_dev_fops);

	/* 创建buttondev类 */
	buttondev_class = class_create(THIS_MODULE, "buttondev");

	/* 在buttondev类下创建buttons设备,供应用程序打开设备*/
	buttons_device = device_create(buttondev_class, NULL, MKDEV(major, 0), NULL, "buttons");//

	return 0;
}

/* 驱动出口函数 */
static void buttons_dev_exit(void)
{
	unregister_chrdev(major, "buttons_dev");
	device_unregister(buttons_device);  //卸载类下的设备
	class_destroy(buttondev_class);		//卸载类
}

/* 模块加载和卸载函数的修饰 */
module_init(buttons_dev_init);  
module_exit(buttons_dev_exit); 

MODULE_AUTHOR("CLBIAO");
MODULE_DESCRIPTION("Just for Demon");
MODULE_LICENSE("GPL");  //遵循GPL协议

测试程序和前面一个一样。

运行结果:

      

(3)自旋锁:就是得不到自旋锁就“原地打转”,进程一直跑不过是原地循环
使用和信号量很相似,主要有:定义、初始化、获得、释放操作。
(4)中断屏蔽:让系统不能进行中断,这样就不会切换到其他进程来抢夺资源了。
使用方法:
①屏蔽中断
local_irq_disable();
②临界区安安心心地访问资源
③开中断
local_irq_enable();
二、阻塞和非阻塞
阻塞:不能获得资源时挂起进程并进入睡眠状态,直到资源到来。之前的驱动都是阻塞方式打开的。这里不再进行列举程序。
app:open("...",O_RDWR);//open默认就是阻塞的方式打开
非阻塞:无资源不挂起进程,之后放弃或不停的查询。通常和信号量配合使用。

app:open("...",O_RDWR|O_NONBLOCK);

非阻塞方式例程序

nonblock.c文件

#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/module.h>
#include <linux/device.h> 		//class_create
#include <mach/regs-gpio.h>		//S3C2410_GPF1 
#include <mach/hardware.h>
#include <linux/interrupt.h>  //wait_event_interruptible
#include <linux/fs.h>
#include <linux/semaphore.h>

/* 定义并初始化等待队列头 */
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);


static DECLARE_MUTEX(button_lock);//(1)获取信号量量并初始化为1


static struct class *buttondev_class;
static struct device *buttons_device;

static struct pin_desc{
	unsigned int pin;
	unsigned int key_val;
};

static struct pin_desc pins_desc[4] = {
		{S3C2410_GPF1,0x01}, //S3C2410_GPF1是对GPF1引脚这种“设备”的编号dev_id
		{S3C2410_GPF4,0x02},
		{S3C2410_GPF2,0x03},
		{S3C2410_GPF0,0x04},
}; 
static int ev_press = 0;
static unsigned char key_val;
int major;

/* 中断处理函数 */
static irqreturn_t handle_irq(int irq, void *dev_id)
{
	struct pin_desc *irq_pindesc = (struct pin_desc *)dev_id;//
	unsigned int pinval;
		
	pinval = s3c2410_gpio_getpin(irq_pindesc->pin);//获取按键值:有按键按下返回按键值0
	/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
	/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
	if(pinval)
	{
		/* 松开 */
		key_val = 0x80 | (irq_pindesc->key_val);
	}
	else
	{
		/* 按下 */
		key_val = irq_pindesc->key_val;
	}

	ev_press = 1;							/* 表示中断已经发生 */
	wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */
	return IRQ_HANDLED;
}

static int buttons_dev_open(struct inode * inode, struct file * filp)
{
	/
	if (file->f_flags & O_NONBLOCK)//判断是不是非阻塞方式
	{
		if (down_trylock(&button_lock))//尝试获取信号量,没有就走人,不等待!
			return -EBUSY;
	}
	else  //是阻塞方式就使用down函数,这样可以进入睡眠
	{
		/* 获取信号量 */
		down(&button_lock);
	}
	
	request_irq(IRQ_EINT1, handle_irq, IRQ_TYPE_EDGE_FALLING, "K1",&pins_desc[0]);
	request_irq(IRQ_EINT4, handle_irq, IRQ_TYPE_EDGE_FALLING, "K2",&pins_desc[1]);
	request_irq(IRQ_EINT2, handle_irq, IRQ_TYPE_EDGE_FALLING, "K3",&pins_desc[2]);
	request_irq(IRQ_EINT0, handle_irq, IRQ_TYPE_EDGE_FALLING, "K4",&pins_desc[3]);
	return 0;
}

static int buttons_dev_close(struct inode *inode, struct file *file)
{
	/
	up(&button_lock);//(3)释放信号量
	
	free_irq(IRQ_EINT1,&pins_desc[0]);
	free_irq(IRQ_EINT4,&pins_desc[1]);
	free_irq(IRQ_EINT2,&pins_desc[2]);
	free_irq(IRQ_EINT0,&pins_desc[3]);
	
	return 0;
}

static ssize_t buttons_dev_read(struct file *file, char __user *user, size_t size,loff_t *ppos)
{
	if (size != 1)
			return -EINVAL;
	/
	if (file->f_flags & O_NONBLOCK) //读不到按键值立马返回,不睡眠!
	{
		if (!ev_press)
			return -EAGAIN;
	}
	else
		wait_event_interruptible(button_waitq, ev_press);//没有按键就进入休眠
	///
	copy_to_user(user, &key_val, 1);
	/* 将ev_press清零 */
	ev_press = 0;
	
	return 1;
}

/* File operations struct for character device */
static const struct file_operations buttons_dev_fops = {
	.owner		= THIS_MODULE,
	.open		= buttons_dev_open,
	.read		= buttons_dev_read,
	.release    = buttons_dev_close,
};

/* 驱动入口函数 */
static int buttons_dev_init(void)
{
	/* 主设备号设置为0表示由系统自动分配主设备号 */
	major = register_chrdev(0, "buttons_dev", &buttons_dev_fops);

	/* 创建buttondev类 */
	buttondev_class = class_create(THIS_MODULE, "buttondev");

	/* 在buttondev类下创建buttons设备,供应用程序打开设备*/
	buttons_device = device_create(buttondev_class, NULL, MKDEV(major, 0), NULL, "buttons");//

	return 0;
}

/* 驱动出口函数 */
static void buttons_dev_exit(void)
{
	unregister_chrdev(major, "buttons_dev");
	device_unregister(buttons_device);  //卸载类下的设备
	class_destroy(buttondev_class);		//卸载类
}

/* 模块加载和卸载函数的修饰 */
module_init(buttons_dev_init);  
module_exit(buttons_dev_exit); 

MODULE_AUTHOR("CLBIAO");
MODULE_DESCRIPTION("Just for Demon");
MODULE_LICENSE("GPL");  //遵循GPL协议

加油!更上一层楼!生气































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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值