互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。
下面说一下如何实现同步。
先提出一个方法,也是很容易想到的,那就是增加一个信号标志。可以先定义一个静态变量canopen=1,在open函数中加上
if(!--canopen==0){
canopen++;
return -EBUSY;
}
然后在close函数里加上canopen++。当第一次打开的时候if的条件不成立,所以会跳过;第二次打开的时候,进入if里,然后返回-EBUSY。但实际上这个方法有不足之处,那就是if语句的条件并不是原子的,可以拆分成读取、修改、写回三个步骤,假如在修改canopen值但还没有写回的时候程序发生了进程切换,而另一个程序恰好也要使用资源进入临界区,那么等到进程再切换回来的时候第一个应用程序并没有意识到第二个程序已经进入了临界区,如果再进行操作的话可能就会产生错误。虽然这个的可能并不是很大,但是还是客观存在的。
下面提出一种解决方法,就是使用原子操作。
可以首先定义一个原子变量
static atomic_t canopen = ATOMIC_INIT(1);
在open函数里将上一个if替换成
if(!atomic_dec_and_test(&canopen))
{
atomic_inc(&canopen);
return -EBUSY;
}
然后在close里加上atomic_inc(&canopen);这样是实现同步的第一个方法。
再提出一个方法,使用信号量。先定义并初始化一个信号量static DECLARE_MUTEX(button_lock);在open的时候获得信号量down(&button_lock);在close的时候释放信号量up(&button_lock);这样就可以完成同步了。
阻塞和非阻塞:
阻塞操作
是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。
非阻塞操作
进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。
其实上面的同步已经实现了阻塞操作。那么如何体现出阻塞和非阻塞呢?在应用程序open函数里确定,fd = open(“/dev/buttons”, O_RDWR | O_NONBLOCK);如果参数里包含fd = open(“/dev/buttons”, O_RDWR | O_NONBLOCK);那么将会执行非阻塞操作,否则默认是阻塞操作。下面给出一个实现非阻塞的例子。
在open函数中加入
if(file->f_flags & O_NONBLOCK)
{
//非阻塞操作会执行下面的语句
if(down_trylock(&button_lock))
return -EBUSY;
}
else
{
//阻塞操作会执行下面的语句
down(&button_lock);
}
在read函数里添加
if(file->f_flags & O_NONBLOCK)
{
//非阻塞操作执行下面的语句
if(!ev_press)
return -EAGAIN;
}
else
{
//阻塞操作执行下面的语句
wait_event_interruptible(button_waitq, ev_press);
}