關鍵詞: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資源。