[Linux同步]读写信号量

以下内容转自: 信号量

一,信号量和自旋锁的区别

信号量与自旋锁有一定的区别,信号量在无法得到资源时,内核线程处于睡眠阻塞状态,而自旋锁处于忙等待状态。因此,如果资源被占用时间很短时,使用自旋锁较好,因为它可节约调度时间。如果资源被占用的时间较长,使用信号量较好。


二,信号量相关的API

信号量的相关API

函数定义功能说明
sema_init(struct semaphore *sem, int val)初始化信号量,将信号量计数器值设置val。
down(struct semaphore *sem)获取信号量,不建议使用此函数。
down_interruptible(struct semaphore *sem)可被中断地获取信号量,如果睡眠被信号中断,返回错误-EINTR。
down_killable (struct semaphore *sem)可被杀死地获取信号量。如果睡眠被致命信号中断,返回错误-EINTR。
down_trylock(struct semaphore *sem)尝试原子地获取信号量,如果成功获取,返回0,不能获取,返回1。
down_timeout(struct semaphore *sem, long jiffies)在指定的时间jiffies内获取信号量,若超时未获取,返回错误-ETIME。
up(struct semaphore *sem)释放信号量sem。

读写信号量的相关API

API函数定义功能说明
DECLARE_RWSEM(name)声明名为name的读写信号量,并初始化它。
void init_rwsem(struct rw_semaphore *sem);对读写信号量sem进行初始化。
void down_read(struct rw_semaphore *sem);读者用来获取sem,若没获得时,则调用者睡眠等待。
void up_read(struct rw_semaphore *sem);读者释放sem。
int down_read_trylock(struct rw_semaphore *sem); 读者尝试获取sem,如果获得返回1,如果没有获得返回0。可在中断上下文使用。
void down_write(struct rw_semaphore *sem);写者用来获取sem,若没获得时,则调用者睡眠等待。
int down_write_trylock(struct rw_semaphore *sem);写者尝试获取sem,如果获得返回1,如果没有获得返回0。可在中断上下文使用
void up_write(struct rw_semaphore *sem);写者释放sem。
void downgrade_write(struct rw_semaphore *sem);把写者降级为读者。


三,信号量相关的函数

信号量的结构体:

struct semaphore {
    spinlock_t lock;
    unsigned int count;
    struct list_head wait_list;
};

读写信号量的结构, 在include/linux/rwsem-spinlock.h中

struct rw_semaphore {
    /*读/写信号量定义:
    * - 如果activity为0,那么没有激活的读者或写者。
    * - 如果activity为+ve,那么将有ve个激活的读者。
    * - 如果activity为-1,那么将有1个激活的写者。 */
    __s32 activity; /*信号量值*/
    spinlock_t wait_lock; /*用于锁等待队列wait_list*/
    struct list_head wait_list; /*如果非空,表示有进程等待该信号量*/
#ifdef CONFIG_DEBUG_LOCK_ALLOC /*用于锁调试*/
    struct lockdep_map dep_map;
#endif
};


读者加锁

读者加锁是通过down_read()函数完成的,

void __sched __down_read(struct rw_semaphore *sem)
{
    struct rwsem_waiter waiter;
    struct task_struct *tsk;

    spin_lock_irq(&sem->wait_lock);
    /*如果有0或多个读者,并且等待队列为空,就可以获取sem*/
    if (sem->activity >= 0 && list_empty(&sem->wait_list)) {
        /* 获得sem */
        sem->activity++; /*读者计数加1*/
        spin_unlock_irq(&sem->wait_lock);
        goto out;
    }

    /*运行到这里,说明不能获取sem,将当前进程加入等待队列进行等待*/
    tsk = current;
    set_task_state(tsk, TASK_UNINTERRUPTIBLE);

    /* 建立等待队列成员*/
    waiter.task = tsk;
    waiter.flags = RWSEM_WAITING_FOR_READ; /*表示等待读操作*/
    get_task_struct(tsk); /*进程使用计数加1*/

    list_add_tail(&waiter.list, &sem->wait_list); /*将等待成员加到等待队列尾*/

    /* 不再需要访问等待队列,因此,这里解锁*/
    spin_unlock_irq(&sem->wait_lock);

    /* 读者等待获取sem */
    for (;;) {
        if (!waiter.task)
            break;
        schedule();
        set_task_state(tsk, TASK_UNINTERRUPTIBLE);
    }
    /*运行这里,退出等待,说明可以获取sem了*/
    tsk->state = TASK_RUNNING;
out:
    ;
}

读者解锁

void __up_read(struct rw_semaphore *sem)
{
    unsigned long flags;

    spin_lock_irqsave(&sem->wait_lock, flags);
    /*如果所有读者完成读操作,并且有写者等待,那么唤醒一个写者*/
    if (--sem->activity == 0 && !list_empty(&sem->wait_list))
        sem = __rwsem_wake_one_writer(sem);

    spin_unlock_irqrestore(&sem->wait_lock, flags);
}

/*唤醒一个写者*/
static inline struct rw_semaphore *__rwsem_wake_one_writer(struct rw_semaphore *sem)
{
    struct rwsem_waiter *waiter;
    struct task_struct *tsk;

    sem->activity = -1; /*表示有一个写者正在写操作*/

    /*获取一个等待者*/
    waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list);
    list_del(&waiter->list); /*将该等待者从等待队列删除*/

    tsk = waiter->task;
    smp_mb(); /*加内存屏障,确保完成上面的指针引用操作*/
    waiter->task = NULL;
    wake_up_process(tsk); /*唤醒进程*/
    put_task_struct(tsk); /*进程上下文使用计数减1*/
    return sem;
}


写者加锁

void __sched down_write(struct rw_semaphore *sem)
{
    might_sleep();
    rwsem_acquire(&sem->dep_map, 0, 0, _RET_IP_);

    LOCK_CONTENDED(sem, __down_write_trylock, __down_write);
}

void __sched __down_write(struct rw_semaphore *sem)
{
    __down_write_nested(sem, 0);
}

函数__down_write_nested完成加写者锁的具体操作。当没有读者或写者操作时,写者才可以获取写者锁。写者锁是独占的。如果有其他写者或读者操作时,写者必须等待。其列出如下:

void __sched __down_write_nested(struct rw_semaphore *sem, int subclass)
{
    struct rwsem_waiter waiter;
    struct task_struct *tsk;

    spin_lock_irq(&sem->wait_lock);
    /*如果没有读者,并且等待队列为空(说明没有写者)时,写者才能获取写者锁*/
    if (sem->activity == 0 && list_empty(&sem->wait_list)) {
        /* 获取写者锁*/
        sem->activity = -1;
        spin_unlock_irq(&sem->wait_lock);
        goto out;
    }

    /*运行到这里,说明有读者或写者在操作,需要等待*/
    tsk = current;
    set_task_state(tsk, TASK_UNINTERRUPTIBLE);

    /* 建立等待队列成员*/
    waiter.task = tsk;
    waiter.flags = RWSEM_WAITING_FOR_WRITE; /*标识为等待写操作*/
    get_task_struct(tsk); /*进程上下文使用计数加1*/

    list_add_tail(&waiter.list, &sem->wait_list); /*加到等待队列尾*/ 
    spin_unlock_irq(&sem->wait_lock);

    /* 进行等待*/
    for (;;) {
        if (!waiter.task)
            break;
       schedule();
        set_task_state(tsk, TASK_UNINTERRUPTIBLE);
    }
    /*被唤醒*/
    tsk->state = TASK_RUNNING;
out:
    ;
}


解写者锁

函数up_write释放写者锁,将读者计数设置为0,其列出如下:
void up_write(struct rw_semaphore *sem)
{
    rwsem_release(&sem->dep_map, 1, _RET_IP_);

    __up_write(sem);
}

void __up_write(struct rw_semaphore *sem)
{
    unsigned long flags;

    spin_lock_irqsave(&sem->wait_lock, flags);

    sem->activity = 0; /*表示有0个读者*/
    if (!list_empty(&sem->wait_list))
        sem = __rwsem_do_wake(sem, 1); /*唤醒等待者*/

    spin_unlock_irqrestore(&sem->wait_lock, flags);
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值