linux2.6.32内核信号量的实现

来源:互联网 发布:健身软件电脑版 编辑:程序博客网 时间:2024/05/22 04:44

首先看看信号量的相关数据结构:

<include/linux/semaphore.h>

struct semaphore{
    spinlock_t lock; #lock应该是这个信号量的自旋锁
    unsigned intcount; #count表示的是这个信号量的计数器
    struct list_head wait_list;#wait_list顾名思义应该是等待链表了
};

信号量的初始化:

<include/linux/semaphore.h>

#define DECLARE_MUTEX(name) \
    struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)

#define __SEMAPHORE_INITIALIZER(name, n) \
{ \
    .lock = __SPIN_LOCK_UNLOCKED((name).lock), \ #初始化自旋锁
    .count = n, \ #将信号量计数器赋值为n
    .wait_list = LIST_HEAD_INIT((name).wait_list), \ #初始化等待队列
}


也可以用以下方法来初始化信号量:

<include/linux/semaphore.h>

staticinline void sema_init(struct semaphore*sem, int val)
{
    static struct lock_class_key __key;
    *sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
    lockdep_init_map(&sem->lock.dep_map,"semaphore->lock",&__key, 0);
}

#define init_MUTEX(sem) sema_init(sem, 1)
#define init_MUTEX_LOCKED(sem) sema_init(sem, 0)


PV操作:

P操作

<kernel/semaphore.c>

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

    spin_lock_irqsave(&sem->lock, flags);
    if (likely(list_empty(&sem->wait_list)))#如果等待链表为空,表示没有正在等待此信号量的进程,count++就行
        sem->count++;
    else
        __up(sem);
    spin_unlock_irqrestore(&sem->lock, flags);
}

static noinlinevoid __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);#唤醒这个进程
}

此函数从等待列表中移出最前面的那个进程,然后唤醒它

struct semaphore_waiter{
    struct list_head list;#链表项
    struct task_struct *task; #进程结构,也可以叫做进程PCB
    int up;
};


这个结构主要就是用来辅助把进程放到信号量的等待队列,后面的V操作会再介绍它

V操作:

<kernel/semaphore.c>

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

    spin_lock_irqsave(&sem->lock, flags);#要对sem进行操作了,加锁
    if (likely(sem->count> 0))#如果count>0,直接count--就行了
        sem->count--;
    else
        __down(sem);#调用__down()
    spin_unlock_irqrestore(&sem->lock, flags);#对sem操作完毕,释放锁
}

如果count是<=0的,那么调用__down()来把进程放进等待队列里,现在看看__down()是怎么实现的

static noinlinevoid __sched __down(struct semaphore*sem)
{
    __down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_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加到链表sem->wait_list后面
    waiter.task = task;#waiter的task设置成正在运行的这个进程
    waiter.up = 0;

    for (;;){
        if (signal_pending_state(state, task))#如果进程被一个意外的信号中断,直接放弃等待(从等待链表中删除),返回-EINTR错误号
            goto interrupted;
        if (timeout<= 0)#如果等待时间到,从等待链表中删除之,返回-ETIME错误号
            goto timed_out;
        __set_task_state(task, state);#设置进程状态为TASK_UNINTERRUPTIBLE
        spin_unlock_irq(&sem->lock);
        timeout = schedule_timeout(timeout);#调度
        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;
}

该函数首先申请一个semaphore_waiter结构,

<kernel/semaphore.c>

struct semaphore_waiter{
    struct list_head list;
    struct task_struct *task;
    int up;
};


然后将这个结构加入到sem信号量的等待链表sem->wait_list中,接着将当前进程task填入waiter.task中,
然后在一个循环中进行进程调度schedule_timeout,每进行一次调度,timeout会减小
如果当前进程task的状态位就绪,则跳转到interrupt,从信号量列表sem-wait_list中删除这个任务对应的节点
如果遇到意外的信号或者等待时间到,从信号量列表sem-wait_list中删除这个任务对应的节点,返回相应的错误号


内核还提供了其他几种V操作:
static noinline int __down_interruptible(struct semaphore *sem);
static noinline int __down_killable(struct semaphore *sem);
static noinline int __down_timeout(struct semaphore *sem, long jiffies);
这里就不一一介绍了
读者也可以参考这篇文章
内核同步机制-信号量


原创粉丝点击