内核同步方法之自旋锁
来源:互联网 发布:数组删除字符串函数 编辑:程序博客网 时间:2024/05/02 06:13
linux内核中最常见的锁是自旋锁(spin lock)。自旋锁最多只能被一个可执行线程持有。如果一个执行线程试图获得一个被争用的自旋锁,那么该线程就会一直进行忙循环等待锁重新可用。要是锁未被争用,请求锁的执行线程便能立刻得到它,继续执行。在任意时间,自旋锁都可以防止多于一个的执行线程同时进入临界区。
一个被正用的自旋锁使得请求它的线程在等待锁重新可用时自旋(特别浪费处理器时间),这种行为是自旋锁的特点。自旋锁不应该被长时间持有。持有自旋锁的时间最好小于完成两次上下文切换的的时间。
自旋锁的实现和体系结构密切相关,代码往往使用汇编实现。
因为自旋锁在同一时刻至多被一个执行线程持有,所以一个时刻只能有一个线程位于临界区内,这就为多处理器提供了防止并发访问所需要的包含机制。在单处理器机器上,编译的时候并不会加入自旋锁。
自旋锁不能递归!
自旋锁可以使用在中断处理程序中(此处不能使用信号量,因为它会导致睡眠)。在中断处理程序中使用自旋锁时,一定要在获取锁之前,首先禁本地中断(在当前处理器上的中断请求),否则,中断处理程序就会打断正持有锁的内核代码,有可能会试图去争用这个已经被持有的自旋锁。注意,需要关闭的只是当前处理器上的中断,因为如果中断发生在不同的处理器上,即使中断处理程序在同一锁上的自旋,也不会妨碍锁的持有者最终释放锁。
下面是相关文件如下:
- /*
- * include/linux/spinlock.h - generic spinlock/rwlock declarations
- *
- * here's the role of the various spinlock/rwlock related include files:
- *
- * on SMP builds:
- *
- * asm/spinlock_types.h: contains the raw_spinlock_t/raw_rwlock_t and the
- * initializers
- *
- * linux/spinlock_types.h:
- * defines the generic type and initializers
- *
- * asm/spinlock.h: contains the __raw_spin_*()/etc. lowlevel
- * implementations, mostly inline assembly code
- *
- * (also included on UP-debug builds:)
- *
- * linux/spinlock_api_smp.h:
- * contains the prototypes for the _spin_*() APIs.
- *
- * linux/spinlock.h: builds the final spin_*() APIs.
- *
- * on UP builds:
- *
- * linux/spinlock_type_up.h:
- * contains the generic, simplified UP spinlock type.
- * (which is an empty structure on non-debug builds)
- *
- * linux/spinlock_types.h:
- * defines the generic type and initializers
- *
- * linux/spinlock_up.h:
- * contains the __raw_spin_*()/etc. version of UP
- * builds. (which are NOPs on non-debug, non-preempt
- * builds)
- *
- * (included on UP-non-debug builds:)
- *
- * linux/spinlock_api_up.h:
- * builds the _spin_*() APIs.
- *
- * linux/spinlock.h: builds the final spin_*() APIs.
- */
内核提供的禁止中断同时请求锁的接口:
- 在<Spinlock.h(include/linux)>中
- #if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)
- #define spin_lock_irqsave(lock, flags) flags = _spin_lock_irqsave(lock)
- #define read_lock_irqsave(lock, flags) flags = _read_lock_irqsave(lock)
- #define write_lock_irqsave(lock, flags) flags = _write_lock_irqsave(lock)
- #ifdef CONFIG_DEBUG_LOCK_ALLOC
- #define spin_lock_irqsave_nested(lock, flags, subclass) /
- flags = _spin_lock_irqsave_nested(lock, subclass)
- #else
- #define spin_lock_irqsave_nested(lock, flags, subclass) /
- flags = _spin_lock_irqsave(lock)
- #endif
- #else
- #define spin_lock_irqsave(lock, flags) _spin_lock_irqsave(spinlock_t * lock)(lock, flags)
- #define read_lock_irqsave(lock, flags) _read_lock_irqsave(lock, flags)
- #define write_lock_irqsave(lock, flags) _write_lock_irqsave(lock, flags)
- #define spin_lock_irqsave_nested(lock, flags, subclass) /
- spin_lock_irqsave(lock, flags)
- #endif
- #define spin_unlock_irqrestore(lock, flags) /
- _spin_unlock_irqrestore(lock, flags)
函数spin_lock_irqsave()保存中断的当然状态,并禁止本地中断,然后再去获取指定的锁。spin_lock_irqsave()对指定的锁解锁,然后让中断恢复到加锁前的状态。所以即使中断最初是被禁止的,你的代码也不会错误地激活它们,相反,会继续让它们禁止。flag变量应该由数值传递,因为锁函数有些是通过宏的方式实现的。
加锁和解锁分别可以禁止和允许内核抢占。
如果能确定中断在加锁前是激活的,就不需要在解锁后恢复中断之前的状态了。这时可以使用spin_lock_irq()和spin_unlock_irq()。
- #define spin_lock_irq(lock) _spin_lock_irq(lock)
- #if defined(CONFIG_DEBUG_SPINLOCK) || defined(CONFIG_PREEMPT) || /
- !defined(CONFIG_SMP)
- # define read_unlock_irq(lock) _read_unlock_irq(lock)
- #else
- # define spin_unlock_irq(lock) /
- do { /
- __raw_spin_unlock(&(lock)->raw_lock); /
- __release(lock); /
- local_irq_enable(); /
- } while (0)
由于内核庞大而复杂,所以在内核的执行路线上,很难搞清楚中断在当前调用点上是否处于激活状态,故一般不提倡使用spin_lock_irq()方法。
针对自旋锁的操作:
spin_lock_init()用来初始化动态创建的自旋锁。
- #ifdef CONFIG_DEBUG_SPINLOCK
- extern void __spin_lock_init(spinlock_t *lock, const char *name,
- struct lock_class_key *key);
- # define spin_lock_init(lock) /
- do { /
- static struct lock_class_key __key; /
- /
- __spin_lock_init((lock), #lock, &__key); /
- } while (0)
- #else
- # define spin_lock_init(lock) /
- do { *(lock) = SPIN_LOCK_UNLOCKED; } while (0)
- #endif
- #ifdef CONFIG_DEBUG_SPINLOCK
- extern void __rwlock_init(rwlock_t *lock, const char *name,
- struct lock_class_key *key);
- # define rwlock_init(lock) /
- do { /
- static struct lock_class_key __key; /
- /
- __rwlock_init((lock), #lock, &__key); /
- } while (0)
- #else
- # define rwlock_init(lock) /
- do { *(lock) = RW_LOCK_UNLOCKED; } while (0)
- #endif
- #define spin_is_locked(lock) __raw_spin_is_locked(&(lock)->raw_lock)
- #define spin_trylock(lock) __cond_lock(lock, _spin_trylock(lock))
spin_trylock()试图获得某个特定的自旋锁,如果该锁已经被争用,那么立刻返回非0值,而不会自旋等待锁被释放;如果获得这个自旋锁,返回0。
spin_is_locked()用于检查特定的锁当前是否已被占用,如被占用返回非0,否则返回0。
- #define spin_lock(lock) _spin_lock(lock)
- /*
- * We inline the unlock functions in the nondebug case:
- */
- #if defined(CONFIG_DEBUG_SPINLOCK) || defined(CONFIG_PREEMPT) || /
- !defined(CONFIG_SMP)
- # define spin_unlock(lock) _spin_unlock(lock)
- #else
- # define spin_unlock(lock) /
- do {__raw_spin_unlock(&(lock)->raw_lock); __release(lock); } while (0)
自旋锁和下半部
函数spin_lock_bh()用于获取指定锁,同时它会禁止所有下半部的执行。spin_unlock_bh()函数执行相反的操作。
- #define spin_lock_bh(lock) _spin_lock_bh(lock)
- #define spin_unlock_bh(lock) _spin_unlock_bh(lock)
- void __lockfunc _spin_lock_bh(spinlock_t *lock)
- {
- local_bh_disable();
- preempt_disable();
- spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
- _raw_spin_lock(lock);
- }
- void __lockfunc _spin_unlock_bh(spinlock_t *lock)
- {
- spin_release(&lock->dep_map, 1, _RET_IP_);
- _raw_spin_unlock(lock);
- preempt_enable_no_resched();
- local_bh_enable_ip((unsigned long)__builtin_return_address(0));
- }
- 内核同步方法之自旋锁
- 内核同步方法之读写自旋锁
- 内核同步方法-自旋锁
- 9.2 内核同步方法_自旋锁
- 内核同步之自旋锁和信号量
- 内核同步之自旋锁和信号量
- 内核同步之自旋锁和信号量
- 内核部件之同步机制之自旋锁
- Linux 同步方法剖析--内核原子,自旋锁和互斥锁
- 9.3 内核同步方法_读-写自旋锁
- 9.6 内核同步方法_自旋锁和信号量
- Linux 同步方法剖析--内核原子,自旋锁和互斥锁
- 内核同步机制之自旋锁&读/写锁
- Linux 内核同步之自旋锁与信号量的异同
- Linux 同步方法剖析-Linux 同步方法剖析内核原子,自旋锁和互斥锁
- linux内核学习(15)内核编程基本功之内核同步与自旋锁spinlock_t
- linux内核学习(15)内核编程基本功之内核同步与自旋锁spinlock_t
- linux内核学习(15)内核编程基本功之内核同步与自旋锁spinlock_t
- Windows SDK笔记
- JS复制文件
- CVS Branch合并到Head的操作
- 【转帖】一个成功软件测试项目的经验
- 【转帖】Oracle与SQLServer选型时不得不说的三个小差异
- 内核同步方法之自旋锁
- 图:乐清市首届民工艺术节闭幕式暨才艺表演决赛圆满结束_渤锐软件提供了【评委打分公开展示】技术支持
- 新开Blog,呵呵
- 数据库连接池技术浅析
- 如何写出漂亮的代码
- 寓言[转]
- Windows调试工具入门1—Windows调试工具介绍
- Linux 设备文件
- bs与cs