下面的内容对学习过程中做一个简单的总结,方便以后回忆,内容可能过于简陋
1. 原子操作
如果我们希望字符驱动程序每次只能给一个应用程序打开,就需要加锁,比如在驱动程序中加一个整型的全局变量canopen:
1代表可以open,0代表不能open。
但是我们不能简单的给整型变量canopen++或者canopen--,因为代码中简单的自加动作,在汇编代码中就要分成3步完成:
ldr ... 读出
add ... 加操作
str ... 回写值
假如A、B两个程序同时操作变量canopen = 1,在多任务的情况下就会有问题,比如:
A B
读出canopen,此时值为1
被切换出执行队列 读出canopen,此时值为1
canopen--,此时值为0
open字符设备成功
此时A程序重新开始执行,进行canopen--,值为0
open字符设备也成功
由上面的分析可知,简单的加减是不可靠的,可以使用原子操作:
原子操作指的是在执行过程中不会被别的代码路径所中断的操作。
常用原子操作函数举例:
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。
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); //定义互斥锁
open时获得信号量:
void down(struct semaphore * sem); //down(&button_lock) //非第一个获取信号量的程序都要进入休眠状态,直到第一个获取信号量的程序释放锁
int down_interruptible(struct semaphore * sem); //加了interruptible,说明就算休眠了也能被别人打断
int down_trylock(struct semaphore * sem); //down_trylock(&button_lock)
close时释放信号量:
void up(struct semaphore * sem); //up(&button_lock);
3. 阻塞
阻塞操作
是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。
被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。
非阻塞操作
进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。
fd = open("...", O_RDWR | O_NONBLOCK); //传入O_NONBLOCK标志位给底层驱动程序
在驱动程序中就可以这么使用:
if (file->f_flags & O_NONBLOCK)
{
if (没有检测事件的发生)
return -EBUSY;
}
else
{
//进入休眠的状态
wait_event_interruptible(...);
}