• 信号量机制DOWN操作和UP操作的详细说明



    DOWN操作:linux内核。信号DOWN例如,下面的操作:

    • void down(struct semaphore *sem); //不间断
    • int down_interruptible(struct semaphore *sem);//可中断
    • int down_killable(struct semaphore *sem);//睡眠的进程能够由于受到致命信号而被唤醒,中断获取信号量的操作。

    • int down_trylock(struct semaphore *sem);//试图获取信号量,若无法获得则直接返回1而不睡眠。

      返回0则 表示获取到了信号量

    • int down_timeout(struct semaphore *sem,long jiffies);//表示睡眠时间是有限制的。假设在jiffies指明的时间到期时仍然无法获得信号量,则将返回错误码。

    在以上四种函数中,驱动程序使用的最频繁的就是down_interruptible函数,下面将对该函数进行分析。

    down_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;
    }

    函数分析:函数首先通过spin_lock_irqsave的调用来保证对sem->count操作的原子性。假设count>0。表示当前进程能够获得信号量,将count的值减1然后退出。

    假设count不大于0,表明当前进程无法获取信号量,则调用__down_interruptible,后者会继续调用__down_common。

    __down_common 函数定义例如以下:

    static inline int __sched __down_common(struct semaphore *sem, longstate,
                                                            longtimeout)
    {
           struct task_struct *task= current;
           struct semaphore_waiterwaiter;
           list_add_tail(&waiter.list,&sem->wait_list);
           waiter.task = task;
           waiter.up = 0; 
           for (;;) {
                  if(signal_pending_state(state, task))
                         gotointerrupted;
                  if (timeout <=0)
                         gototimed_out;
                  __set_task_state(task,state);
                  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;
    }

    函数分析:在__down_common函数数运行了下面操作。

    (1)将当前进程放到信号量成员变量wait_list所管理的队列中。

    (2)在一个for循环中把当前的进程状态这是为TASK_INTERRUPTIBLE,在调用schedule_timeout使当前进程进入睡眠状态。函数将停留在schedule_timeout调用上,知道再次被调度运行。

    (3) 当该进程再一次被调度时,按原因运行对应的操作:假设waiter.up不为0说明进程被该信号量的up操作所唤醒,进程能够获得信号量。假设进程是由于被用户空间的信号所中断或超时信号所引起的唤醒。则返回对应的错误代码。

    UP操作:LINUX内核仅仅提供了一个up函数

    up函数定义例如以下:

    void up(struct semaphore *sem)
    {
           unsigned long flags;
     
           spin_lock_irqsave(&sem->lock,flags);
           if(likely(list_empty(&sem->wait_list)))
                  sem->count++;
           else
                  __up(sem);
           spin_unlock_irqrestore(&sem->lock,flags);
    }

    函数分析:假设sem的wait_list队列为空,则表明没有其它进程正在等待该信号量,那么仅仅须要把sem的count加1就可以。假设wait_list队列不为空,则说明有其它进程正睡眠在wait_list上等待该信号。此时调用__up(sem)来唤醒进程:

    __up()函数定义例如以下:

    static noinline void __sched __up(struct semaphore *sem)
    {
           struct semaphore_waiter*waiter = list_first_entry(&sem->wait_list,
                                              structsemaphore_waiter, list);
           list_del(&waiter->list);
           waiter->up = 1;
           wake_up_process(waiter->task);
    }

    函数分析:在函数中,调用了wake_up_process来唤醒进程,这样进程就从之前的__down_interruptible调用中的timeout=schedule_timeout(timeout)处醒来,wait-up=1, __down_interruptible返回0。进程获得了信号量。

    up()与down()函数之间的联系:由上面对两个函数的分析能够知道,__down_common函数中timeout=schedule_timeout(timeout) 它具有非常重要的数据。

    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    回溯算法(DFS:深度优先)
    KNN原理和实现
    Anaconda虚拟环境控制
    c++容器
    最坏情况为线性时间的选择算法
    JVM原理解析
    substr、substring和slice的区别
    word-wrap与break-word属性的区别
    js修改伪类的值
    快速批量删除文件名中相同的文字
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4849064.html
Copyright © 2020-2023  润新知