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); //定义原子变量并初始化为1
在“open”操作中:
if (!atomic_dec_and_test(&canopen))
{
atomic_inc(&canopen);
return -EBUSY;
}
“canopen”一开始是“1”,自减后判断它是否为“0”。是“0”则为“true”,这里前面加个
“!”非,所以“if”中的分支不执行。
假设“canopen”已经被调用过一次变成“0”了,则“atomic_dec_and_test(&canopen)”中
自减操作后变成“-1”了,则为“false”。
加上“!”非,则“if(true)”会执行 if 分支。在分支中将“canopen”自加一次。
在“close”设备中:
atomic_inc(&canopen);
这样就不会发生“A 程序执行时被 B 程序切换出去”的情况。汇编过程中的“读出-修改-
写回”在“int atomic_dec_and_test(atomic_t *v)”一次性完成。中间不会被打断。这就是“原
子变量”。
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);
就是在操作之前申请一个“信号量”。申请不到,则要么等待,要么休眠;如果申请到了“信
号量”则继续往下执行代码。操作完毕,要释放这个信号量。这时若有其他程序在等待信号
量,就去唤醒那个应用程序。
①,先定义“信号量”:
在加载函数中初始化信号量
②,在“open”中获取“信号量”。
如果是第一次执行“open”时,这里便会获取到“信号量”。若是另一个程序又来调用“open”
设备,这时
down(&button_lock);是无法获取信号量的。
第一个程序执行完成后,这个信号量就要释放出来给其他程序“open”这个设备时用。释放
是在“close”函数中
3. 阻塞
阻塞操作
是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。
被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。
非阻塞操作
进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为
止。
fd = open("…", O_RDWR | O_NONBLOCK);
目标:同一时刻只能有一个应用程序打开驱动程序/dev/buttons。
fd = open("…", O_RDWR | O_NONBLOCK);
如想读一个按键值,若当前没有按键按下,就一直等待有按下才返回,这是“阻塞操作”;
“非阻塞操作”是指若想读一个按键值,若没有按键值可读,则立刻返回一个错误。
分辨他们,是在 open 设备时,加入一个参数。若传入“ O_NONBLOCK”就是非阻塞操作,
若不传入“ O_NONBLOCK”标记时,
默认为“阻塞操作”。对于“阻塞、非阻塞”,驱动程序要对这个“ O_NONBLOCK”进行处
理。
这个“O_NONBLOCK”标记就是在上面“file”结构中获取。这个结构是内核提供的。如果
“file->f_flags”是“ O_NONBLOCK”时,
是“非阻塞”操作,否则就是“阻塞”操作。
如果它无法获取这个“信号量”,这个“down(&button_lock);”会陷入休眠。如果“open”打
不开,立刻返回一个错误,
就用了“down_trylock(&button_lock)”这个函数。表示如果无法获取到“信号量”就返回“return
-EBUSY”.
“read”中也要做这样的事: