字符驱动程序之——同步互斥阻塞

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。

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);

3. 阻塞
阻塞操作
是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。
被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。

非阻塞操作
进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。

fd = open("...", O_RDWR | O_NONBLOCK);

 利用原子访问实现驱动层函数只被一个任务调用(有同步线程的功能):

首先定义一个原子变量初始值为1:

在open函数中增加判断,这个是原子操作:

如果是第一次调用,if就不会满足,第一次的时候自减操作为0,自减函数为0返回true,取反就是false,不执行。

当open函数同时被其他进程再次调用时,由于第一次调用的进程没有释放资源,自减操作为-1,if条件满足,先恢复之前原子变量的状态,然后返回错误代码我忙碌的状态,此时应用程序可以得到不能打开该文件的提示。

APP测试程序如下:

终端现象入下:

多次后台运行应用程序会出现不能打开的提示,第一次则不会。同时按键依旧有效。

利用互斥机制实现同一个应用程序同时只被一个进程调用:

先定义一个互斥信号量:

 在open函数中获取信号量:第一次来获取的时候,没有任何问题,在没有释放的情况下又一个线程来获取信号量的时候就需要等待第一次获取的线程释放了之后才可以。

在close函数中释放信号量:

应用程序后台运行并查看PID,此时为809:

 再次后台运行应用程序,并查看PID,此时为811:

 注意,这里的809,也就是第一次后台运行的时候,是处于S状态(可中断的睡眠状态),而在第一次没有释放信号量的时候,第二次后台运行应用程序显示的是D状态(不可中断状态,不是cpu不响应外部中断,而是该进程不响应异步信号)。

什么是不可中断状态呢?比如这里我们要kill掉这个进程,但由于811处于D状态,是不响应kill信号的。

现在我们把第一个进程809给kill掉(释放了信号量),此时会发现811变成了S状态,可以被异步信号触发了。

实验的时候意外发现,虽然刚才我们kill 811被拒绝了,但是当我kill 809之后,811被自动kill了,说明linux这里有保存操作的功能。

在互斥信号的时候不释放就再次打开不会提示can't open.而是进入休眠状态。

阻塞与非阻塞:

在open中判断应用程序是不是非阻塞的读取:

down_trylock如果无法获取信号量,立即返回。

应用程序,使用非阻塞的情况,阻塞情况前面的学习中已经实验过了:

终端显示:

没有按键下的时候,ret一直为-1

有按键的时候:

有按键时候,就不会返回-1.

but,是不是和我在看韦老师实验的时候一样,如同上图一样,明明有些地方有按键值,但是ret却是-1啊!!!

难道是驱动写出bug了?可韦老师的也是这样的现象啊。

Answer:

bug是有,但不是这里的非阻塞驱动,是我们按键程序,在按键按下之后是一个值,松开之后是另外一个,在没有按键的时候,应该将这个按键值清零才对。

 更改read驱动函数如下:

 这样更改了之后,没有按键的时候就是0键值,ret为-1,有按键的时候就是返回键值,ret为1.

进步是有的,能够发现老师代码bug了。每天给自己一点鼓励,前路漫漫其修远,吾将上下而求索。

韦老师也说了更加深入的知识点需要看更多的书籍,这里只是简单的入门介绍。

原文地址:https://www.cnblogs.com/yangguang-it/p/8976496.html