zoukankan      html  css  js  c++  java
  • sched_yield()和nanosleep()对进程调度的影响

    关键词:sched_yield()、nanosleep()等等。

    sched_yield()主动放弃CPU执行权,nanosleep()是睡眠一段时间后再唤醒。 

    1. sched_yield()实现

    sched_yield()会主动放弃当前CPU给其他进程使用;但是如果当前CPU上无其他进程等待执行,则直接返回继续执行当前进程。

    调用sched_yield()之后当前进程会被移动到进程优先级等待队列尾部,让相同或者更高优先级进程运行。

    sched_yield()确保当前进程在资源竞争严重时,给其他进程执行机会来提高性能。

    SYSCALL_DEFINE0(sched_yield)
    {
        struct rq *rq = this_rq_lock();
    
        schedstat_inc(rq->yld_count);
        current->sched_class->yield_task(rq);
    
        __release(rq->lock);
        spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
        do_raw_spin_unlock(&rq->lock);
        sched_preempt_enable_no_resched();
    
        schedule();
    
        return 0;
    }
    
    asmlinkage __visible void __sched schedule(void)
    {
        struct task_struct *tsk = current;
    
        sched_submit_work(tsk);
        do {
            preempt_disable();
            __schedule(false);
            sched_preempt_enable_no_resched();
        } while (need_resched());
    }
    
    static void __sched notrace __schedule(bool preempt)
    {
        struct task_struct *prev, *next;
        unsigned long *switch_count;
        struct pin_cookie cookie;
        struct rq *rq;
        int cpu;
    ...
        next = pick_next_task(rq, prev, cookie);---------------------选择优先级最高的进程作为下一个运行进程。
        clear_tsk_need_resched(prev);
        clear_preempt_need_resched();
        rq->clock_skip_update = 0;
    
        if (likely(prev != next)) {----------------------------------如果sched_yield()后,当前进程prev即为优先级最高的进程,即prev==next。那么则不会进行进程切换操作,直接返回。
            rq->nr_switches++;
            rq->curr = next;
            ++*switch_count;
    
            trace_sched_switch(preempt, prev, next);
            rq = context_switch(rq, prev, next, cookie); /* unlocks the rq */
        } else {
            lockdep_unpin_lock(&rq->lock, cookie);
            raw_spin_unlock_irq(&rq->lock);
        }
    
        balance_callback(rq);
    }

    对于CFS调度器类,yield_task()对应yield_task_fair()。

    static void yield_task_fair(struct rq *rq)
    {
        struct task_struct *curr = rq->curr;
        struct cfs_rq *cfs_rq = task_cfs_rq(curr);
        struct sched_entity *se = &curr->se;
    
        if (unlikely(rq->nr_running == 1))--------------------如果当前运行队列上仅有一个运行进程,直接返回。
            return;
    
        clear_buddies(cfs_rq, se);
    
        if (curr->policy != SCHED_BATCH) {
            update_rq_clock(rq);
    
            update_curr(cfs_rq);
    
            rq_clock_skip_update(rq, true);
        }
    
        set_skip_buddy(se);
    }

    下面是系统无其他进程运行时,可以看出进程独占了CPU很长时间。只是在有其他内核线程运行后,才放弃CPU执行权。

    2. nanosleep()和sched_yield()对比

    2.1 while(true)尽量独占CPU

    #include <iostream>  
    #include <chrono>  
    #include <thread> 
    #include <atomic>
    #include <mutex>
    #include <time.h>
    
    std::mutex g_mutex;
    std::atomic<bool> ready(false);
    
    void count1m(int id) 
    {
        while (true)
        {             
        }
    }
    
    int main()
    {
        std::thread threads;
    
        threads = std::thread(count1m, 1);
        threads.join();
    
        return 0;
    }

    这种情况进程会尽可能独占整个CPU,但是在有竞争进程存在的时候,需要和其他进程均分CPU时间。所以出现下面每工作4ms,然后切换出去4ms时间的情况。

    在没有其他进程运行的时候,可以独占CPU时间。

    2.2 nanosleep()进程休眠一段时间

    #include <iostream>  
    #include <chrono>  
    #include <thread> 
    #include <atomic>
    #include <mutex>
    #include <time.h>
    
    std::mutex g_mutex;
    std::atomic<bool> ready(false);
    
    void count1m(int id) 
    {
        struct timespec delay;
        delay.tv_sec = 0;
        delay.tv_nsec = 1000000;
        while (true)
        {             
            nanosleep(&delay, NULL);
        }
    }
    
    int main()
    {
        std::thread threads;
    
        threads = std::thread(count1m, 1);
        threads.join();
    
        return 0;
    }

    间隔休眠唤醒情况下,即使系统中存在其他进程在运行,当前进程唤醒后仍然可以抢到CPU资源,sched_switch表示放入队列,sched_wakeup表示得到CPU资源,中间可能存在一定延时。

    在没有其他进程情况下,能更快得到调度。

    2.3 sched_yield()主动放弃

    #include <iostream>  
    #include <chrono>  
    #include <thread> 
    #include <atomic>
    #include <mutex>
    #include <time.h>
    
    std::mutex g_mutex;
    std::atomic<bool> ready(false);
    
    void count1m(int id) 
    {
        while (true)
        {             
            std::this_thread::yield();
        }
    }
    
    int main()
    {
        std::thread threads;
    
        threads = std::thread(count1m, 1);
        threads.join();
    
        return 0;
    }

    这种情况和第一种区别在于,sched_yield()会主动放弃CPU执行权。第一种情况是根据CFS调度器类来分配时间;这里还结合了进程主动放弃调度的情况。

    2.4 sched_yield()和nanosleep()混合使用

    #include <iostream>  
    #include <chrono>  
    #include <thread> 
    #include <atomic>
    #include <mutex>
    #include <time.h>
    
    std::mutex g_mutex;
    std::atomic<bool> ready(false);
    
    void count1m(int id) 
    {
        struct timespec delay;
        delay.tv_sec = 0;
        delay.tv_nsec = 1000000;
        while (true)
        {             
            std::this_thread::yield();
            nanosleep(&delay, NULL);
        }
    }
    
    int main()
    {
        std::thread threads;
    
        threads = std::thread(count1m, 1);
        threads.join();
    
        return 0;
    }

    这种情况下sched_yield()和nanosleep()叠加使用和单独使用nanosleep()效果类似,nanosleep()本省也是主动放弃CPU使用权。

    所以综合来看while(1)中使用sched_yield()要比延时的响应更及时,但是也牺牲了CPU占用率。在没有其他进程运行的情况下,sched_yield()就会一个人独占CPU资源。

  • 相关阅读:
    菜根谭#298
    菜根谭#297
    菜根谭#296
    菜根谭#295
    菜根谭#294
    菜根谭#293
    菜根谭#292
    菜根谭#291
    菜根谭#290
    菜根谭#289
  • 原文地址:https://www.cnblogs.com/arnoldlu/p/11287972.html
Copyright © 2011-2022 走看看