linux mutex

常规的理解,mutex,就是获取锁,没获取到就挂起。其实 read the fxxk source code之后,发现了其实内里还有很多细节,针对一些场景做优化。

总结起来,就是尽量站住cpu,不休眠下去,等别人释放锁的第一时间就接住。

先上数据结构:

struct mutex {
	atomic_long_t		owner;
	raw_spinlock_t		wait_lock;
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
	struct optimistic_spin_queue osq; /* Spinner MCS lock */
#endif
	struct list_head	wait_list;
#ifdef CONFIG_DEBUG_MUTEXES
	void			*magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map	dep_map;
#endif
};

owner 原子结构,用法for() {cmpxchg},看似是一个变量,实则内里乾坤。owner分为两部分,高位部分是持有锁的task_struct指针,由于其字节对齐,低位没用,所以这里被抠出来三个bit。

 * @owner: contains: 'struct task_struct *' to the current lock owner,
 * NULL means not owned. Since task_struct pointers are aligned at
 * at least L1_CACHE_BYTES, we have low bits to store extra state.
 * Bit0 indicates a non-empty waiter list; unlock must issue a wakeup.
 * Bit1 indicates unlock needs to hand the lock to the top-waiter
 * Bit2 indicates handoff has been done and we're waiting for pickup.
 */
#define MUTEX_FLAG_WAITERS	0x01
#define MUTEX_FLAG_HANDOFF	0x02
#define MUTEX_FLAG_PICKUP	0x04

要是光看这个注释,给你看蒙蔽。

bit0 是一个状态位,置位代表现在这个锁有人在等,也就是wait_list上挂着waiter。

bit1 和 bit2 其实是用来握手的通讯位。比如A持有锁,B等着锁,且B是wait_list最top的waiter,则B置位bit1 handoff,然后A释放锁的时候,发现handoff置位,则把owner设置成top waiter,并且把 bit 2置位pickup,bit1清掉。实现一个简单握手。当B检查到pickup位,且owner与自己current相等,则自己获取到锁咯。

这里引出一个逻辑,除了锁没人等,则owner是由持有锁的线程释放锁的时候,将owner设置成top waiter的task_struct, 而不是由申请锁的线程来修改owner。击鼓传花。

然后就是链表wait_list,用来放置waiter,既然是链表,就少不了wait_lock锁来锁住.wait_list上挂的数据结构如下,没啥好说的:

struct mutex_waiter {
	struct list_head	list;
	struct task_struct	*task;
	struct ww_acquire_ctx	*ww_ctx;
#ifdef CONFIG_DEBUG_MUTEXES
	void			*magic;
#endif
};

几个关键函数注解:

static inline struct task_struct *__mutex_trylock_common(struct mutex *lock, bool handoff)
{
    unsigned long owner, curr = (unsigned long)current;

	owner = atomic_long_read(&lock->owner);
	for (;;) { /* must loop, can race against a flag */
		// 从owner变量内 提取那三个bit flag
        unsigned long flags = __owner_flags(owner);
        // 从owner变量内 提取当前持有者的 task_struct
		unsigned long task = owner & ~MUTEX_FLAGS;

		if (task) {// 如果当前有人持有锁
			if (flags & MUTEX_FLAG_PICKUP) {//如果pick_up置位,则持有者释放锁,并交权咯
				if (task != curr)//如果当前 task_struct 与本线程的不相等,则现在这个锁不是给我的
					break;
				flags &= ~MUTEX_FLAG_PICKUP;// 走到这里说明这个锁现在是给我的,把pickup清掉 
			} else if (handoff) { //如果 handoff 传参为1,则置位 handoff 位
                //这里再说一下,只有当前task是wait_list的第一个,handoff才是1
				if (flags & MUTEX_FLAG_HANDOFF)//已经置位了,不用管了
					break;
				flags |= MUTEX_FLAG_HANDOFF;// 没有置位,置位
			} else {
				break;//啥都没有,还是走吧
			}
		} else { //如果当前没人持有锁,就是没锁
            //这里告警,flag有置位,但是还走到这里了,不应该啊,有bug
			MUTEX_WARN_ON(flags & (MUTEX_FLAG_HANDOFF | MUTEX_FLAG_PICKUP));
			task = curr;//直接把 task设置成本大爷的。
		}

        // 原子操作cmpxchg,可能会失败,所以要for(;;)
		if (atomic_long_try_cmpxchg_acquire(&lock->owner, &owner, task | flags)) {
			if (task == curr)//如果 task是自己,则获取成功咯,直接return NULL
				return NULL;
			break;
		}
	}

    //跟我没关系,return 现在的task
	return __owner_task(owner);
}
__mutex_add_waiter(struct mutex *lock, struct mutex_waiter *waiter,
		   struct list_head *list)
{
	hung_task_set_blocker(lock, BLOCKER_TYPE_MUTEX);
	debug_mutex_add_waiter(lock, waiter, current);

    //无脑往队列tail一塞,如果是第一个则,置位waiter flag
	list_add_tail(&waiter->list, list);
	if (__mutex_waiter_is_first(lock, waiter))
		__mutex_set_flag(lock, MUTEX_FLAG_WAITERS);
}
static noinline
bool mutex_spin_on_owner(struct mutex *lock, struct task_struct *owner,
			 struct ww_acquire_ctx *ww_ctx, struct mutex_waiter *waiter)
{
	bool ret = true;

	lockdep_assert_preemption_disabled();

	while (__mutex_owner(lock) == owner) {//保证这个锁控制权一直在这一个家伙手里
		/*
		 * Ensure we emit the owner->on_cpu, dereference _after_
		 * checking lock->owner still matches owner. And we already
		 * disabled preemption which is equal to the RCU read-side
		 * crital section in optimistic spinning code. Thus the
		 * task_strcut structure won't go away during the spinning
		 * period
		 */
		barrier();

		/*
		 * Use vcpu_is_preempted to detect lock holder preemption issue.
		 */
        //这个注释还是老版本的注释,代码已经更新了,差评啊差评
        //这里判断锁的持有者 是否还在cpu上面跑,如果不在cpu上面跑了,
        //也没必要等下去了,应为他也释放不了锁了
        //然后判断本cpu有人没有线程要跑,如果有其他线程想跑,咱先让一让。
		if (!owner_on_cpu(owner) || need_resched()) {
			ret = false;
			break;
		}

		if (ww_ctx && !ww_mutex_spin_on_owner(lock, ww_ctx, waiter)) {
			ret = false;
			break;
		}

		cpu_relax();
	}

	return ret;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值