如有错误请指出,大家一起学习:
1、
访问共享资源的的代码都是临界区
用户态的锁都是睡眠性质的
内核中有些锁是死等,有些锁是睡眠
2、内核抢占
2.6 以后的内核都支持抢占
内核代码可被打断
默认情况下,内核不支持抢占
服务器是不会支持抢占的,自己玩的桌面版的一般支持
make menuconfig
Kernel Features --->
Preemption Model (No Forced Preemption (Server)) --->
( ) No Forced Preemption (Server) //不支持抢占
( ) Voluntary Kernel Preemption (Desktop) //介于上下两者之间
(X) Preemptible Kernel (Low-Latency Desktop) //抢占式内核
原子变量是可被打断的,eg:ldrex、strex 内核中所有的锁都是基于这两条指令的
抢占式内核中才有用的函数,在非抢占式内核中都是空函数:
禁止内核抢占:
preempt_disable()
开启内核抢占:
preempt_enable()
3、原子变量
只有指令不能被打断,原子变量也会被打断,只是会保证即使被打断结果也是正确的,
原子变量的实现必须有硬件支持
ldrex strex slrex
通过 ldrex 命令从内存中把地址放到指定寄存器的,在经过总线的时候会标记某个标志位(硬件支持);别的代码也是可以取这个地址的数据,也会标记这个标志位;当操作完这个数据通过 strex 放回的时候会检测这个标志位(strex r0, r1, [r2], r0 寄存器就是放标志位的),检测通过会将数据放回那个地址并清除标志位;别的程序再往回放的时候检测不到标志位,就会自动返回去,从拿数据开始重新操作
核心头文件 <arch/arm/include/asm/atomic.h>
########################################################################
# 汇编语言:
# Qo:相当于C中的 volatile
# cc:保护 CPSR 寄存器
# smp_mb():封装后的 dmb
# Ir:所代表的变量是立即数
# bne 1b
# b:before;往前面的 1 标号跳,这样一个标号可以被使用两次,bne 1a(a:after)
########################################################################
################################################################
# arm 会根据自己的实现机制,将代码乱序执行
# 为了防止代码被乱序执行,指令之间需要加上屏障,这就需要 DMB、DSB、ISB
# DMB 只对操作内存的指令有影响,DMB 上下的指令还是可以颠倒的
# DSB 必须上面的执行完了,下面的指令才能执行
# ISB 会刷新上下文,只有在异常或异常返回的时候才会刷新上下文
################################################################
相关函数:
atomic_read
atomic_set //获得原子变量?
atomic_add
atomic_add_return
atomic_sub
atomic_sub_return
atomic_cmpxchg(atomic, old, new)
如果要设置的原子变量的值等于 old,才会把 new 设置为新的值;否则原子变量的值不变
atomic_clear_mask(mask, addr)
*addr &= ~mask;就是被 bic,经过一系列操作包装成原子变量
相关宏:
#define atomic_inc(v) atomic_add(1, v) //加1
#define atomic_dec(v) atomic_sub(1, v) //减1
#define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0) //查看 +1后返回值是否为 0
#define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0) //查看 -1后返回值是否为 0
#define atomic_inc_return(v) (atomic_add_return(1, v)) //+1 后的返回值
#define atomic_dec_return(v) (atomic_sub_return(1, v)) //-1 后的返回值
#define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0) //查看 -i后返回值是否为 0
4、原子位操作
p:代表指针
nr:代表第几位,从 0 位开始
可以使用大于 32 位的位操作
相关宏:
set_bit(nr, p) 把 p 指向的数的 nr 位 置1
clear_bit(nr, p) 把 p 指向的数的 nr 位 置0
change_bit(nr, p) 把 p 指向的数的 nr 位 反转
test_set_bit(nr, p) 先返回原来的值,把 p 指向的数的 nr 位 置1
test_clear_bit(nr, p) 先返回原来的值,把 p 指向的数的 nr 位 置0
test_change_bit(nr, p) 先返回原来的值,把 p 指向的数的 nr 位 反转
5、自旋锁
多核CPU使用
在不支持抢占的单核系统中,自旋锁的函数都是空函数;在支持抢占的单核系统中,自旋锁锁上相当于禁止抢占,释放锁相当于可以抢占
死等的锁叫自旋锁,占着 CPU 不放,死等着
比如,cpu0 获得锁后,cpu1的代码也想获得这把锁,cpu1的代码就会占着cpu1不放,死等着,直到cpu0释放锁
特点:死等
死锁:
ABBA死锁:两个锁都需要对方的锁,都等待对方释放
自死锁:自己请求自己的锁,等待自己释放
<-****** 带着锁一定不能去睡眠 ******->
带锁的时候如果睡眠了,其它的程序如果想获得这把锁,就会占着cpu不放,死等着,此时调度器就起不来,
spinlock_t(结构体):自旋锁类型
核心头文件:<include/linux/spinlock.h>
函数:
初始化锁:
获得锁:(一把锁同时只能一个人持有)
spin_lock
试图获得锁,如果获得返回 1,否则 0;不会去死等
spin_trylock
获得锁并禁止中断下半部分:
spin_lock_bh
获得锁并禁止中断:
spin_lock_irq
获得锁并禁止中断且保存中断状态:
spin_lock_irqsave
释放锁:
spin_unlock
释放锁并允许中断下半部分:
spin_unlock_bh
释放锁并允许中断:
spin_unlock_irq
释放锁并允许中断且恢复中断状态
spin_unlock_irqrestore
6、X86下写模块
内核地址:/lib/modules/3.8.0-32-generic/build/
修改Makefile 为:
LINUX_PATH = /lib/modules/`uname -r`/
7、信号量
特点:睡眠等待
计数信号量
二值信号量/互斥信号量
死锁:
获得了自旋锁,又要获得信号量;相当于带着锁睡眠了
在中断上下文中不恩功能用信号量
自死锁
核心头文件: <include/linux/semaphore.h>
struct semaphore {
raw_spinlock_t lock; //这个锁是用于保护链表的,不是说信号量是基于自旋锁的
unsigned int count; //计数信号量,count为几,就几个同时拥有;二值信号量count初始化为1
struct list_head wait_list; //内核用链表维护信号量
};
函数:
初始化一个信号量
sema_init(sem, val) //val就是上面count的值
获得信号量,如果获取不到信号量会睡眠,睡眠不可打断
down()
获得信号量,如果获取不到信号量会睡眠,睡眠可打断;任何信号能唤醒
down_interruptible()
获得信号量,如果获取不到信号量会睡眠,睡眠可打断;如果杀死进程的信号可以唤醒它
down_killable()
试图获得信号量,如果获取返回 0,否则 1
down_trylock()
获得信号量,如果获取不到信号量会睡眠,睡眠不可打断;超时自己醒来,jiffies设定时间
down_timeout(sem, jiffies)
释放信号量
up()
8、加锁也可以被正常的中断打断,因为锁就是一个普通的代码,想不被中断打断,使用禁止中断的锁函数
1、
访问共享资源的的代码都是临界区
用户态的锁都是睡眠性质的
内核中有些锁是死等,有些锁是睡眠
2、内核抢占
2.6 以后的内核都支持抢占
内核代码可被打断
默认情况下,内核不支持抢占
服务器是不会支持抢占的,自己玩的桌面版的一般支持
make menuconfig
Kernel Features --->
Preemption Model (No Forced Preemption (Server)) --->
( ) No Forced Preemption (Server) //不支持抢占
( ) Voluntary Kernel Preemption (Desktop) //介于上下两者之间
(X) Preemptible Kernel (Low-Latency Desktop) //抢占式内核
原子变量是可被打断的,eg:ldrex、strex 内核中所有的锁都是基于这两条指令的
抢占式内核中才有用的函数,在非抢占式内核中都是空函数:
禁止内核抢占:
preempt_disable()
开启内核抢占:
preempt_enable()
3、原子变量
只有指令不能被打断,原子变量也会被打断,只是会保证即使被打断结果也是正确的,
原子变量的实现必须有硬件支持
ldrex strex slrex
通过 ldrex 命令从内存中把地址放到指定寄存器的,在经过总线的时候会标记某个标志位(硬件支持);别的代码也是可以取这个地址的数据,也会标记这个标志位;当操作完这个数据通过 strex 放回的时候会检测这个标志位(strex r0, r1, [r2], r0 寄存器就是放标志位的),检测通过会将数据放回那个地址并清除标志位;别的程序再往回放的时候检测不到标志位,就会自动返回去,从拿数据开始重新操作
核心头文件 <arch/arm/include/asm/atomic.h>
########################################################################
# 汇编语言:
# Qo:相当于C中的 volatile
# cc:保护 CPSR 寄存器
# smp_mb():封装后的 dmb
# Ir:所代表的变量是立即数
# bne 1b
# b:before;往前面的 1 标号跳,这样一个标号可以被使用两次,bne 1a(a:after)
########################################################################
################################################################
# arm 会根据自己的实现机制,将代码乱序执行
# 为了防止代码被乱序执行,指令之间需要加上屏障,这就需要 DMB、DSB、ISB
# DMB 只对操作内存的指令有影响,DMB 上下的指令还是可以颠倒的
# DSB 必须上面的执行完了,下面的指令才能执行
# ISB 会刷新上下文,只有在异常或异常返回的时候才会刷新上下文
################################################################
相关函数:
atomic_read
atomic_set //获得原子变量?
atomic_add
atomic_add_return
atomic_sub
atomic_sub_return
atomic_cmpxchg(atomic, old, new)
如果要设置的原子变量的值等于 old,才会把 new 设置为新的值;否则原子变量的值不变
atomic_clear_mask(mask, addr)
*addr &= ~mask;就是被 bic,经过一系列操作包装成原子变量
相关宏:
#define atomic_inc(v) atomic_add(1, v) //加1
#define atomic_dec(v) atomic_sub(1, v) //减1
#define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0) //查看 +1后返回值是否为 0
#define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0) //查看 -1后返回值是否为 0
#define atomic_inc_return(v) (atomic_add_return(1, v)) //+1 后的返回值
#define atomic_dec_return(v) (atomic_sub_return(1, v)) //-1 后的返回值
#define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0) //查看 -i后返回值是否为 0
4、原子位操作
p:代表指针
nr:代表第几位,从 0 位开始
可以使用大于 32 位的位操作
相关宏:
set_bit(nr, p) 把 p 指向的数的 nr 位 置1
clear_bit(nr, p) 把 p 指向的数的 nr 位 置0
change_bit(nr, p) 把 p 指向的数的 nr 位 反转
test_set_bit(nr, p) 先返回原来的值,把 p 指向的数的 nr 位 置1
test_clear_bit(nr, p) 先返回原来的值,把 p 指向的数的 nr 位 置0
test_change_bit(nr, p) 先返回原来的值,把 p 指向的数的 nr 位 反转
5、自旋锁
多核CPU使用
在不支持抢占的单核系统中,自旋锁的函数都是空函数;在支持抢占的单核系统中,自旋锁锁上相当于禁止抢占,释放锁相当于可以抢占
死等的锁叫自旋锁,占着 CPU 不放,死等着
比如,cpu0 获得锁后,cpu1的代码也想获得这把锁,cpu1的代码就会占着cpu1不放,死等着,直到cpu0释放锁
特点:死等
死锁:
ABBA死锁:两个锁都需要对方的锁,都等待对方释放
自死锁:自己请求自己的锁,等待自己释放
<-****** 带着锁一定不能去睡眠 ******->
带锁的时候如果睡眠了,其它的程序如果想获得这把锁,就会占着cpu不放,死等着,此时调度器就起不来,
spinlock_t(结构体):自旋锁类型
核心头文件:<include/linux/spinlock.h>
函数:
初始化锁:
获得锁:(一把锁同时只能一个人持有)
spin_lock
试图获得锁,如果获得返回 1,否则 0;不会去死等
spin_trylock
获得锁并禁止中断下半部分:
spin_lock_bh
获得锁并禁止中断:
spin_lock_irq
获得锁并禁止中断且保存中断状态:
spin_lock_irqsave
释放锁:
spin_unlock
释放锁并允许中断下半部分:
spin_unlock_bh
释放锁并允许中断:
spin_unlock_irq
释放锁并允许中断且恢复中断状态
spin_unlock_irqrestore
6、X86下写模块
内核地址:/lib/modules/3.8.0-32-generic/build/
修改Makefile 为:
LINUX_PATH = /lib/modules/`uname -r`/
7、信号量
特点:睡眠等待
计数信号量
二值信号量/互斥信号量
死锁:
获得了自旋锁,又要获得信号量;相当于带着锁睡眠了
在中断上下文中不恩功能用信号量
自死锁
核心头文件: <include/linux/semaphore.h>
struct semaphore {
raw_spinlock_t lock; //这个锁是用于保护链表的,不是说信号量是基于自旋锁的
unsigned int count; //计数信号量,count为几,就几个同时拥有;二值信号量count初始化为1
struct list_head wait_list; //内核用链表维护信号量
};
函数:
初始化一个信号量
sema_init(sem, val) //val就是上面count的值
获得信号量,如果获取不到信号量会睡眠,睡眠不可打断
down()
获得信号量,如果获取不到信号量会睡眠,睡眠可打断;任何信号能唤醒
down_interruptible()
获得信号量,如果获取不到信号量会睡眠,睡眠可打断;如果杀死进程的信号可以唤醒它
down_killable()
试图获得信号量,如果获取返回 0,否则 1
down_trylock()
获得信号量,如果获取不到信号量会睡眠,睡眠不可打断;超时自己醒来,jiffies设定时间
down_timeout(sem, jiffies)
释放信号量
up()
8、加锁也可以被正常的中断打断,因为锁就是一个普通的代码,想不被中断打断,使用禁止中断的锁函数