在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

概述一下Linux內核搶占的圖景

嵌入式悅翔園 ? 來源:老吳嵌入式 ? 2023-04-18 11:50 ? 次閱讀

本文以內核搶占為引子,概述一下 Linux 搶占的圖景。

我盡量避開細節問題和源碼分析。????

什么是內核搶占?

別急,咱們慢慢來。

先理解搶占 (preemption) 這個概念

involuntarily suspending a running process is called preemption

奪取一個進程的 cpu 使用權的行為就叫做搶占。

根據是否可以支持搶占,多任務操作系統 (multitasking operating system) 分為 2 類

1、cooperative multitasking os

這種 os,進程會一直運行直到它自愿停下來。這種自愿停止運行自己的行為稱為 yielding。協作式多任務系統,一聽就知道這是一個烏托邦式的系統,只有當所有進程都很 nice 并樂意經常 yielding 時,系統才能正常工作。如果某個進程太傻或者太壞,系統很快就完蛋了。

2、preemptive multitasking os

這種 os,會有一個調度器 (scheduler,其實就是一段用于調度進程的程序),scheduler 決定進程何時停止運行以及新進程何時開始運行。當一個進程的 cpu 使用權被 scheduler 分配給另一個進程時,就稱前一個進程被搶占了。

你可以把 sheduler 想象成非常智能的交警,交警按照一定的交通規則、當前的交通狀況以及車輛的優先級 (救護車之類的),決定了哪些車可以行駛、哪些車要停下來等待。

很明顯,現階段,preemptive os 優于 cooperative os。所以 Linux 被設計成 preemptive。

搶占的核心操作包括 2 個步驟

1、從用戶態陷入到內核態 (trap kernel),3 個路徑:

a. 系統調用,本質是 soft interrupt,通常就是一條硬件指令 (x86 的 int 0x80)。

b. 硬件中斷,最典型的就是會周期性發生的 timer 中斷,或者其他各種外設中斷.

c. exception,例如 page fault、div 0。

37d7ec96-dd9b-11ed-bfe3-dac502259ad0.jpg

點擊查看大圖

2、陷入到內核態后,在合適的時機下,調用 sheduler 選出一個最重要的進程,如果被選中的不是當前正在運行的進程的話,就會執行 context switch 切換到新的進程。

根據搶占時機點的不同,搶占分為 2 種類型

1、user preemption

這里的 user 并不是指在 user-space 里進行搶占,而是指在返回 user-space 前進行搶占,具體的:

When returning to user-space from a system call

When returning to user-space from an interrupt handler

即從 system call 和 interrupt handler 返回到 user-space 前進行搶占,這時仍然是在 kernel-space 里,搶占是需要非常高的權限的事情,user-space 沒權利也不應該干這事。

2、kernel preemption

Linux 2.6 之前是不支持內核搶占的。這意味著當處于用戶空間的進程請求內核服務時,在該進程阻塞(進入睡眠)等待某事(通常是 I/O)或系統調用完成之前,不能調度其他進程。支持內核搶占意味著當一個進程在內核里運行時,另一個進程可以搶占第一個進程并被允許運行,即使第一個進程尚未完成其在內核里的工作。

37f98ca2-dd9b-11ed-bfe3-dac502259ad0.png

支持內核搶占 vs 不支持內核搶占

舉個例子:

3822e3f4-dd9b-11ed-bfe3-dac502259ad0.png

點擊查看大圖

在上圖中,進程 A 已經通過系統調用進入內核,也許是對設備或文件的 write() 調用。內核代表進程 A 執行時,具有更高優先級的進程 B 被中斷喚醒。內核搶占進程 A 并將 CPU 分配給進程 B,即使進程 A 既沒有阻塞也沒有完成其在內核里的工作。

內核搶占的時機:

When an interrupt handler exits, before returning to kernel-space

When kernel code becomes preemptible again

If a task in the kernel explicitly calls schedule()

If a task in the kernel blocks (which results in a call to schedule() )

為什么要引入內核搶占?

根本原因

trade-offs between latency and throughput

在系統延遲和吞吐量之間進行權衡。

并不是說內核搶占就是絕對的好,使用什么搶占機制最優是跟你的應用場景掛鉤的。如果不是為了滿足用戶,內核其實是完全不想進行進程切換的,因為每一次 context switch,都會有 overhead,這些 overhead 就是對 cpu 的浪費,意味著吞吐量的下降。

但是,如果你想要系統的響應性好一點,就得盡量多的允許搶占的發生,這是 Linux 作為一個通用操作系統所必須支持的。當你的系統做到隨時都可以發生搶占時,系統的響應性就會非常好。

為了讓用戶根據自己的需求進行配置,Linux 提供了 3 種 Preemption Model。

383a799c-dd9b-11ed-bfe3-dac502259ad0.png

CONFIG_PREEMPT_NONE=y:不允許內核搶占,吞吐量最大的 Model,一般用于 Server 系統。

3850f6f4-dd9b-11ed-bfe3-dac502259ad0.png

CONFIG_PREEMPT_VOLUNTARY=y:在一些耗時較長的內核代碼中主動調用cond_resched()讓出CPU,對吞吐量有輕微影響,但是系統響應會稍微快一些。

3873f58c-dd9b-11ed-bfe3-dac502259ad0.png

CONFIG_PREEMPT=y:除了處于持有 spinlock 時的 critical section,其他時候都允許內核搶占,響應速度進一步提升,吞吐量進一步下降,一般用于 Desktop / Embedded 系統。

38900ea2-dd9b-11ed-bfe3-dac502259ad0.png

另外,還有一個沒有合并進主線內核的 Model: CONFIG_PREEMPT_RT,這個模式幾乎將所有的 spinlock 都換成了 preemptable mutex,只剩下一些極其核心的地方仍然用禁止搶占的 spinlock,所以基本可以認為是隨時可被搶占。

38b0a8c4-dd9b-11ed-bfe3-dac502259ad0.png

搶占前的檢查

這里的檢查是同時針對所有的 preemption 的。如果你理解了前面的 4 種 preempiton model 的話,應該能感覺到其實是不用太嚴格區分 user / kernel preemption,所有搶占的作用和性質都一樣:降低 lantency,完全可以將它們一視同仁。

搶占的發生要同時滿足兩個條件

需要搶占;

能搶占;

1、是否需要搶占?

判斷是否需要搶占的依據是:thread_info 的成員 flags 是否設置了 TIF_NEED_RESCHED 標志位。

相關的 API:

set_tsk_need_resched() 用于設置該 flag。

tif_need_resched() 被用來判斷該 flag 是否置位。

resched_curr(struct rq *rq),標記當前 runqueue 需要搶占。

2、是否能搶占?

搶占發生的前提是要確保此次搶占是安全的 (preempt-safe)。什么才是 preempt-safe:不產生 race condition / deadlock。

值得注意的是,只有 kernel preemption 才有被禁止的可能,而 user preemption 總是被允許,因此這時馬上就要返回 user space 了,肯定是處于一個可搶占的狀態了。

在引入內核搶占機制的同時引入了為 thread_info 添加了新的成員:preempt_count ,用來保證搶占的安全性,獲取鎖時會增加 preempt_count,釋放鎖時則會減少。搶占前會檢查 preempt_count 是否為 0,為 0 才允許搶占。

相關的 API:

preempt_enable(),使能內核搶占,可嵌套調用。

preempt_disable(),關閉內核搶占,可嵌套調用。

preempt_count(),返回 preempt_count。

什么場景會設置需要搶占 (TIF_NEED_RESCHED = 1)

通過 grep resched_curr 可以找出大多數標記搶占的場景。

下面列舉的是幾個我比較關心的場景。

1、周期性的時鐘中斷

時鐘中斷處理函數會調用 scheduler_tick(),它通過調度類(scheduling class) 的 task_tick 方法 檢查進程的時間片是否耗盡,如果耗盡則標記需要搶占:

//kernel/sched/core.c
voidscheduler_tick(void)
{
[...]
curr->sched_class->task_tick(rq,curr,0);
[...]
}

Linux 的調度策略被封裝成調度類,例如 CFS、Real-Time。CFS 調度類的 task_tick() 如下:

//kernel/sched/fair.c
task_tick_fair()
->entity_tick()
->resched_curr(rq_of(cfs_rq));

2、喚醒進程的時候

當進程被喚醒的時候,如果優先級高于 CPU 上的當前進程,就會觸發搶占。相應的內核代碼中,try_to_wake_up() 最終通過 check_preempt_curr() 檢查是否標記需要搶占:

//kernel/sched/core.c
voidcheck_preempt_curr(structrq*rq,structtask_struct*p,intflags)
{
conststructsched_class*class;

if(p->sched_class==rq->curr->sched_class){
rq->curr->sched_class->check_preempt_curr(rq,p,flags);
}else{
for_each_class(class){
if(class==rq->curr->sched_class)
break;
if(class==p->sched_class){
resched_curr(rq);
break;
}
}
}
[...]
}

參數 "p" 指向被喚醒進程,"rq" 代表搶占的 CPU。如果 p 的調度類和 rq 當前的調度類相同,則調用 rq 當前的調度類的 check_preempt_curr() (例如 cfs 的 check_preempt_wakeup()) 來判斷是否要標記需要搶占。

如果 p 的調度類 > rq 當前的調度類,則用 resched_curr() 標記需要搶占,反之,則不標記。

3、新進程創建的時候

如果新進程的優先級高于 CPU 上的當前進程,會需要觸發搶占。相應的代碼是 sched_fork(),它再通過調度類的 task_fork() 標記需要搶占:

//kernel/sched/core.c
intsched_fork(unsignedlongclone_flags,structtask_struct*p)
{
[...]
if(p->sched_class->task_fork)
p->sched_class->task_fork(p);
[...]
}

//kernel/sched/fair.c
staticvoidtask_fork_fair(structtask_struct*p)
{
[...]
if(sysctl_sched_child_runs_first&&curr&&entity_before(curr,se)){
resched_curr(rq);
}
  [...]
}

4、進程修改 nice 值的時候

如果修改進程 nice 值導致優先級高于 CPU 上的當前進程,也要標記需要搶占,代碼見 set_user_nice()。

//kernel/sched/core.c
voidset_user_nice(structtask_struct*p,longnice)
{
[...]
//Ifthetaskincreaseditspriorityorisrunningandlowereditspriority,thenrescheduleitsCPU
if(delta0&&task_running(rq,p)))
resched_curr(rq);
}

還有很多場景,這里就不一一列舉了。

什么場景下要禁止內核搶占 (preempt_count > 0)

有幾種場景是明確需要關閉內核搶占的。

1、訪問 Per-CPU data structures 的時候

看下面這個例子:

structthis_needs_lockingtux[NR_CPUS];
tux[smp_processor_id()]=some_value;
/*taskispreemptedhere...*/
something=tux[smp_processor_id()];

如果搶占發生在注釋所在的那一行,當進程再次被調度時,smp_processor_id() 值可能已經發生變化了,這種場景下需要通過禁止內核搶占來做到 preempt safe。

2、訪問 CPU state 的時候

這個很好理解,你正在操作 CPU 相關的寄存器以進行 context switch 時,肯定是不能再允許搶占。

asmlinkage__visiblevoid__schedschedule(void)
{
structtask_struct*tsk=current;

sched_submit_work(tsk);
do{
//調度前禁止內核搶占
preempt_disable();
__schedule(false);
sched_preempt_enable_no_resched();
}while(need_resched());
sched_update_worker(tsk);
}


3、持有 spinlock 的時候

支持內核搶占,這意味著進程有可能與被搶占的進程在相同的 critical section 中運行。為防止這種情況,當持有自旋鎖時,要禁止內核搶占。

staticinlinevoid__raw_spin_lock(raw_spinlock_t*lock)
{
preempt_disable();
spin_acquire(&lock->dep_map,0,0,_RET_IP_);
LOCK_CONTENDED(lock,do_raw_spin_trylock,do_raw_spin_lock);
}

還有很多場景,這里就不一一列舉了。

真正執行搶占的地方

這部分是 platform 相關的,下面以 ARM64 Linux-5.4 為例,快速看下執行搶占的具體代碼。

執行 user preemption

系統調用和中斷返回用戶空間的時候:

它們都是在 ret_to_user() 里判斷是否執行用戶搶占。

//arch/arm64/kernel/entry.S
ret_to_user()//返回到用戶空間
work_pending()
do_notify_resume()
schedule()

//arch/arm64/kernel/signal.c
asmlinkagevoiddo_notify_resume(structpt_regs*regs,
unsignedlongthread_flags)
{
do{
[...]
//檢查是否要需要調度
if(thread_flags&_TIF_NEED_RESCHED){
local_daif_restore(DAIF_PROCCTX_NOIRQ);
schedule();
}else{
[...]
}while(thread_flags&_TIF_WORK_MASK);
}

執行 kernel preemption

中斷返回內核空間的時候:

//arch/arm64/kernel/entry.S
el1_irq
irq_handler
arm64_preempt_schedule_irq
preempt_schedule_irq
__schedule(true)

//kernel/sched/core.c
/*Thisistheentrypointtoschedule()fromkernelpreemption*/
asmlinkage__visiblevoid__schedpreempt_schedule_irq(void)
{
[...]
do{
preempt_disable();
local_irq_enable();
__schedule(true);
local_irq_disable();
sched_preempt_enable_no_resched();
}while(need_resched());

exception_exit(prev_state);
}

內核恢復為可搶占的時候:

前面列舉了集中關閉搶占的場景,當離開這些場景時,會恢復內核搶占。

例如 spinlock unlock 時:

staticinlinevoid__raw_spin_unlock(raw_spinlock_t*lock)
{
spin_release(&lock->dep_map,1,_RET_IP_);
do_raw_spin_unlock(lock);
preempt_enable();//使能搶占時,如果需要,就會執行搶占
}

//include/linux/preempt.h
#definepreempt_enable()
do{
barrier();
if(unlikely(preempt_count_dec_and_test()))
__preempt_schedule();
}while(0)

內核顯式地要求調度的時候:

內核里有大量的地方會顯式地要求進行調度,最常見的是:cond_resched() 和 sleep()類函數,它們最終都會調用到 __schedule()。

內核阻塞的時候:

例如 mutex,sem,waitqueue 獲取不到資源,或者是等待 IO。這種情況下進程會將自己的狀態從TASK_RUNNING 修改為 TASK_INTERRUPTIBLE,然后調用 schedule() 主動讓出 CPU 并等待喚醒。

//block/blk-core.c
staticstructrequest*get_request(structrequest_queue*q,intop,
intop_flags,structbio*bio,
gfp_tgfp_mask)
{
[...]
prepare_to_wait_exclusive(&rl->wait[is_sync],&wait,
TASK_UNINTERRUPTIBLE);
io_schedule();//會調用schedule();
[...]
}






審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 寄存器
    +關注

    關注

    31

    文章

    5357

    瀏覽量

    120681
  • LINUX內核
    +關注

    關注

    1

    文章

    316

    瀏覽量

    21675
  • CFS
    CFS
    +關注

    關注

    0

    文章

    7

    瀏覽量

    9058
  • 調度器
    +關注

    關注

    0

    文章

    98

    瀏覽量

    5261

原文標題:內核搶占,讓世界變得更美好 | Linux 內核

文章出處:【微信號:嵌入式悅翔園,微信公眾號:嵌入式悅翔園】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    文搞懂Linux內核鏈表

    hello 大家好,今天給大家介紹一下linux 內核鏈表的分析,在寫這篇文章前,筆者自己以前也只是停留在應用層面,沒有深究其中的細節,很多也是理解的不是很透徹。寫完此文后,發現對鏈表的理解更加深刻了。很多現代計算機的思想在
    發表于 11-14 09:17 ?1076次閱讀

    Linux內核搶占和用戶搶占的概念和區別

    本文詳解了Linux內核搶占實現機制。首先介紹了內核搶占和用戶搶占的概念和區別,接著分析了不可
    發表于 08-05 08:18

    詳解Linux內核搶占實現機制

    本文詳解了Linux內核搶占實現機制。首先介紹了內核搶占和用戶搶占的概念和區別,接著分析了不可
    發表于 08-06 06:16

    linux 5.4.31為例來介紹一下linux內核目錄結構

    ,它是Linux內核概述和編譯命令說明。readme的說明更加針對X86等通用的平臺,對于某些特殊的體系結構,可能有些特殊的地方。內核源碼很復雜,包含多級目錄,形成
    發表于 02-16 07:30

    記錄一下Linux設備模型學習歷程

    Linux設備模型學習筆記1KobjectKobject, Kset和KtypeUeventsysfs文件系統wowo這里寫的很好了:內核等看wowo寫的很有幫助我寫一下我的理解。記錄一下
    發表于 02-17 06:05

    介紹一下Linux內核編譯和更新的操作流程

    。由于官方沒有提高最新Linux內核版本的燒寫固件,為了解決些比較嚴重的bug,需要自行編譯Linux內核進行更新,接下來就介紹
    發表于 06-21 09:58

    如何自行編譯Linux內核的詳細資料概述

    ,你能很輕易地安裝實驗版本的或者指定版本的內核(比如針對音頻產品的實時內核)。 考慮一下,既然升級內核如此容易,為什么你不愿意自行編譯
    的頭像 發表于 05-27 10:12 ?3233次閱讀
    如何自行編譯<b class='flag-5'>一</b>個<b class='flag-5'>Linux</b><b class='flag-5'>內核</b>的詳細資料<b class='flag-5'>概述</b>

    Linux內核搶占相關的基礎知識

    今天要分享的是搶占相關的基礎知識。本文以內核搶占為引子,概述一下 Linux
    的頭像 發表于 11-09 16:48 ?2055次閱讀

    STM32MP157 Linux系統移植開發篇7:Linux內核目錄結構詳解

    ,它是Linux內核概述和編譯命令說明。readme的說明更加針對X86等通用的平臺,對于某些特殊的體系結構,可能有些特殊的地方。內核源碼很復雜,包含多級目錄,形成
    發表于 12-17 18:29 ?10次下載
    STM32MP157 <b class='flag-5'>Linux</b>系統移植開發篇7:<b class='flag-5'>Linux</b><b class='flag-5'>內核</b>目錄結構詳解

    小編科普一下Linux內核中常用的C語言技巧

    Linux內核采用的是GCC編譯器,GCC編譯器除了支持ANSI C,還支持GNU C。在Linux內核中,許多地方都使用了GNU C語言的擴展特性,如typeof、__attribu
    的頭像 發表于 02-08 11:51 ?706次閱讀

    介紹一下linux內核比較優秀的調試方式KGDB

    printf相信學過C語言的同志再熟悉不過了,然而在linux內核開發中有種非常簡潔的日志輸出函數叫-printk。
    的頭像 發表于 03-08 13:45 ?1867次閱讀

    文搞懂Linux系統內核的重要性

    今天我要跟大家分享一下Linux內核的重要性。內核就像Linux系統運行的大心臟,對系統的運行起到了至關重要的作用。那么
    的頭像 發表于 03-24 15:16 ?943次閱讀
    <b class='flag-5'>一</b>文搞懂<b class='flag-5'>Linux</b>系統<b class='flag-5'>內核</b>的重要性

    介紹一下Linux內核中的各種鎖

    Linux內核中有許多不同類型的鎖,它們都可以用來保護關鍵資源,以避免多個線程或進程之間發生競爭條件,從而保護系統的穩定性和可靠性。
    的頭像 發表于 05-16 14:13 ?5258次閱讀

    操作系統中搶占式和非搶占內核的區別

    操作系統般分為搶占內核和非搶占內核,通常RTOS都是搶占
    的頭像 發表于 05-29 10:47 ?1970次閱讀
    操作系統中<b class='flag-5'>搶占</b>式和非<b class='flag-5'>搶占</b>式<b class='flag-5'>內核</b>的區別

    Linux系統內核概述

    Linux 內核Linux 操作系統的主要組件,也是計算機硬件與其進程之間的核心接口。它負責兩者之間的通信,還要盡可能高效地管理資源。
    發表于 06-09 09:29 ?628次閱讀
    <b class='flag-5'>Linux</b>系統<b class='flag-5'>內核</b><b class='flag-5'>概述</b>
    主站蜘蛛池模板: 韩国三级视频网站| 丁香婷婷社区| 日韩一级在线观看| 在线观看免费xx高清视频| 日韩成a人片在线观看日本| 四虎一影院区永久精品| 俄罗斯久久| 国产精品久久久久久免费播放 | 久久新地址| 欧美一级特黄视频| 亚洲色图欧美色| 香蕉视频在线免费播放| 在线亚洲色图| 五色网| www.一区二区三区| 69xxxxtube日本免费| 欲色啪| 亚洲码欧美码一区二区三区| 成人综合激情| 夜夜超b天天| 天天舔天天干天天操| 亚洲69视频| 免费福利午夜影视网| 1024国产你懂的日韩| 色综合视频一区二区三区| 32pao强力打造免费高速高清| 日本免费一区视频| 午夜久久免影院欧洲| 免费一日本一级裸片在线观看| 女人精aaaa片一级毛片女女| 日本福利网址| 欧美特黄一级视频| 岛国片欧美一级毛片| 国产成人精品曰本亚洲77美色| 午夜精品视频在线| 天天插天天干天天操| aa小视频| 91大神亚洲影视在线| 亚洲六月婷婷| 毛片免费网站| 天天干夜夜夜操|