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