标签:
周日傍晚,我去家附近的超市(...)买苏打水,准备自制青柠苏打,我感觉我做的比买的那个巴黎水要更爽口。由于天气太热,很多人都去超市避暑去了,超市也不撵人,这仿佛是他们的策略,人过来避暑了,走的时候难免要买些东西的,就跟很多美女在公交地铁上看淘宝消磨时光,然后就下单了...这是多么容易一件事,反之开车的美女网购就少很多。对于超市的避暑者,要比公交车上下单更麻烦些,因为有一个成本问题,这就是排队成本。/* * 为了突出重点问题,不至于迷失在代码细节.我做了以下的假设: * 1.我省去了操作信号量本身的自旋锁,我假设P/V操作过程的任意序列都是原子的. * 2.我取消了超时参数以及state,我假设除非得到信号量,否则一定等下去,我还假设睡眠不会被打断,除非有人唤醒. * 3.我取消了inline,因为我想突出围绕本地栈变量本地自旋,这样不会cache pingpong. */ struct semaphore { raw_spinlock_t lock; unsigned int count; struct list_head wait_list; }; struct semaphore_waiter { struct list_head list; struct task_struct *task; // 本地局部检测变量 bool up; }; static int down(struct semaphore *sem) { if (likely(sem->count > 0)) { sem->count--; } else { struct task_struct *task = current; struct semaphore_waiter waiter; // 栈上的排队体,相当于ticket,获得信号量(函数返回)后就没有用了 list_add_tail(&waiter.list, &sem->wait_list); waiter.task = task; waiter.up = false; for (;;) { __set_task_state(task, TASK_UNINTERRUPTIBLE); schedule(); // 本地栈变量的检测,减少了多处理器之间的cache同步,不会cache乒乓 // ******************************************************************** // 但是要想到一种情况,如果多个进程试图写这个变量,还是要有锁操作的。 // 虽然我的假设是所有操作以及操作序列都是原子的,但是在up操作中,持有信 // 号量的进程只是简单的wake up了队列,而这并不能确保被唤醒的task就一定可 // 以得到执行,中间还有一个schedule层呢。鉴于这种复杂的局面,我想到了不 // sleep,而是本地自旋版本的信号量,不管怎样,它确实解决了我的问题。 // [事实上,由于sem本身拥有一把自旋锁,这就禁止了多个“服务台”同时召唤 // 同一个等待者的局面,而我在我的描述中,忽略了这把自旋锁,这是为什么呢? // 因为,我想为我的自旋信号量版本贴金,不然人家都把问题解决了,我还扯啥 // 玩意儿啊!] // ******************************************************************** // 这种情况在spin lock下不会存在,因为同时只有一个进程会持有lock, // 不可能多个进程同时操作。 if (waiter.up) { return 0; } } } } void up(struct semaphore *sem) { unsigned long flags; if (likely(list_empty(&sem->wait_list))) { sem->count++; } else { struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list, struct semaphore_waiter, list); // 标准的Linux kernel中,该操作被spin lock保护,这意味着不可能多个服务台同时将 // 服务给与同一个等待者。 list_del(&waiter->list); waiter->up = true; // 简单wake up进程,它何时投入运行,看调度器何时调度它了。 wake_up_process(waiter->task); } }
?/* * 我引入了BEGIN_ATOMIC和END_ATOMIC两个宏,因为我不想贴汇编码,所以这两个宏的意思就是它们之间的代码都是由 * lock前缀修饰的,锁总线。 * 此外,什么事情都没有做,只是改了名称。如果想初始化一个标准的排队自旋锁,将初始化宏的val设置成1即可。 */ struct spin_semaphore { unsigned int count; struct list_head wait_list; }; struct spin_semaphore_waiter { struct list_head list; struct task_struct *task; // 本地局部检测变量 bool up; }; static int spin_down(struct spin_semaphore *sem) { if (likely(sem->count > 0)) { sem->count--; } else { struct task_struct *task = current; struct spin_semaphore_waiter waiter; BEGIN_ATOMIC list_add_tail(&waiter.list, &sem->wait_list); waiter.task = task; waiter.up = false; END_ATOMIC for (;;) { cpu_relax(); // PAUSE if (waiter.up) { return 0; } } } } void up(struct spin_semaphore *sem) { unsigned long flags; BEGIN_ATOMIC if (likely(list_empty(&sem->wait_list))) { sem->count++; END_ATOMIC } else { struct spin_semaphore_waiter *waiter = list_first_entry(&sem->wait_list, struct spin_semaphore_waiter, list); list_del(&waiter->list); waiter->up = true; END_ATOMIC } }
版权声明:本文为博主原创文章,未经博主允许不得转载。
本地自旋锁与信号量/多服务台自旋队列-spin wait风格的信号量
标签:
原文地址:http://blog.csdn.net/dog250/article/details/47098055