Chinaunix首页 | 论坛 | 博客
  • 博客访问: 259462
  • 博文数量: 74
  • 博客积分: 1470
  • 博客等级: 上尉
  • 技术积分: 793
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-25 21:01
文章分类

全部博文(74)

文章存档

2011年(1)

2010年(32)

2009年(32)

2008年(9)

我的朋友

分类: LINUX

2009-11-11 18:42:38

  最近在当操作系统助教,老师要求完成linux0.11下的信号量的实现,感觉很多师弟师妹们对信号量的实现还不是很清楚,于是详细的看了一下linux2.6.27内核关于信号量的实现

----------------------------------------------------------------------------------------------------------------

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

<include/linux/semaphore.h>

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

信号量的数据结构就这么简单.那么信号量的初始化是怎么做的呢?还有大家熟悉的信号量P,V操作是怎么实现的呢?

----------------------------------------------------------------------------------------------------------------

信号量的初始化:

<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>

static inline 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);
}


如果等待链表为空,表示没有正在等待此信号量的进程,count++就行

如果等待链表不为空,也就是还有进程在等待此信号量,那么此时进入到__up()这个函数中:


<kernel/semaphore.c>

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

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

<kernel/semaphore.c>

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()是怎么实现的

<kernel/semaphore.c>

static noinline void __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);
这里就不一一介绍了

读者也可以参考这篇文章
内核同步机制-信号量

阅读(1071) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~