互斥体

互斥体是一种睡眠锁,他是一种简单的睡眠锁,其行为和 count 为 1 的信号量类似。(关于信号量参考:Linux 内核同步(四):信号量 semaphore)。

互斥体简洁高效,但是相比信号量,有更多的限制,因此对于互斥体的使用条件更加严格:

任何时刻,只有一个指定的任务允许持有 mutex,也就是说,mutex 的计数永远是 1;

给 mutex 上锁这,必须负责给他解锁,也就是不允许在一个上下文中上锁,在另外一个上下文中解锁。这个限制注定了 mutex 无法承担内核和用户空间同步的复杂场景。常用的方式是在一个上下文中进行上锁/解锁。

递归的调用上锁和解锁是不允许的。也就是说,不能递归的去持有同一个锁,也不能够递归的解开一个已经解开的锁。

当持有 mutex 的进程,不允许退出

mutex 不允许在中断上下文和软中断上下文中使用过,即便是mutex_trylock 也不行

mutex 只能使用内核提供的 APIs操作,不允许拷贝,手动初始化和重复初始化

信号量和互斥体

他们两者很相似,除非是 mutex 的限制妨碍到逻辑,否则这两者之间,首选 mutex

自旋锁和互斥体

多数情况,很好区分。中断中只能考虑自旋锁,任务睡眠使用互斥体。如果都可以的的情况下,低开销或者短时间的锁,选择自旋锁,长期加锁的话,使用互斥体。

互斥体的使用

函数定义

功能说明

mutex_lock(struct mutex *lock)

加锁,如果不可用,则睡眠(UNINTERRUPTIBLE)

mutex_lock_interruptible(struct mutex *lock);

加锁,如果不可用,则睡眠(TASK_INTERRUPTIBLE)

mutex_unlock(struct mutex *lock)

解锁

mutex_trylock(struct mutex *lock)

试图获取指定的 mutex,或得到返回1,否则返回 0

mutex_is_locked(struct mutex *lock)

如果 mutex 被占用返回1,否则返回 0

互斥体的实现

互斥体的定义在:include/linux/mutex.h

1. mutex 的结构

struct mutex {

atomic_long_towner;

spinlock_twait_lock;

#ifdef CONFIG_MUTEX_SPIN_ON_OWNER

struct optimistic_spin_queue osq; /* Spinner MCS lock */

#endif

struct list_headwait_list;

#ifdef CONFIG_DEBUG_MUTEXES

void*magic;

#endif

#ifdef CONFIG_DEBUG_LOCK_ALLOC

struct lockdep_mapdep_map;

#endif

};

2. mutex 初始化

void

__mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key)

{

atomic_long_set(&lock->owner, 0);

spin_lock_init(&lock->wait_lock);

INIT_LIST_HEAD(&lock->wait_list);

#ifdef CONFIG_MUTEX_SPIN_ON_OWNER

osq_lock_init(&lock->osq);

#endif

debug_mutex_init(lock, name, key);

}

EXPORT_SYMBOL(__mutex_init);

3. mutex 加锁

void __sched mutex_lock(struct mutex *lock)

{

might_sleep();

if (!__mutex_trylock_fast(lock))

__mutex_lock_slowpath(lock);

}

首先 check 是否能够获得锁,否则调用到 __mutex_lock_slowpath:

static noinline void __sched

__mutex_lock_slowpath(struct mutex *lock)

{

__mutex_lock(lock, TASK_UNINTERRUPTIBLE, 0, NULL, _RET_IP_);

}

static int __sched

__mutex_lock(struct mutex *lock, long state, unsigned int subclass,

struct lockdep_map *nest_lock, unsigned long ip)

{

return __mutex_lock_common(lock, state, subclass, nest_lock, ip, NULL, false);

}

所以调用到了 __mutex_lock_common 函数:

static __always_inline int __sched

__mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,

struct lockdep_map *nest_lock, unsigned long ip,

struct ww_acquire_ctx *ww_ctx, const bool use_ww_ctx)

{

struct mutex_waiter waiter;

bool first = false;

struct ww_mutex *ww;

int ret;

might_sleep();

ww = container_of(lock, struct ww_mutex, base);

if (use_ww_ctx && ww_ctx) {

if (unlikely(ww_ctx == READ_ONCE(ww->ctx)))

return -EALREADY;

/*

* Reset the wounded flag after a kill. No other process can

* race and wound us here since they can't have a valid owner

* pointer if we don't have any locks held.

*/

if (ww_ctx->acquired == 0)

ww_ctx->wounded = 0;

}

preempt_disable();

mutex_acquire_nest(&lock->dep_map, subclass, 0, nest_lock, ip);

if (__mutex_trylock(lock) ||

mutex_optimistic_spin(lock, ww_ctx, use_ww_ctx, NULL)) {

/* got the lock, yay! */

lock_acquired(&lock->dep_map, ip);

if (use_ww_ctx && ww_ctx)

ww_mutex_set_context_fastpath(ww, ww_ctx);

preempt_enable();

return 0;

}

spin_lock(&lock->wait_lock);

/*

* After waiting to acquire the wait_lock, try again.

*/

if (__mutex_trylock(lock)) {

if (use_ww_ctx && ww_ctx)

__ww_mutex_check_waiters(lock, ww_ctx);

goto skip_wait;

}

debug_mutex_lock_common(lock, &waiter);

lock_contended(&lock->dep_map, ip);

if (!use_ww_ctx) {

/* add waiting tasks to the end of the waitqueue (FIFO): */

__mutex_add_waiter(lock, &waiter, &lock->wait_list);

#ifdef CONFIG_DEBUG_MUTEXES

waiter.ww_ctx = MUTEX_POISON_WW_CTX;

#endif

} else {

/*

* Add in stamp order, waking up waiters that must kill

* themselves.

*/

ret = __ww_mutex_add_waiter(&waiter, lock, ww_ctx);

if (ret)

goto err_early_kill;

waiter.ww_ctx = ww_ctx;

}

waiter.task = current;

set_current_state(state);

for (;;) {

/*

* Once we hold wait_lock, we're serialized against

* mutex_unlock() handing the lock off to us, do a trylock

* before testing the error conditions to make sure we pick up

* the handoff.

*/

if (__mutex_trylock(lock))

goto acquired;

/*

* Check for signals and kill conditions while holding

* wait_lock. This ensures the lock cancellation is ordered

* against mutex_unlock() and wake-ups do not go missing.

*/

if (unlikely(signal_pending_state(state, current))) {

ret = -EINTR;

goto err;

}

if (use_ww_ctx && ww_ctx) {

ret = __ww_mutex_check_kill(lock, &waiter, ww_ctx);

if (ret)

goto err;

}

spin_unlock(&lock->wait_lock);

schedule_preempt_disabled();

/*

* ww_mutex needs to always recheck its position since its waiter

* list is not FIFO ordered.

*/

if ((use_ww_ctx && ww_ctx) || !first) {

first = __mutex_waiter_is_first(lock, &waiter);

if (first)

__mutex_set_flag(lock, MUTEX_FLAG_HANDOFF);

}

set_current_state(state);

/*

* Here we order against unlock; we must either see it change

* state back to RUNNING and fall through the next schedule(),

* or we must see its unlock and acquire.

*/

if (__mutex_trylock(lock) ||

(first && mutex_optimistic_spin(lock, ww_ctx, use_ww_ctx, &waiter)))

break;

spin_lock(&lock->wait_lock);

}

spin_lock(&lock->wait_lock);

acquired:

__set_current_state(TASK_RUNNING);

if (use_ww_ctx && ww_ctx) {

/*

* Wound-Wait; we stole the lock (!first_waiter), check the

* waiters as anyone might want to wound us.

*/

if (!ww_ctx->is_wait_die &&

!__mutex_waiter_is_first(lock, &waiter))

__ww_mutex_check_waiters(lock, ww_ctx);

}

mutex_remove_waiter(lock, &waiter, current);

if (likely(list_empty(&lock->wait_list)))

__mutex_clear_flag(lock, MUTEX_FLAGS);

debug_mutex_free_waiter(&waiter);

skip_wait:

/* got the lock - cleanup and rejoice! */

lock_acquired(&lock->dep_map, ip);

if (use_ww_ctx && ww_ctx)

ww_mutex_lock_acquired(ww, ww_ctx);

spin_unlock(&lock->wait_lock);

preempt_enable();

return 0;

err:

__set_current_state(TASK_RUNNING);

mutex_remove_waiter(lock, &waiter, current);

err_early_kill:

spin_unlock(&lock->wait_lock);

debug_mutex_free_waiter(&waiter);

mutex_release(&lock->dep_map, 1, ip);

preempt_enable();

return ret;

}

进入等待队列。

4. mutex 解锁

void __sched mutex_unlock(struct mutex *lock)

{

#ifndef CONFIG_DEBUG_LOCK_ALLOC

if (__mutex_unlock_fast(lock))

return;

#endif

__mutex_unlock_slowpath(lock, _RET_IP_);

}

EXPORT_SYMBOL(mutex_unlock);

调用到 __mutex_unlock_slowpath :

static noinline void __sched __mutex_unlock_slowpath(struct mutex *lock, unsigned long ip)

{

struct task_struct *next = NULL;

DEFINE_WAKE_Q(wake_q);

unsigned long owner;

mutex_release(&lock->dep_map, 1, ip);

/*

* Release the lock before (potentially) taking the spinlock such that

* other contenders can get on with things ASAP.

*

* Except when HANDOFF, in that case we must not clear the owner field,

* but instead set it to the top waiter.

*/

owner = atomic_long_read(&lock->owner);

for (;;) {

unsigned long old;

#ifdef CONFIG_DEBUG_MUTEXES

DEBUG_LOCKS_WARN_ON(__owner_task(owner) != current);

DEBUG_LOCKS_WARN_ON(owner & MUTEX_FLAG_PICKUP);

#endif

if (owner & MUTEX_FLAG_HANDOFF)

break;

old = atomic_long_cmpxchg_release(&lock->owner, owner,

__owner_flags(owner));

if (old == owner) {

if (owner & MUTEX_FLAG_WAITERS)

break;

return;

}

owner = old;

}

spin_lock(&lock->wait_lock);

debug_mutex_unlock(lock);

if (!list_empty(&lock->wait_list)) {

/* get the first entry from the wait-list: */

struct mutex_waiter *waiter =

list_first_entry(&lock->wait_list,

struct mutex_waiter, list);

next = waiter->task;

debug_mutex_wake_waiter(lock, waiter);

wake_q_add(&wake_q, next);

}

if (owner & MUTEX_FLAG_HANDOFF)

__mutex_handoff(lock, next);

spin_unlock(&lock->wait_lock);

wake_up_q(&wake_q);

}

做唤醒操作。

linux 内核互斥体,Linux 内核同步(六):互斥体(mutex)相关推荐

  1. Linux系统编程----16(线程同步,互斥量 mutex,互斥锁的相关函数,死锁,读写锁)

    同步概念 所谓同步,即同时起步,协调一致.不同的对象,对"同步"的理解方式略有不同.如,设备同步,是指在两 个设备之间规定一个共同的时间参考:数据库同步,是指让两个或多个数据库内容 ...

  2. java 信号量 互斥锁_线程同步(互斥锁与信号量的作用与区别)

    "信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在semtake的时候,就阻塞在 哪里).而互斥锁是用在多线程多任务互斥的,一 ...

  3. Linux宏定义实现类成员函数,全面解析Linux内核的同步与互斥机制

    http://blog.csdn.net/sailor_8318/archive/2008/06/30/2599357.aspx [摘 要]本文分析了内核的同步及互斥的几种机制:原子运算符(atomi ...

  4. 2017-2018-1 20179209《Linux内核原理与分析》第六周作业

    一.分析system_call中断处理过程 实验 下载最新menu,并在test.c中增加mkdir与mkdir-asm函数原型 rm menu -rf git clone https://githu ...

  5. linux虚拟网络设备--内核网桥的实现分析(六)

    一.Linux内核网桥的实现分析 Linux 内核分别在2.2 和 2.4内核中实现了网桥.但是2.2 内核和 2.4内核的实现有很大的区别,2.4中的实现几乎是全部重写了所有的实现代码.本文以2.4 ...

  6. linux内核重要结构体,Linux中list_head结构体相关 | 技术部落

    在Linux内核中,提供了一个用来创建双向循环链表的结构 list_head.虽然linux内核是用C语言写的,但是list_head的引入,使得内核数据结构也可以拥有面向对象的特性,通过使用操作li ...

  7. 解析Linux内核源码中数据同步问题丨C++后端开发丨Linux服务器开发丨Linux内核开发丨驱动开发丨嵌入式开发丨内核操作系统

    剖析Linux内核源码数据同步 1.pdflush机制原理 2.超级块同步/inode同步 3.拥塞及强制回写技术 视频讲解如下,点击观看: 解析Linux内核源码中数据同步问题丨C++后端开发丨Li ...

  8. 【Linux 内核 内存管理】物理内存组织结构 ④ ( 内存区域 zone 简介 | zone 结构体源码分析 | zone 结构体源码 )

    文章目录 一.内存区域 zone 简介 二.zone 结构体源码分析 1.watermark 成员 2.lowmem_reserve 成员 3.zone_pgdat 成员 4.pageset 成员 5 ...

  9. 初探内核之《Linux内核设计与实现》笔记上

    内核简介  本篇简单介绍内核相关的基本概念. 主要内容: 单内核和微内核 内核版本号 1. 单内核和微内核   原理 优势 劣势 单内核 整个内核都在一个大内核地址空间上运行. 1. 简单. 2. 高 ...

最新文章

  1. android_通过高级应用程序开发策略在Android中进行用户参与
  2. boost::python::detail::is_string_literal相关的测试程序
  3. Android 编译环境的依赖库安装
  4. @async方法不调用了_在Spring中使用Future对象调用Async方法调用
  5. 左侧固定,右侧自适应的布局方式(新增评论区大佬教的方法)
  6. [Node.js] mySQL数据库 -- NPM包
  7. 8.Azure文件(文件共享)-NAS(中)
  8. 由两个问题引发的对GaussDB(DWS)负载均衡的思考
  9. SFB 项目经验-29-批量-启用用户-启用企业语音-设置分机号(项目中)
  10. Iplimage设置感兴趣区域(ROI)
  11. Verilog练习二【串行加法器】(附公式推导)
  12. android调用系统自带的文件管理器,Android打开系统自带文件管理器,选择指定类型的文件...
  13. mac 截图工具只能截取桌面问题
  14. FPGA实现和ET1100通信verilog源码。 ethercat从站方案
  15. 苏州大学 计算机网络,苏州大学计算机网络与通信期末考试卷-20210517192500.docx-原创力文档...
  16. php 化学泥浆,天水化学泥浆
  17. 用Paddle自动生成二次元人物头像
  18. 全国计算机技术与软件专业技术考试----(高级资格/高级工程师)各资格证详细介绍
  19. 守望账号与服务器失去连接,win10玩守望先锋与服务器一直断开连接的解决方法...
  20. 《数字图像处理》学习笔记(四)--混合空间增强法(待修改)

热门文章

  1. 以后的知识点以PPT的形式展现
  2. jquery给元素添加样式表的方法
  3. POJ 1018 Communication System
  4. 技术方案——可控组播
  5. 关于ASP.NET 中站点地图sitemap 的使用【转xugang】
  6. android某个活动全屏,android – 重新组合活动布局,以便在旋转屏幕时全屏播放视频...
  7. python递归函数是指_python 函数递归作业求解析
  8. python模拟登录qq账号密码_最新的Python模拟登陆QQ脚本,一键批量登录,强行过验证!...
  9. 华硕服务器如何安装系统安装win7系统,华硕电脑怎么重新安装win7系统
  10. mysql的连接leftjion,mysql的表连接(left|right)join