【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
Linux中的同步互斥
1. 什么是同步?
生活中的例子:
洗菜必须在买菜之后, 做菜必须在洗菜之后, 吃饭必须在做菜之后.
软件上的概念:
指的是散落在不同线程/进程中的代码片段, 他们的执行顺序必须是有序, 因为B代码片段的执行依赖着A代码片段的结果, 同理C代码片段的执行依赖着B代码片段的结果.
那么, 怎么实现ABC的有序执行呢? 这就是同步的任务. 同步用于保证这几个代码片段的执行是有序的.
2. 什么是互斥?
生活中的例子:
同一时间, 茅坑(马桶)只能有一个人蹲着, 人在进入厕所时会把门锁上, 在上一个人没有出来前, 其他人没办法进去. 这就是互斥.如果你一定要用这个厕所, 但是发现厕所已经被其他人使用着, 根据你的反应, 存在两种情况:
- 在门口等, 等上一个人使用完立刻进去; 这个叫阻塞, 期间你没办法做任何事;
- 过一会再回来看看是否可以上厕所, 这个叫非阻塞, 期间你可以做其他事情, 例如给阿姨倒一杯卡布奇洛.
软件上的概念:
针对系统的某些资源, 同一时间内只允许一个进程/线程进行访问, 当资源被占用时, 其它进程/线程不得占用, 这就是互斥. 当有一个进程/线程, 需要访问该资源, 而该资源被其它进程/线程占用时, 有存在两种情况:
- 进程/线程原地等待(自旋等待/睡眠等待), 期间不会做其它事情, 直到获取到该资源. 这个就是阻塞;
- 进程/线程先去做其他事情, 等会再次查询该资源是否可用, 这个就是非阻塞.
3. 同步与互斥的关系
个人观点: 同步互斥更像一种兄弟关系, 没必要分谁是谁的什么, 但是两者通常一起出现.
买菜->洗菜->做菜->吃饭, 这个流程中必须保持同步(有序进行), 但是过程中并不互斥, 哪一个地方, 都可以插入多个人, 但是菜同一时间可能只有一个人碰. 同步中存在着互斥.
上厕所为例子, 你必须先锁上门, 然后才开始上厕所. 互斥中存在着同步.
代码也源于生活, 不是吗?
4. Linux同步互斥的代码实现
4.1 应用层
int fd = open("/dev/yangbkDevice", O_RDWR | O_NONBLOCK); // 非阻塞方式打开文件, 默认是阻塞
int ret = read(fd, &value, 4);
4.2 驱动层
使用原子变量: (同一时间内只有一个进程/线程可以修改它的值, 通过这个变量的数值改变来进行判断, 在原子变量改变的前后只有一个进程/线程访问这段代码/资源)
//定义原子变量并初始化为1
static atomic_t canopen = ATOMIC_INIT(1);
// 在驱动的open()函数中, 当需要执行临界区的代码/访问敏感资源时, 试图修改原子变量以试图得到权限.
/**
* atomic_dec_and_test - decrement and test
* @v: pointer of type atomic_t
*
* Atomically decrements @v by 1 and
* returns true if the result is 0, or false for all other
* cases.
*/
if (!atomic_dec_and_test(&canopen))
{
atomic_inc(&canopen);
return -EBUSY;
}
// 操作/资源访问完成时, 释放权限.
atomic_inc(&canopen);
使用互斥锁:
//定义互斥锁
static DECLARE_MUTEX(button_lock);
// 在驱动的open()函数中
static int btn_open(struct inode *inode, struct file *file)
{
if (file->f_flags & O_NONBLOCK)
{ // 判断fd是否以非阻塞方式打开, 若是以非阻塞方式打开, 不能立刻获得互斥锁则直接返回.
if (down_trylock(&button_lock))
return -EBUSY;
}
else
{ // 否则是以阻塞方式打开, 故在原地等待锁被释放被获取锁.
down(&button_lock);
}
}
tips: 在驱动的read()函数中, 也需要判断fd是否以非阻塞方式打开, 也需要遵循O_NONBLOCK
// 释放互斥锁
up(&button_lock);
5. 总结
同步与互斥只是一个概念, 需要程序员去遵循的规则, 以防止程序跑飞.