Process scheduling - Linux Kernel 实现 欣赏

这篇内容可能会较多,慢慢来欣赏

这是以前分析的
http://blog.csdn.net/leesagacious/article/details/50493939

这是github上模拟linux kernel的等待队列,暂未实现
https://github.com/leesagacious/wait_queue

这个是在各个子系统中的具体实现
http://blog.csdn.net/leesagacious/article/details/51627608

进程需要休眠,是为了等待某些资源可用或者某些事件发生
比如 设备初始化完成、I/O操作完成、定时器到时、进程同步、中断到来等

linux kernel 使用 等待队列来将 process 与 它等待的资源、事件关联在一起.
不是常说吗,当你的能力还驾驭不了你的目标时,就应该沉下心来,历练………哈哈, process 也是这样,需要资源或者事件,那就在等待队列上休息吧

那么,问题来了,多个process在等待同一个事件发生、资源满足.如果满足了,唤醒是怎么一个场景?
下面有分析

一 :显然,队列上的每个Node表示一个处于睡眠状态的进程, 那么,是用什么来表示一个处于睡眠状态的进程?
是下面这个struct,

/*
    等待队列上的一个元素,即 表示一个睡眠的进程
    睡眠是暂时的,睡眠的时候还持有一个被唤醒的回调函数 func

    不同睡眠状态的进程都可以在同一个队列上,这个与中断处理函数设计的不同,
    在Interrupt中,不是共享中断且触发类型不同的isr,是不会让给你挂接到isr链表上的
    中断限制的更严格点
*/
struct __wait_queue {
    /*
        这个flags很重要,涉及到一个被<<深入Linux内核架构>>称之为“惊群效应”的问题
        下面会有分析
    */
    unsigned int flags;
    /*
        0x01 很重要 !
    */
#define WQ_FLAG_EXCLUSIVE   0x01
    /*
        在初始化等待队列元素的时候 ,会指向睡眠进程的task_struct.
        看!没有等待队列元素,别想挂入等待队列,这里的等待队列元素就
        相当于要睡眠进程的一张单人床.
    */
    void *private;
    /*
        自然是唤醒函数了,这个唤醒函数可以由用户自己指定,也可以使用系统默认的唤醒函数
        这也是在初始化等待队列时,通过不同的函数来指定的.
        那么,问了,如果是用户自定义的唤醒函数,被唤醒是一个什么样的流程和场景,别急,
        下面有分析
    */
    wait_queue_func_t func;
    /*
        等待队列也是一个普通的内核链表,双向循环链表
    */
    struct list_head task_list;
};
include/linux/wait.h

唤醒的过程

简单的画了一个小流程框图,帮你理解

这里写图片描述

代码流程欣赏 :

/*
    @x
        等待队列头,你要唤醒的Process所在的等待队列的队列头

    @TASK_NORMAL
        #define TASK_NORMAL  (TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)
        看上面的宏,可以知道 wake_up 能唤醒这两种状态的睡眠进程
        比 TASK_ALL 少了两个

    @nr_exclusive   
        唤醒几个标志位为WQ_FLAG_EXCLUSIVE的Process
        这里是 1 ,只唤醒一个,即 找到了,就停止遍历,就brea了
        如果你要唤醒多个,可以使用另外一个函数,wake_up_nr(),它指定了唤醒的个数

    @key
        如果找到了匹配的Process,那么就会回调它持有的唤醒函数,这个key是传递给唤醒函数的参数 

    看到了把,wake_up() 的参数就表明了它要干什么、它能干什么了

    好,唤醒的条件都给你了,下面就是看__wake_up()怎么表演了,
    下面关注一下 这些参数在底层函数的活动
*/
#define wake_up(x)  __wake_up(x,TASK_NORMAL,1,NULL)

void __wake_up(wait_queue_head_t *q, unsigned int mode,int nr_exclusive, void *key)
{
    /**
        一看到这个未初始化的flags 下面一定要spin_lock_irqsave、spin_lock_irqrestore了
    */
    unsigned long flags;
    /*
        锁     : 防止来自其他处理器的并发访问
        禁用中断: 防止来自中断处理程序的并发访问

        内核抢占呢?  
        加自旋锁了,就禁止内核抢占了,自旋锁期间,你还想发生内核抢占!!
        为什么? 看下面

        #define spin_lock_irqsave(lock, flags)              
        do {                                
            raw_spin_lock_irqsave(spinlock_check(lock), flags); 
        } while (0)

        ↓
        #define raw_spin_lock_irqsave(lock, flags)      
        do {                        
            typecheck(unsigned long, flags);    
            _raw_spin_lock_irqsave(lock, flags);    
        } while (0)

        ↓
        unsigned long __lockfunc _raw_spin_lock_irqsave(raw_spinlock_t *lock)
        {
            return __raw_spin_lock_irqsave(lock);
        }

        ↓
        static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
        {
            ...
            preempt_disable();
        }

        ↓
        #define preempt_disable() 
        do { 
            inc_preempt_count(); 
            barrier(); 
        } while (0)

        ↓   接近真相了
        #define inc_preempt_count() add_preempt_count(1)

        ↓
        #define add_preempt_count(val)  do { preempt_count() += (val); } while (0)

        ↓
        #define preempt_count() (current_thread_info()->preempt_count)

        隐藏的好深呀,这次我拿出来让它透透气、见见光,哈哈!
        具体是 + 1 的操作.解锁的时候 -1,这里不再赘述了

        看到了把,preempt_count计数器的值,决定了允不允许抢占,

        这个preempt 是一个 int 类型,下面会分析它的具体位置代表的含义

        我只想说,仅仅这个preempt的值这一点,可以一叶知秋看出调度可真够精彩的!!  还不止,还有调度类、周期性调度任务
        cpu亲和力、虚拟运行时间、进程权重、唤醒抢占、优先级、cpu负载均衡.......... 一大波重量级的嘉宾正在赶来
         哈哈!
    */
    spin_lock_irqsave(&q->lock, flags);
    /**
        受保护的这个函数,它就是遍历上图的那个链表了
        它执行的那个唤醒函数 将会是我们分析的重点
        因为,里面包含了新唤醒的这个进程能否抢占到当前运行的进程的处理
    */
    __wake_up_common(q, mode, nr_exclusive, 0, key);
    spin_unlock_irqrestore(&q->lock, flags);
}

/**
    这段代码太撩人!! 作者是哪位大神??
*/
static int
try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
{
    unsigned long flags;
    int cpu, success = 0;
}

猜你喜欢

转载自blog.csdn.net/leesagacious/article/details/64980255