信号量

信号量(semaphore)是用于保护临界区的一种常用方法。只有得到信号量的进程才能执行临界区代码,而没有得到信号量的进程进入休眠等待状态。

1 定义信号量
struct semaphore sem;

include/linux/semaphore.h
struct semaphore{
       spinlock_t              lock;
       unsigned int           count;
       struct list_head       //管理所有在该信号量上睡眠的进程个数
};
2  初始化信号量
void sema_init(struct semaphore* sem, int val)
函数用于初始化信号量,并设置信号量sem的值为val。尽管信号量可以被初始化为大于1的值从而成为一个计数信号量, 设置为 1 或者 0 是信号量变成互斥锁
#define init_MUTEX(sem)                sema_init(sem, 1) // 用于互斥
#define init_MUTEX_LOCKED(sem)         sema_init(sem, 0) // 用于同步 completion完成量就根据信号量初始化为0实现的 如果信号量被初始化为0,则它可以用于同步,同步意味着一个执行单元的继续执行需要等待另一个执行单元完成某事,保证执行的先后顺序
使用init_MUTEX(sem) 初始化信号量时,表示信号量最初是可以被获取的。而使用init_MUTEX_LOCKED(sem) 初始化信号量时,此信号量只有先被释放才可以获取。

3 获取信号量
void down(struct semaphore *sem); // 该函数用于获取信号量sem,它会导致睡眠,因此不能在中断上下文使用

void down(struct semaphore *sem)
{
        unsigned long flags;

        spin_lock_irqsave(&sem->lock, flags);
        if (likely(sem->count > 0)) // 信号量大于0, 表示可以获得
                sem->count--; // 获取信号量之后减 1
        else
                __down(sem); // 获取信号量失败, 进入休眠状态, 并将此进程插入到等待队列尾部
        spin_unlock_irqrestore(&sem->lock, flags);
}
static noinline void __sched __down(struct semaphore *sem)
{
    __down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}

struct semaphore_waiter {
    struct list_head list;
    struct task_struct *task;
    int up;
};
static inline int __sched __down_common(struct semaphore *sem, long state,
                                long timeout)
{
    struct task_struct *task = current;
    struct semaphore_waiter waiter;

    list_add_tail(&waiter.list, &sem->wait_list); //  将该进程放入等待队列
    waiter.task = task;
    waiter.up = 0;

    for (;;) {
        if (signal_pending_state(state, task)) // 根据state确定是否可以被信号中断打断
            goto interrupted;
        if (timeout <= 0)
            goto timed_out;
        __set_task_state(task, state);
        raw_spin_unlock_irq(&sem->lock);
        timeout = schedule_timeout(timeout); // 让出cpu,进入休眠, timeoutt时间后再回来
        raw_spin_lock_irq(&sem->lock);
        if (waiter.up)
            return 0; // 回来时如果可以获得信号量waiter.up 即不为零,则返回
    }

 timed_out:
    list_del(&waiter.list);
    return -ETIME;

 interrupted:
    list_del(&waiter.list);
    return -EINTR;
}
int down_interruptible(struct semaphore *sem); // 获取信号量失败进入休眠后可以被信号打断, 跟down(struct semaphore *sem) 区别在于__down_common() 中的第二个 参数为TASK_INTERRUPTIBLE
int down_interruptible(struct semaphore *sem)
{
    unsigned long flags;
    int result = 0;

    spin_lock_irqsave(&sem->lock, flags);
    if (likely(sem->count > 0))
        sem->count--;
    else
        result = __down_interruptible(sem);
    spin_unlock_irqrestore(&sem->lock, flags);

    return result;
}
static noinline int __sched __down_interruptible(struct semaphore *sem)
{
    return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); // 可参考上面__down_common()代码
}

这里我们可以看到,当获取信号量成功时,返回0,而获取信号量失败时,返回一个非0的值。在使用down_interruptible()函数获取信号量时,对返回值一般会进行检查,如果非0,通常立即返回-ERESTARTSYS。如:
if (down_interruptible(&sem))
    return -ERESTARTSYS;  // 如果获取不到信号量 进入休眠且被信号中断,则返回ERESTARTSYS 让用户空间重启进程
4 释放信号量
void up(struct semaphore *sem); // 该函数用于释放信号量sem,唤醒等待者。

void up(struct semaphore *sem)
{
    unsigned long flags;

    raw_spin_lock_irqsave(&sem->lock, flags);
    if (likely(list_empty(&sem->wait_list)))
        sem->count++; // 信号量加1
    else
        __up(sem); //
    raw_spin_unlock_irqrestore(&sem->lock, flags);
}
static noinline void __sched __up(struct semaphore *sem)
{
    struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
                        struct semaphore_waiter, list);
    list_del(&waiter->list);
    waiter->up = 1; // 进程获得信号量
    wake_up_process(waiter->task); // 唤醒该进程
}





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值