同步互斥:
目的:在同一时间只有一个应用程序打开设备节点/dev/buttons。linux是一个多任务系统,在读写同一个节点的时候,可能会有多个应用程序。这就需要进行同步操作,保证应用程序的完整执行。
方法1:在linux中使用原子操作:
1. 原子操作
原子操作指的是在执行过程中不会被别的代码路径所中断的操作。
常用原子操作函数举例:
atomic_t v = ATOMIC_INIT(0); //定义原子变量v并初始化为0
atomic_read(atomic_t *v); //返回原子变量的值
void atomic_inc(atomic_t *v); //原子变量增加1
void atomic_dec(atomic_t *v); //原子变量减少1
int atomic_dec_and_test(atomic_t *v); //自减操作后测试其是否为0,为0则返回true,否则返回false。
基于之前的代码,修改如下:
static atomic_t canopen = ATOMIC_INIT(1);
static int buttons_drv_open(struct inode *inode, struct file *file)
{
if(!atomic_dec_and_test(&canopen)){
atomic_inc(&canopen);
return -EBUSY;
}
printk("buttons_drv_open \n");
request_irq(IRQ_EINT(0),button_irq_handle,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "PAD1", &pins_desc[0]);
request_irq(IRQ_EINT(1),button_irq_handle,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "PAD2", &pins_desc[1]);
request_irq(IRQ_EINT(2),button_irq_handle,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "PAD3", &pins_desc[2]);
request_irq(IRQ_EINT(3),button_irq_handle,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "PAD4", &pins_desc[3]);
return 0;
}
在释放的时候:
int buttons_drv_release(struct inode *inode, struct file *file)
{
atomic_inc(&canopen);
free_irq(IRQ_EINT(0),&pins_desc[0]);
free_irq(IRQ_EINT(1),&pins_desc[1]);
free_irq(IRQ_EINT(2),&pins_desc[2]);
free_irq(IRQ_EINT(3),&pins_desc[3]);
return 0;
}
现象如下:
方法2:信号量:
信号量(semaphore)是用于保护临界区的一种常用方法,只有得到信号量的进程才能执行临界区代码。当获取不到信号量时,进程进入休眠等待状态。
定义信号量
struct semaphore sem;
初始化信号量
void sema_init (struct semaphore *sem, int val);
void init_MUTEX(struct semaphore *sem);//初始化为0
static DECLARE_MUTEX(button_lock); //定义互斥锁
获得信号量
void down(struct semaphore * sem);
int down_interruptible(struct semaphore * sem);
int down_trylock(struct semaphore * sem);
释放信号量
void up(struct semaphore * sem);
代码修改如下:
static DECLARE_MUTEX(button_lock);
static int buttons_drv_open(struct inode *inode, struct file *file)
{
down(&button_lock);
printk("buttons_drv_open \n");
request_irq(IRQ_EINT(0),button_irq_handle,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "PAD1", &pins_desc[0]);
request_irq(IRQ_EINT(1),button_irq_handle,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "PAD2", &pins_desc[1]);
request_irq(IRQ_EINT(2),button_irq_handle,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "PAD3", &pins_desc[2]);
request_irq(IRQ_EINT(3),button_irq_handle,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "PAD4", &pins_desc[3]);
return 0;
}
int buttons_drv_release(struct inode *inode, struct file *file)
{
free_irq(IRQ_EINT(0),&pins_desc[0]);
free_irq(IRQ_EINT(1),&pins_desc[1]);
free_irq(IRQ_EINT(2),&pins_desc[2]);
free_irq(IRQ_EINT(3),&pins_desc[3]);
up(&button_lock);
return 0;
}
现象如下:
执行两次./synctest &
ps命令:
其中一个会进行休眠,当前面的synctest结束后,后面的synctest会立即从休眠中唤醒立即执行。
阻塞与非阻塞:
阻塞操作
是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。
非阻塞操作
进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。
fd = open("...", O_RDWR | O_NONBLOCK);