linux源码中spin_lock函数分析

spin_lock

代码位置:/kernel/include/linux/spinlock.h

static __always_inline void spin_lock(spinlock_t *lock)
{
        raw_spin_lock(&lock->rlock);
}

可见,spin_lock的实现实际上是通过raw_spin_lock实现的。
看一下spinlock和raw_spinlock的结构体定义:/kernel/include/linux/spinlock_types.h

typedef struct spinlock {
        union {
                struct raw_spinlock rlock;//raw_spinlock是成员

#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
                struct {
                        u8 __padding[LOCK_PADSIZE];
                        struct lockdep_map dep_map;
                };
#endif
        };
} spinlock_t;
......
typedef struct raw_spinlock {
        arch_spinlock_t raw_lock;
#ifdef CONFIG_DEBUG_SPINLOCK
        unsigned int magic, owner_cpu;
        void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
        struct lockdep_map dep_map;
#endif
} raw_spinlock_t;

可见,spinlock里包含raw_spinlock的成员,这才使spinlock调用raw_spinlock的函数变得可实现起来。

raw_spin_lock系列函数

/kernel/include/linux/spinlock.h中,

#define raw_spin_lock(lock)     _raw_spin_lock(lock)

/kernel/include/linux/spinlock_api_smp.h中,

#define _raw_spin_lock(lock) __raw_spin_lock(lock)

static inline void __raw_spin_lock(raw_spinlock_t *lock)
{
        preempt_disable();
        spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
        LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}

其中,preempt_disable是用于关闭抢占的。(与之配套的函数是preempt_enable)

之所以要在真正获得锁之前关闭抢占,是为了防止系统进入死锁。
例如有线程A经过系统调用进入内核态,且需要访问临界区,这时它会获得互斥锁,随后进入临界区里。

若在A尚未离开临界区的过程中,又发生了一个中断,而此时内核是可抢占的,这说明此时需要按调度算法来判断线程的优先级决定谁拥有访问权。若恰巧,队列中有一个线程B的优先级在这一时刻高于进程A,则B将拥有处理器,A将被换出。若B也需要进入内核态来访问同一块临界区,则此时出现死锁。因A已被换出,故而无法释放锁,这导致B永远无法获得锁。

上述情况在单CPU和多CPU场景下均有可能出现。有可能是单cpu的多个线程同时访问同一个资源,也可能是来自多个cpu的多个线程同时访问同一个资源。

故而为了避免此种现象,需要在真正获得锁之前关闭掉系统抢占。在真正释放锁之后再打开系统抢占。

接着分析代码。在/kernel/include/linux/lockdep.h里可以看到spin_acquire和LOCK_CONTENDED的定义。

#define spin_acquire(l, s, t, i)  lock_acquire_exclusive(l, s, t, NULL, i)//由函数名称很清楚的看出来这是用于占有排他性锁的函数。
...... ......
#ifdef CONFIG_LOCK_STAT

extern void lock_contended(struct lockdep_map *lock, unsigned long ip);
extern void lock_acquired(struct lockdep_map *lock, unsigned long ip);
//这种情况下,对应__raw_spin_lock传进来的参数,其实是调用了do_raw_spin_try_lock(lock)
#define LOCK_CONTENDED(_lock, try, lock)                        \
do {                                                            \
        if (!try(_lock)) {                                      \
                lock_contended(&(_lock)->dep_map, _RET_IP_);    \
                lock(_lock);                                    \
        }                                                       \
        lock_acquired(&(_lock)->dep_map, _RET_IP_);                     \
} while (0)
...... ......

#else /* CONFIG_LOCK_STAT */

#define lock_contended(lockdep_map, ip) do {} while (0)
#define lock_acquired(lockdep_map, ip) do {} while (0)
//这种情况下,对应__raw_spin_lock传进来的参数,其实是调用了do_raw_spin_lock(lock)
#define LOCK_CONTENDED(_lock, try, lock) \
        lock(_lock)
...... ......
#endif /* CONFIG_LOCK_STAT */

上述一个新属性是CONFIG_LOCK_STAT,关于它的详细介绍可以在/kernel/Documentation/locking/lockstat.txt中找到。以下只贴一部分,有兴趣自行查看:

LOCK STATISTICS

- WHAT

As the name suggests, it provides statistics on locks.

- WHY

Because things like lock contention can severely impact performance.

- HOW

Lockdep already has hooks in the lock functions and maps lock instances to
lock classes. We build on that (see Documentation/locking/lockdep-design.txt).
The graph below shows the relation between the lock functions and the various
hooks therein.
//这是lock的主要功能流程图
        __acquire
            |
           lock _____
            |        \
            |    __contended//锁占有权的竞争阶段
            |         |
            |       <wait>//等待
            | _______/
            |/
            |
       __acquired//竞争成功,获得锁
            |
            .
          <hold>//保持
            .
            |
       __release//释放锁
            |
         unlock

lock, unlock    - the regular lock functions
__*             - the hooks
<>              - states

...... ......
 - CONFIGURATION

Lock statistics are enabled via CONFIG_LOCK_STAT.

总结翻译过来就是,如果CONFIG_LOCK_STAT置1,则会开启记录locks详细属性的功能。设计目的和使用方法上面文档都写清楚了。

由LOCK_CONTENDED的定义,知道根据CONFIG的值,有两条调用路径。

do_raw_spin_try_lock

位置在/kernel/include/linux/spinlock.h

static inline int do_raw_spin_trylock(raw_spinlock_t *lock)
{
        return arch_spin_trylock(&(lock)->raw_lock);//调用了arch的函数
}

arch位置:/kernel/arch/arm/include/asm/spinlock.h

static inline int arch_spin_trylock(arch_spinlock_t *lock)
{
        unsigned long contended, res;
        u32 slock;

        prefetchw(&lock->slock);
        do {
                __asm__ __volatile__(
                "       ldrex   %0, [%3]\n"
                "       mov     %2, #0\n"
                "       subs    %1, %0, %0, ror #16\n"
                "       addeq   %0, %0, %4\n"
                "       strexeq %2, %0, [%3]"
                : "=&r" (slock), "=&r" (contended), "=&r" (res)
                : "r" (&lock->slock), "I" (1 << TICKET_SHIFT)
                : "cc");
        } while (res);

        if (!contended) {
                smp_mb();
                return 1;
        } else {
                return 0;
        }
}

do_raw_spin_lock

位置在/kernel/include/linux/spinlock.h

#ifdef CONFIG_DEBUG_SPINLOCK	//如果定义了该属性,则do_raw_spin_lock的定义在别的文件里,此处不贴代码了
 extern void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock);
......
 extern int do_raw_spin_trylock(raw_spinlock_t *lock);
......
#else
static inline void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock)
{
        __acquire(lock);
        arch_spin_lock(&lock->raw_lock);
}
......
#endif

arch位置:/kernel/arch/arm/include/asm/spinlock.h

/*
 * ARMv6 ticket-based spin-locking.
 *
 * A memory barrier is required after we get a lock, and before we
 * release it, because V6 CPUs are assumed to have weakly ordered
 * memory.
 */

static inline void arch_spin_lock(arch_spinlock_t *lock)
{
        unsigned long tmp;
        u32 newval;
        arch_spinlock_t lockval;

        prefetchw(&lock->slock);
        __asm__ __volatile__(
"1:     ldrex   %0, [%3]\n"
"       add     %1, %0, %4\n"
"       strex   %2, %1, [%3]\n"
"       teq     %2, #0\n"
"       bne     1b"
        : "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
        : "r" (&lock->slock), "I" (1 << TICKET_SHIFT)
        : "cc");
        
        while (lockval.tickets.next != lockval.tickets.owner) {
                wfe();
                lockval.tickets.owner = READ_ONCE(lock->tickets.owner);
        }

        smp_mb();
}

总结:
由上述源码,可知其实最终一步调用的函数是当前系统架构里的处理函数。

PS:本部分略微高深,本人菜鸡,若有哪里写错了跪求大佬指导

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值