标签:
周日傍晚,我去家附近的超市(...)买苏打水,准备自制青柠苏打,我感觉我做的比买的那个巴黎水要更爽口。由于天气太热,很多人都去超市避暑去了,超市也不撵人,这仿佛是他们的策略,人过来避暑了,走的时候难免要买些东西的,就跟很多美女在公交地铁上看淘宝消磨时光,然后就下单了...这是多么容易一件事,反之开车的美女网购就少很多。对于超市的避暑者,要比公交车上下单更麻烦些,因为有一个成本问题,这就是排队成本。/*
* 为了突出重点问题,不至于迷失在代码细节.我做了以下的假设:
* 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