内核中读写信号量(rwsem)实现的关键注释

Posted 程序猿Ricky的日常干货

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内核中读写信号量(rwsem)实现的关键注释相关的知识,希望对你有一定的参考价值。

本文代码摘自于linux-3.10.0-327.el7版本内核:

读锁实现注释:
读锁在获取时主入口时down_read,在这个函数中会先尝试__down_read_trylock,如果__down_read_trylock失败,就会执行__down_read,在这个函数中实际运行的是慢速路径,它的逻辑实现在如下:

struct rw_semaphore __sched *rwsem_down_read_failed(struct rw_semaphore *sem)

    //1.默认是减去这个值,在运行到这里之前,会先try,在try时增加了read标记,但是获取锁失败了,所以要undo这个操作
    long count, adjustment = -RWSEM_ACTIVE_READ_BIAS;
    struct rwsem_waiter waiter;
    struct task_struct *tsk = current;

    /* set up my own style of waitqueue */
    waiter.task = tsk;
    waiter.type = RWSEM_WAITING_FOR_READ;
    get_task_struct(tsk);

    raw_spin_lock_irq(&sem->wait_lock);
    //2.如果list是空的,那么我们将要添加一个waiter,因此需要添加wait flag到count中.
    if (list_empty(&sem->wait_list))
        adjustment += RWSEM_WAITING_BIAS;
    list_add_tail(&waiter.list, &sem->wait_list);

    /* we're now waiting on the lock, but no longer actively locking */

    //3.此时我们已经添加到了wait_list,因此需要把前面获取到的adjustment更新到count标记
    count = rwsem_atomic_update(adjustment, sem);

    /* If there are no active locks, wake the front queued process(es).
     *
     * If there are no writers and we are first in the queue,
     * wake our own waiter to join the existing active readers !
     */
    //4.唤醒wait_list中前面的进程,可能为一个写者,或者多个读者(可能包含自己)。
    //如果唤醒本进程,会把对应的读waiter中的task设置为NULL
    if (count == RWSEM_WAITING_BIAS ||
        (count > RWSEM_WAITING_BIAS &&
         adjustment != -RWSEM_ACTIVE_READ_BIAS))
        sem = __rwsem_do_wake(sem, RWSEM_WAKE_ANY);

    raw_spin_unlock_irq(&sem->wait_lock);

    /* wait to be given the lock */
    while (true) 
        set_task_state(tsk, TASK_UNINTERRUPTIBLE);
        //判断task为NULL,说明不用继续等待了,可以持锁了.
        if (!waiter.task)
            break;
        schedule();
       

    tsk->state = TASK_RUNNING;

    return sem;

写锁实现注释:
写锁在获取时主入口时down_write,在这个函数中会先尝试__down_write_trylock,如果__down_write_trylock失败,就会执行__down_write,在这个函数中实际运行的是慢速路径,它的逻辑实现在如下:

struct rw_semaphore __sched *rwsem_down_write_failed(struct rw_semaphore *sem)

    //1.默认是减去这个值,在try时增加了write标记,但是获取锁失败了,所以要undo这个操作
    long count, adjustment = -RWSEM_ACTIVE_WRITE_BIAS;
    struct rwsem_waiter waiter;
    struct task_struct *tsk = current;

    /* set up my own style of waitqueue */
    waiter.task = tsk;
    waiter.type = RWSEM_WAITING_FOR_WRITE;

    raw_spin_lock_irq(&sem->wait_lock);
    //2.如果list是空的,那么我们将要添加一个waiter,因此需要添加wait flag到count中.
    if (list_empty(&sem->wait_list))
        adjustment += RWSEM_WAITING_BIAS;
    list_add_tail(&waiter.list, &sem->wait_list);

    /* we're now waiting on the lock, but no longer actively locking */

    //3.此时我们已经添加到了wait_list,因此需要把前面获取到的adjustment更新到count标记
    count = rwsem_atomic_update(adjustment, sem);

    /* If there were already threads queued before us and there are no
     * active writers, the lock must be read owned; so we try to wake
     * any read locks that were queued ahead of us. */
    //判断如果wait_list中已经存在等待的读者时,
    //为了加快读者执行完成临界区执行,需要先把他们唤醒
    //这样当前写者才能更快的获取到写锁
    if (count > RWSEM_WAITING_BIAS &&
        adjustment == -RWSEM_ACTIVE_WRITE_BIAS)
        sem = __rwsem_do_wake(sem, RWSEM_WAKE_READERS);

    /* wait until we successfully acquire the lock */
    set_task_state(tsk, TASK_UNINTERRUPTIBLE);
    while (true) 
        if (!(count & RWSEM_ACTIVE_MASK)) 
            /* Try acquiring the write lock. */
            //4.没有active时,尝试获取锁,添加write active标记
            count = RWSEM_ACTIVE_WRITE_BIAS;
            if (!list_is_singular(&sem->wait_list))
                count += RWSEM_WAITING_BIAS;

            if (sem->count == RWSEM_WAITING_BIAS &&
                cmpxchg(&sem->count, RWSEM_WAITING_BIAS, count) ==
                            RWSEM_WAITING_BIAS)
                break;
        

        raw_spin_unlock_irq(&sem->wait_lock);

        /* Block until there are no active lockers. */
        do 
            schedule();
            set_task_state(tsk, TASK_UNINTERRUPTIBLE);
         while ((count = sem->count) & RWSEM_ACTIVE_MASK);

        raw_spin_lock_irq(&sem->wait_lock);
    

    list_del(&waiter.list);  
    raw_spin_unlock_irq(&sem->wait_lock);
    tsk->state = TASK_RUNNING;

    return sem;

读写锁的实现总结:

  • 当读者持有锁时,且没有写者等待时,后续的读者可以直接获取锁成功;
  • 当有一个写者等待时,后续的写者和读者都不能获取锁;
  • 当写者持有锁时,后续的写者和读者都不能获取锁;
  • 等待wait_list上,第一个都是写者;
  • 当写者释放锁时,等待wait_list上的第一个将会获取锁(不管其是读者还是写者);

参考:http://linux.laoqinren.net/kernel/rw-semaphore/

以上是关于内核中读写信号量(rwsem)实现的关键注释的主要内容,如果未能解决你的问题,请参考以下文章

内核中读写信号量(rwsem)实现的关键注释

linux内核 —— 读写信号量实验

todo:读写信号量

精通内核Linux 内核写锁实现原理与源码解析

不相关进程的读写器信号量

《Linux内核设计与实现》读书笔记- 内核同步方法