linux代码之semaphore

/*
 * 2020/12/4    15:22    qing
 */

/*
 * 信号量是一种睡眠锁
 *
 * 本质上信号量是一个计数器,是进程间通信处理同步互斥的机制。
 * 是在多线程环境下使用的一种措施,它负责协调各个进程,以保证他们能够正确、合理的使用公共资源。
 *
 * 信号量一般可以用来标记可用资源的个数
 *
 * 信号量和spin lock最大的不同之处就是:无法获取信号量的进程可以睡眠,因此会导致系统调度。
 *
 *
 */

/*
 * semaphore
 */
/* Please don't access any members of this structure directly */
struct semaphore {
    raw_spinlock_t        lock;                /* spin lock, 保证自身的操作不被打断或修改 */
    unsigned int        count;                /* 计数 */
    struct list_head    wait_list;            /* 等待进程的链表头 */
};

/*
 * raw_spinlock_t
 */
typedef struct raw_spinlock {
    arch_spinlock_t raw_lock;
#ifdef CONFIG_GENERIC_LOCKBREAK
    unsigned int break_lock;
#endif
#ifdef CONFIG_DEBUG_SPINLOCK
    unsigned int magic, owner_cpu;
    void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
    struct lockdep_map dep_map;
#endif
} raw_spinlock_t;

/*
 * arch_spinlock_t
 */
typedef struct {
#ifdef __AARCH64EB__
    u16 next;
    u16 owner;
#else
    u16 owner;
    u16 next;
#endif
} __aligned(4) arch_spinlock_t;


/*
 * semaphore_waiter
 */
struct semaphore_waiter {
    struct list_head list;        /* 当进程无法获取信号量的时候挂入semaphore的wait_list成员 */
    struct task_struct *task;    /* 记录后续被唤醒的进程信息 */
    bool up;
};

/*
 * down
 *
 * deprecated (已弃用)
 */
void down(struct semaphore *sem)
{
    unsigned long flags;

    raw_spin_lock_irqsave(&sem->lock, flags);
    if (likely(sem->count > 0))
        sem->count--;
    else
        __down(sem);
    raw_spin_unlock_irqrestore(&sem->lock, flags);
}

/*
 * down_interruptible
 */
int down_interruptible(struct semaphore *sem)
{
    unsigned long flags;
    int result = 0;

    raw_spin_lock_irqsave(&sem->lock, flags);
    if (likely(sem->count > 0))
        sem->count--;                                /* 资源还有剩余 */
    else
        result = __down_interruptible(sem);            /* 进入等待信号量,任务调度睡眠 */
    raw_spin_unlock_irqrestore(&sem->lock, flags);

    return result;
}

static noinline void __sched __down(struct semaphore *sem)
{
    __down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}

static noinline int __sched __down_interruptible(struct semaphore *sem)
{
    return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}

static noinline int __sched __down_killable(struct semaphore *sem)
{
    return __down_common(sem, TASK_KILLABLE, MAX_SCHEDULE_TIMEOUT);
}

static noinline int __sched __down_timeout(struct semaphore *sem, long timeout)
{
    return __down_common(sem, TASK_UNINTERRUPTIBLE, timeout);
}

/*
 * __down_common
 *
 * 因为这个函数是内联的,所以'state'参数将是常量,因此编译器会对其进行优化。同样,对于没有超时的情况,使用"timeout"参数。
 */
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 = false;

    for (;;) {
        if (signal_pending_state(state, task))
            goto interrupted;
        if (unlikely(timeout <= 0))
            goto timed_out;
        __set_task_state(task, state);
        raw_spin_unlock_irq(&sem->lock);
        timeout = schedule_timeout(timeout);    /* 调度 */
        raw_spin_lock_irq(&sem->lock);
        if (waiter.up)
            return 0;
    }

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

 interrupted:
    list_del(&waiter.list);
    return -EINTR;
}

/**
 * up - release the semaphore
 *
 * 与互斥锁不同,up()可以从任何上下文调用,甚至可以由从未调用down()的任务调用。
 */
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 = true;
    wake_up_process(waiter->task);        /* 唤醒等待进程 */
}

猜你喜欢

转载自blog.csdn.net/xiaozhiwise/article/details/110665298