關於讀寫信號量 (linux 2.6.11)

 

 

 

 

 

讀寫信號量的分析:

1 X86 彙編指令

js  :   jump on SF (sign flag)

xadd   src, dest;

       temp = src + dest;

       src = dest;

       dest = temp;

 

2)信號量定義:

struct rw_semaphore {

    signed long       count;

    spinlock_t    wait_lock;

    struct list_head  wait_list;

};

count: 關於讀者和寫者的計數:

16位: Active Writer + Waiting Control Path(Read & Write);

16位: Nonwaiting Reader + Nonwaiting Writer;

 

#define RWSEM_UNLOCKED_VALUE       0x00000000

#define RWSEM_ACTIVE_BIAS          0x00000001

#define RWSEM_ACTIVE_MASK          0x0000ffff

#define RWSEM_WAITING_BIAS         (-0x00010000)

#define RWSEM_ACTIVE_READ_BIAS     RWSEM_ACTIVE_BIAS

#define RWSEM_ACTIVE_WRITE_BIAS    (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)

 

3) count sem 是分開保護的, sem 通過wait_lock保護, count 通過原子指令保護;

4)具體代碼如下

_down_read:

lock:  sem->count = sem->count + (0x0000 0001)// increase active reader;

       if (sem->count < 0)      // has writer or waiter already

       {

           /*

* decrease active reader & increase waitting control path

           */

           rwsem_down_failed_common(sem,

&waiter,

(-0x0001 0000 -0x0000 0001));

       }

 

 

 

 

 

 

 

 

_down_write:

/* increase active writer (in high part & in low part)*/

lock:  temp_sum = sem->count + (-0x0001 0000 + 0x0000 0001);

lock:  tmp = sem->count;

lock:  sem->count = temp_sum;

       if (tmp != 0)        // must be zero if the writer keeps on;

       {

           /*

           * decrease active writer, trans active writer count

           * (-0x0001 0000) to waitting control path;

           */

           rwsem_down_failed_common(sem, &waiter, (-0x0000 0001));

       }

 

 

 

 

 

 

rwsem_down_failed_common(sem, waiter, adjustment)

 

       set_task_state(tsk, TASK_UNINTERRUPTIBLE);

       spin_lock(&sem->wait_lock);

       add_tail(&waiter->list, &sem->wait_list);

 

 

/*

* failed to get semaphore, do some retify to mark waitting path count;

*/

lock:  tmp_sum = adjustment + sem->count;

lock:  tmp = sem->count;

lock:  sem->count = tmp_sum;

   

       rettemp = sem->count; /*tmp +  adjustment;*/

 

       /* no active process ,keeps on */

       if ((rettemp & 0x0000 FFFF) == 0x0000 0000)

       {

           sem = _rwsem_do_wake(sem, 0); 

       }

 

       spin_unlock(&sem->wait_lock);

       for ( ; ;)

       {

           if (!waiter->task)

           {

              break;

           }

           schedule();

           set_task_state(tsk, TASK_UNINTERRUPTIBLE);

       }

 

       tsk->state = TASK_RUNNING;

 

 

喚醒函數:

__rwsem_do_wake(struct rw_semaphore *sem, int downgrading)

{

    struct rwsem_waiter *waiter;

    struct task_struct *tsk;

    struct list_head *next;

    signed long oldcount, woken, loop;

 

   

 

    if (downgrading)

       goto dont_wake_writers;

 

try_again:

    /* when active count is 0x0000, increase it by 1 to  prohibit others

    *  operation;

    */

    oldcount = rwsem_atomic_update(RWSEM_ACTIVE_BIAS, sem)

-        RWSEM_ACTIVE_BIAS;

 

/* some one already modify the count field, so undo it */

    if (oldcount & RWSEM_ACTIVE_MASK)

       goto undo;

 

 

 

 

    waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list);

 

 

    /*

    * if the first path in the Queue is’t a writer, we wake all reader

    * in the queue;  

    */

    if (!(waiter->flags & RWSEM_WAITING_FOR_WRITE))

       goto readers_only;

 

    /*

    * wake writer here. for a writer add 0x0000 0001 to indicat an active

    * writer, the waitting path control part -0x0001 0000 automately change

    * to mark an active writer in the HIGH part; 

    */

    list_del(&waiter->list);

    tsk = waiter->task;

    mb();

    waiter->task = NULL;

    wake_up_process(tsk);

    put_task_struct(tsk);

    goto out;

 

    /* don't want to wake any writers */

 dont_wake_writers:

    waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list);

    if (waiter->flags & RWSEM_WAITING_FOR_WRITE)

       goto out;

 

readers_only:

    /*

    * wake all reader here;

    */

    woken = 0;

    do {

       woken++;

 

       if (waiter->list.next == &sem->wait_list)

           break;

 

       waiter = list_entry(waiter->list.next,

                  struct rwsem_waiter, list);

 

    } while (waiter->flags & RWSEM_WAITING_FOR_READ);

 

    loop = woken;

    woken *= RWSEM_ACTIVE_BIAS - RWSEM_WAITING_BIAS;

    if (!downgrading)

       /* we'd already done one increment earlier */

       woken -= RWSEM_ACTIVE_BIAS;

 

    /* + RWSEM_ACTIVE_BIAS, indicate an active reader

    *  - RWSEM_WAITING_BIAS because of the reader process state

    * changing from waiting to active;

    */

    rwsem_atomic_add(woken, sem);

 

    next = sem->wait_list.next;

    for (; loop > 0; loop--) {

       waiter = list_entry(next, struct rwsem_waiter, list);

       next = waiter->list.next;

       tsk = waiter->task;

       mb();

       /* dechain the process */

       waiter->task = NULL;

       wake_up_process(tsk);

       put_task_struct(tsk);

    }

 

    sem->wait_list.next = next;

    next->prev = &sem->wait_list;

 

 out:

    rwsemtrace(sem, "Leaving __rwsem_do_wake");

    return sem;

 

    /* undo the change to count, but check for a transition 1->0 */

 undo:

    if (rwsem_atomic_update(-RWSEM_ACTIVE_BIAS, sem) != 0)

       goto out;

    goto try_again;

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章