自旋锁,旋啊旋
曾经的某一天,接触了“自旋锁”,听到这个名字,脑子里出现的是“中子星”——都是在旋啊旋。
脉冲星(Pulsar),又称波霎,是中子星的一种,为会周期性发射脉冲信号的星体。
人们最早认为恒星是永远不变的。而大多数恒星的变化过程是如此的漫长,人们也根本觉察不到。然而,并不是所有的恒星都那么平静。后来人们发现,有些恒星也很“调皮”,变化多端。于是,就给那些喜欢变化的恒星起了个专门的名字,叫“变星”。 脉冲星发射的射电脉冲的周期性非常有规律。一开始,人们对此很困惑,甚至曾想到这可能是外星人在向我们发电报联系。据说,第一颗脉冲星就曾被叫做“小绿人一号”。经过几位天文学家一年的努力,终于证实,脉冲星就是正在快速自转的中子星。而且,正是由于它的快速自转而发出射电脉冲。
瞎旋个啥,咋不去休眠、挂起呢?因为一些代码是大忙人,闲不得,更是停不得,就在门口自己玩死循环,急切的等待屋子里的人出来把锁给自己。
关于锁,最常使用的便是:自旋锁与信号量。先贴些实例,来点感性的认识。
-- include/linux/spinlock_types.h -- typedef struct { raw_spinlock_t raw_lock; #ifdef CONFIG_GENERIC_LOCKBREAK unsigned int break_lock; #endif #ifdef CONFIG_DEBUG_SPINLOCK unsigned int magic, owner_cpu; void *owner; #endif #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif } spinlock_t; typedef struct { volatile unsigned int lock; } raw_spinlock_t;
以上是2.6.32中的定义。raw_spinlock_t在2.6.39中的定义:
typedef struct spinlock { union { struct raw_spinlock rlock; #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_GENERIC_LOCKBREAK unsigned int break_lock; #endif #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; typedef struct { volatile unsigned int lock; } arch_spinlock_t;
大体上就是将struct raw_spinlock变肥了而已,对于我们而言,该吃吃,该睡睡,平时如何写代码,现在依旧如何写。只要volatile unsigned int lock在就好。
关于lock变量,涉及到一个“排队自旋锁“的问题,将lock分为三部分:高16位(一般未用),第16位再一分为二(next域,owner域)。
简单的说,申请自旋锁,next++;释放自旋锁,owner++;
if (next<未增值前> == owner)
能申请自旋锁
else
不能申请自旋锁
spinlock_t lock = SPIN_LOCK_UNLOCKED; int __init my_init(void) { /*输出宏SPIN_LOCK_UNLOCKED的相关信息*/ printk("<0>SPIN_LOCK_UNLOCKED: %d\n",SPIN_LOCK_UNLOCKED.raw_lock.rlock); spin_lock_init( &lock ); //初始化自旋锁 printk("<0>after init, lock: %d\n",lock.raw_lock.rlock); printk("<0>\n"); spin_lock( &lock ); //第一次获取自旋锁 printk("<0>first spin_lock, lock: %d\n",lock.raw_lock.rlock); spin_unlock( &lock ); //第一次释放自旋锁 printk("<0>first spin_unlock, lock: %d\n",lock.raw_lock.rlock); printk("<0>\n"); spin_lock( &lock ); //第二次获取自旋锁 printk("<0>second spin_lock, lock: %d\n",lock.raw_lock.rlock); spin_unlock( &lock ); //第二次释放自旋锁 printk("<0>second spin_unlock, lock: %d\n",lock.raw_lock.rlock); return 0; }
加载结果:
[ 4123.219762] after init, lock: 0
[ 4123.219764]
[ 4123.219765] first spin_lock, lock: 256
[ 4123.219768] first spin_unlock, lock: 257
[ 4123.219770]
[ 4123.219771] second spin_lock, lock: 513
[ 4123.219774] second spin_unlock, lock: 514
加锁是主菜,当然还会有一些附属功能(irq)一并执行,比如下面的三个实例:
(1)
int __init spin_lock_bh_init(void) { spinlock_t lock = SPIN_LOCK_UNLOCKED; spin_lock_init( &lock ); //初始化自旋锁 printk("<0>in_softirq():%ld\n", in_softirq()); //输出软中断计数 printk("<0>lock........\n"); spin_lock_bh( &lock); //获取自旋锁同时禁止软中断 printk("<0>in_softirq():%ld\n", in_softirq()); printk("<0>unlock........\n"); spin_unlock_bh( &lock); //释放自旋锁同时使能软中断 printk("<0>in_softirq():%ld\n", in_softirq()); return 0; } -------------------------------------- [ 5065.951735] in_softirq():0 [ 5065.951739] lock........ [ 5065.951741] in_softirq():256 [ 5065.951743] unlock........ [ 5065.951745] in_softirq():0
(2)
int __init spin_lock_irq_init(void) { spinlock_t lock = SPIN_LOCK_UNLOCKED; spin_lock_init( &lock ); //初始化自旋锁 printk("<0>lock........\n"); spin_lock_irq( &lock); //获取自旋锁同时禁止本地中断 printk("<0>irqs_disabled():%d\n",irqs_disabled()); //查看中断是否被禁止 printk("<0>unlock........\n"); spin_unlock_irq( &lock); //释放自旋锁同时使能本地中断 printk("<0>irqs_disabled():%d\n",irqs_disabled()); return 0; } --------------------------------------- [ 5747.407543] lock........ [ 5747.407548] irqs_disabled():1 [ 5747.407550] unlock........ [ 5747.407552] irqs_disabled():0
(3)
int __init spin_lock_irqsave_init(void) { unsigned long flags = 0; spinlock_t lock = SPIN_LOCK_UNLOCKED; spin_lock_init( &lock ); //初始化自旋锁 printk("<0>lock........\n"); spin_lock_irqsave( &lock, flags ); //先禁止中断,后加锁,将加锁前的中断状态保存在flag printk("<0>irqs_disabled():%d\n",irqs_disabled()); //查看中断是否被禁止 printk("<0>flags = 0x%lx\n",flags); //输出标志寄存器的值 printk("<0>unlock........\n"); spin_unlock_irqrestore( &lock, flags ); printk("<0>irqs_disabled():%d\n",irqs_disabled()); return 0; } -------------------------------------- [ 6197.981793] lock........ [ 6197.981798] irqs_disabled():1 [ 6197.981800] flags = 0x200296 [ 6197.981802] unlock........ [ 6197.981804] irqs_disabled():0
再介绍一个try_lock:
-------trylock的特点在于会有返回值。
spinlock_t lock = SPIN_LOCK_UNLOCKED; int ret; int my_function(void * argc) { printk("<0>\nin child, the current pid is:%d\n",current->pid); //显示子进程PID ret = spin_trylock( &lock ); if( ret == 1 ) { spin_unlock( &lock ); } else { printk("<0>spin_trylock could't get the lock!\n"); printk("<0>need the parent to unlock.\n\n"); } return 0; } int __init spin_trylock_init(void) { int ret0; printk("<0>in parent, the current pid is:%d\n",current->pid); //显示父进程PID spin_lock_init( &lock ); spin_lock( &lock ); //获取自旋锁 ret0 = kernel_thread(my_function,NULL,CLONE_KERNEL); spin_unlock( &lock ); //释放自旋锁 printk("<0>parent unlock!\n"); return 0; }
-----------
读写自旋锁
-----------
raw_rwlock_t raw_lock;
#ifdef CONFIG_GENERIC_LOCKBREAK
unsigned int break_lock;
#endif
#ifdef CONFIG_DEBUG_SPINLOCK
unsigned int magic, owner_cpu;
void*owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
} rwlock_t;
typedef struct {
volatile unsigned intlock;
} raw_rwlock_t;
读写锁当然也有个lock,低24位为读者计数器(0~23)。
24位为“未锁“标志字段
其他未用。
未锁置1,表示此时锁没人拿。
未锁置0,其他也为0,表示写者掌控锁。
未锁置0,低24位有值,表示读者掌控锁,读者的个数表示有点特别,就是:
一个读者,则:0x00ffffff
两个读者,则:0x00fffffe
以此列推,大伙儿都看得出来。
最后来个实例,帮助理解:
rwlock_t rwlock = RW_LOCK_UNLOCKED; int __init write_trylock_init(void) { int ret; rwlock_init( &rwlock ); //读写自旋锁初始化 read_lock( &rwlock ); //读者申请得到读写锁rwlock /* 输出读写自旋锁lock字段信息*/ printk("<0>after read_lock,lock: 0x%x\n",rwlock.raw_lock.lock); printk("<0>\n"); ret = write_trylock( &rwlock ); //写者试图获得自旋锁 if( ret == 1 ) { printk("<0>after write_trylock, lock: 0x%x\n",rwlock.raw_lock.lock); write_unlock( &rwlock ); printk("<0>after write_unlock, lock: 0x%x\n",rwlock.raw_lock.lock); } else { printk("<0>write_trylock could't get the lock!\n"); } printk("<0>\n"); read_unlock( &rwlock ); //读者释放读写锁rwlock printk("<0>after read_unlock,lock: 0x%x\n",rwlock.raw_lock.lock); return 0; }
加载结果:
[ 9106.498749] after read_lock,lock: 0xffffff [ 9106.498753] [ 9106.498755] write_trylock could't get the lock! [ 9106.498757] [ 9106.498759] after read_unlock,lock: 0x1000000
当然,kernel里的锁还有许多,顺序锁啊,信号量啊什么。但基本都是那个样子。再说一个completioin,这个东西初次看到有点唬人,先来个实例:
struct completion { unsigned int done; wait_queue_head_t wait; }; -------------------------------- static struct completion comple; int my_function(void * argc) { wait_queue_head_t head; wait_queue_t data; printk("<0>in the kernel thread function!\n"); init_waitqueue_head(&head); init_waitqueue_entry(&data,current); add_wait_queue(&head,&data); sleep_on_timeout(&head,10); printk("<0>the current pid is:%d\n",current->pid); printk("<0>the state of the parent is:%ld\n",current->real_parent->state); complete(&comple); //这里若不执行此函数,之前的wait_for_completioin便会一直堵死在那里 printk("<0>out the kernel thread function\n"); return 0; } static int __init wait_for_completion_init(void) { int result; wait_queue_t data; printk("<0>into wait_for_completion_init.\n"); result=kernel_thread(my_function, NULL, CLONE_KERNEL); struct pid * kpid=find_get_pid(result); struct task_struct * task=pid_task(kpid,PIDTYPE_PID); init_completion(&comple); //初始化好变量,记得变量里还包含个:wait_queue_head_t init_waitqueue_entry(&data, task); __add_wait_queue_tail(&(comple.wait), &data); //加入这个队列头 wait_for_completion(&comple); //等待,起到同步作用 printk("<0>the result of the kernel_thread is :%d\n",result); printk("<0>the current pid is:%d\n",current->pid); printk("<0>out wait_for_completion_init.\n"); return 0; }
completion是同步用的,和等待队列放在一起,自然就露出了她的本来面目~
好了,就先说这么些。。。