chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

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

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

3天內(nèi)不再提示

棧是什么?棧有什么作用?

xCb1_yikoulinux ? 來源:一口Linux ? 作者:一口Linux ? 2022-06-17 11:19 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

棧是什么?棧有什么作用?

首先,棧 (stack) 是一種串列形式的數(shù)據(jù)結(jié)構(gòu)。這種數(shù)據(jù)結(jié)構(gòu)的特點是后入先出(LIFO, Last In First Out),數(shù)據(jù)只能在串列的一端 (稱為:棧頂 top) 進行推入(push) 和彈出(pop) 操作。根據(jù)棧的特點,很容易的想到可以利用數(shù)組,來實現(xiàn)這種數(shù)據(jù)結(jié)構(gòu)。但是本文要討論的并不是軟件層面的棧,而是硬件層面的棧。

e09106d8-edeb-11ec-ba43-dac502259ad0.jpg

大多數(shù)的處理器架構(gòu),都有實現(xiàn)硬件棧。有專門的棧指針寄存器,以及特定的硬件指令來完成 入棧/出棧 的操作。例如在 ARM 架構(gòu)上,R13 (SP) 指針是堆棧指針寄存器,而 PUSH 是用于壓棧的匯編指令,POP 則是出棧的匯編指令。

【擴展閱讀】:ARM 寄存器簡介

ARM 處理器擁有 37 個寄存器。這些寄存器按部分重疊組方式加以排列。每個處理器模式都有一個不同的寄存器組。編組的寄存器為處理處理器異常和特權(quán)操作提供了快速的上下文切換。

提供了下列寄存器:
- 三十個 32 位通用寄存器:
- 存在十五個通用寄存器,它們分別是 r0-r12、sp、lr
- sp (r13) 是堆棧指針。C/C++ 編譯器始終將 sp 用作堆棧指針
- lr (r14) 用于存儲調(diào)用子例程時的返回地址。如果返回地址存儲在堆棧上,則可將 lr 用作通用寄存器
- 程序計數(shù)器 (pc):指令寄存器
- 應用程序狀態(tài)寄存器 (APSR):存放算術(shù)邏輯單元 (ALU) 狀態(tài)標記的副本
- 當前程序狀態(tài)寄存器 (CPSR):存放 APSR 標記,當前處理器模式,中斷禁用標記等
- 保存的程序狀態(tài)寄存器 (SPSR):當發(fā)生異常時,使用 SPSR 來存儲 CPSR

上面是棧的原理和實現(xiàn),下面我們來看看棧有什么作用。棧作用可以從兩個方面體現(xiàn):函數(shù)調(diào)用多任務支持。

一、函數(shù)調(diào)用

我們知道一個函數(shù)調(diào)用有以下三個基本過程:
- 調(diào)用參數(shù)的傳入
- 局部變量的空間管理
- 函數(shù)返回

函數(shù)的調(diào)用必須是高效的,而數(shù)據(jù)存放在CPU通用寄存器或者RAM 內(nèi)存中無疑是最好的選擇。以傳遞調(diào)用參數(shù)為例,我們可以選擇使用 CPU通用寄存器 來存放參數(shù)。但是通用寄存器的數(shù)目都是有限的,當出現(xiàn)函數(shù)嵌套調(diào)用時,子函數(shù)再次使用原有的通用寄存器必然會導致沖突。因此如果想用它來傳遞參數(shù),那在調(diào)用子函數(shù)前,就必須先保存原有寄存器的值,然后當子函數(shù)退出的時候再恢復原有寄存器的值

函數(shù)的調(diào)用參數(shù)數(shù)目一般都相對少,因此通用寄存器是可以滿足一定需求的。但是局部變量的數(shù)目和占用空間都是比較大的,再依賴有限的通用寄存器未免強人所難,因此我們可以采用某些 RAM 內(nèi)存區(qū)域來存儲局部變量。但是存儲在哪里合適?既不能讓函數(shù)嵌套調(diào)用的時候有沖突,又要注重效率。

這種情況下,棧無疑提供很好的解決辦法。一、對于通用寄存器傳參的沖突,我們可以再調(diào)用子函數(shù)前,將通用寄存器臨時壓入棧中;在子函數(shù)調(diào)用完畢后,在將已保存的寄存器再彈出恢復回來。二、而局部變量的空間申請,也只需要向下移動下棧頂指針;將棧頂指針向回移動,即可就可完成局部變量的空間釋放;三、對于函數(shù)的返回,也只需要在調(diào)用子函數(shù)前,將返回地址壓入棧中,待子函數(shù)調(diào)用結(jié)束后,將函數(shù)返回地址彈出給 PC 指針,即完成了函數(shù)調(diào)用的返回;

于是上述函數(shù)調(diào)用的三個基本過程,就演變記錄一個棧指針的過程。每次函數(shù)調(diào)用的時候,都配套一個棧指針。即使循環(huán)嵌套調(diào)用函數(shù),只要對應函數(shù)棧指針是不同的,也不會出現(xiàn)沖突。

e0a3b634-edeb-11ec-ba43-dac502259ad0.jpg

【擴展閱讀】:函數(shù)棧幀 (Stack Frame)

函數(shù)調(diào)用經(jīng)常是嵌套的,在同一時刻,棧中會有多個函數(shù)的信息。每個未完成運行的函數(shù)占用一個獨立的連續(xù)區(qū)域,稱作棧幀(Stack Frame)。棧幀存放著函數(shù)參數(shù),局部變量及恢復前一棧幀所需要的數(shù)據(jù)等,函數(shù)調(diào)用時入棧的順序為:

實參N~1 → 主調(diào)函數(shù)返回地址 → 主調(diào)函數(shù)幀基指針EBP → 被調(diào)函數(shù)局部變量1~N

棧幀的邊界由棧幀基地址指針 EBP棧指針 ESP界定,EBP 指向當前棧幀底部(高地址),在當前棧幀內(nèi)位置固定;ESP指向當前棧幀頂部(低地址),當程序執(zhí)行時ESP會隨著數(shù)據(jù)的入棧和出棧而移動。因此函數(shù)中對大部分數(shù)據(jù)的訪問都基于EBP進行。函數(shù)調(diào)用棧的典型內(nèi)存布局如下圖所示:

e0b3ac9c-edeb-11ec-ba43-dac502259ad0.jpg

二、多任務支持

然而棧的意義還不只是函數(shù)調(diào)用,有了它的存在,才能構(gòu)建出操作系統(tǒng)的多任務模式。我們以 main 函數(shù)調(diào)用為例,main 函數(shù)包含一個無限循環(huán)體,循環(huán)體中先調(diào)用 A 函數(shù),再調(diào)用 B 函數(shù)。

func B():
 return;

func A():
 B();

func main():
 while (1)
  A();

試想在單處理器情況下,程序?qū)⒂肋h停留在此 main 函數(shù)中。即使有另外一個任務在等待狀態(tài),程序是沒法從此 main 函數(shù)里面跳轉(zhuǎn)到另一個任務。因為如果是函數(shù)調(diào)用關(guān)系,本質(zhì)上還是屬于 main 函數(shù)的任務中,不能算多任務切換。此刻的 main 函數(shù)任務本身其實和它的棧綁定在了一起,無論如何嵌套調(diào)用函數(shù),棧指針都在本棧范圍內(nèi)移動。

由此可以看出一個任務可以利用以下信息來表征:
1. main 函數(shù)體代碼
2. main 函數(shù)棧指針
3. 當前 CPU 寄存器信息

假如我們可以保存以上信息,則完全可以強制讓出 CPU 去處理其他任務。只要將來想繼續(xù)執(zhí)行此 main 任務的時候,把上面的信息恢復回去即可。有了這樣的先決條件,多任務就有了存在的基礎(chǔ),也可以看出棧存在的另一個意義。在多任務模式下,當調(diào)度程序認為有必要進行任務切換的話,只需保存任務的信息(即上面說的三個內(nèi)容)?;謴土硪粋€任務的狀態(tài),然后跳轉(zhuǎn)到上次運行的位置,就可以恢復運行了。

可見每個任務都有自己的??臻g,正是有了獨立的??臻g,為了代碼重用,不同的任務甚至可以混用任務的函數(shù)體本身,例如可以一個main函數(shù)有兩個任務實例。至此之后的操作系統(tǒng)的框架也形成了,譬如任務在調(diào)用 sleep() 等待的時候,可以主動讓出 CPU 給別的任務使用,或者分時操作系統(tǒng)任務在時間片用完是也會被迫的讓出 CPU。不論是哪種方法,只要想辦法切換任務的上下文空間,切換棧即可。

e0c224e8-edeb-11ec-ba43-dac502259ad0.jpg

【擴展閱讀】:任務、線程、進程 三者關(guān)系

任務是一個抽象的概念,即指軟件完成的一個活動;而線程則是完成任務所需的動作;進程則指的是完成此動作所需資源的統(tǒng)稱;關(guān)于三者的關(guān)系,有一個形象的比喻:
- 任務 = 送貨
- 線程 = 開送貨車
- 系統(tǒng)調(diào)度 = 決定合適開哪部送貨車
- 進程 = 道路 + 加油站 + 送貨車 + 修車廠


Linux 中有幾種棧?各種棧的內(nèi)存位置?

介紹完棧的工作原理和用途作用后,我們回歸到 Linux 內(nèi)核上來。內(nèi)核將棧分成四種:

  • 進程棧

  • 線程棧

  • 內(nèi)核棧

  • 中斷棧

一、進程棧

進程棧是屬于用戶態(tài)棧,和進程虛擬地址空間 (Virtual Address Space)密切相關(guān)。那我們先了解下什么是虛擬地址空間:在 32 位機器下,虛擬地址空間大小為 4G。這些虛擬地址通過頁表 (Page Table) 映射到物理內(nèi)存,頁表由操作系統(tǒng)維護,并被處理器的內(nèi)存管理單元 (MMU) 硬件引用。每個進程都擁有一套屬于它自己的頁表,因此對于每個進程而言都好像獨享了整個虛擬地址空間。

Linux 內(nèi)核將這 4G 字節(jié)的空間分為兩部分,將最高的 1G 字節(jié)(0xC0000000-0xFFFFFFFF)供內(nèi)核使用,稱為內(nèi)核空間。而將較低的3G字節(jié)(0x00000000-0xBFFFFFFF)供各個進程使用,稱為用戶空間。每個進程可以通過系統(tǒng)調(diào)用陷入內(nèi)核態(tài),因此內(nèi)核空間是由所有進程共享的。雖然說內(nèi)核和用戶態(tài)進程占用了這么大地址空間,但是并不意味它們使用了這么多物理內(nèi)存,僅表示它可以支配這么大的地址空間。它們是根據(jù)需要,將物理內(nèi)存映射到虛擬地址空間中使用。

e0ccb96c-edeb-11ec-ba43-dac502259ad0.jpg

Linux 對進程地址空間有個標準布局,地址空間中由各個不同的內(nèi)存段組成 (Memory Segment),主要的內(nèi)存段如下:
- 程序段 (Text Segment):可執(zhí)行文件代碼的內(nèi)存映射
- 數(shù)據(jù)段 (Data Segment):可執(zhí)行文件的已初始化全局變量的內(nèi)存映射
- BSS段 (BSS Segment):未初始化的全局變量或者靜態(tài)變量(用零頁初始化)
- 堆區(qū) (Heap) : 存儲動態(tài)內(nèi)存分配,匿名的內(nèi)存映射
- 棧區(qū) (Stack) : 進程用戶空間棧,由編譯器自動分配釋放,存放函數(shù)的參數(shù)值、局部變量的值等
- 映射段(Memory Mapping Segment):任何內(nèi)存映射文件

e0da6f08-edeb-11ec-ba43-dac502259ad0.jpg

而上面進程虛擬地址空間中的棧區(qū),正指的是我們所說的進程棧。進程棧的初始化大小是由編譯器和鏈接器計算出來的,但是棧的實時大小并不是固定的,Linux 內(nèi)核會根據(jù)入棧情況對棧區(qū)進行動態(tài)增長(其實也就是添加新的頁表)。但是并不是說棧區(qū)可以無限增長,它也有最大限制RLIMIT_STACK(一般為 8M),我們可以通過ulimit來查看或更改RLIMIT_STACK的值。

【擴展閱讀】:如何確認進程棧的大小

我們要知道棧的大小,那必須得知道棧的起始地址和結(jié)束地址。棧起始地址獲取很簡單,只需要嵌入?yún)R編指令獲取棧指針 esp 地址即可。棧結(jié)束地址的獲取有點麻煩,我們需要先利用遞歸函數(shù)把棧搞溢出了,然后再 GDB 中把棧溢出的時候把棧指針 esp 打印出來即可。代碼如下:

/* file name: stacksize.c */

void *orig_stack_pointer;

void blow_stack() {
  blow_stack();
}

int main() {
  __asm__("movl %esp, orig_stack_pointer");

  blow_stack();
  return 0;
}
$ g++ -g stacksize.c -o ./stacksize
$ gdb ./stacksize
(gdb) r
Starting program: /home/home/misc-code/setrlimit

Program received signal SIGSEGV, Segmentation fault.
blow_stack () at setrlimit.c:4
4    blow_stack();
(gdb) print (void *)$esp
$1 = (void *) 0xffffffffff7ff000
(gdb) print (void *)orig_stack_pointer
$2 = (void *) 0xffffc800
(gdb) print 0xffffc800-0xff7ff000
$3 = 8378368  // Current Process Stack Size is 8M

上面對進程的地址空間有個比較全局的介紹,那我們看下 Linux 內(nèi)核中是怎么體現(xiàn)上面內(nèi)存布局的。內(nèi)核使用內(nèi)存描述符來表示進程的地址空間,該描述符表示著進程所有地址空間的信息。內(nèi)存描述符由 mm_struct 結(jié)構(gòu)體表示,下面給出內(nèi)存描述符結(jié)構(gòu)中各個域的描述,請大家結(jié)合前面的 進程內(nèi)存段布局 圖一起看:

struct mm_struct {
  struct vm_area_struct *mmap;      /* 內(nèi)存區(qū)域鏈表 */
  struct rb_root mm_rb;         /* VMA 形成的紅黑樹 */
  ...
  struct list_head mmlist;        /* 所有 mm_struct 形成的鏈表 */
  ...
  unsigned long total_vm;        /* 全部頁面數(shù)目 */
  unsigned long locked_vm;        /* 上鎖的頁面數(shù)據(jù) */
  unsigned long pinned_vm;        /* Refcount permanently increased */
  unsigned long shared_vm;        /* 共享頁面數(shù)目 Shared pages (files) */
  unsigned long exec_vm;         /* 可執(zhí)行頁面數(shù)目 VM_EXEC & ~VM_WRITE */
  unsigned long stack_vm;        /* 棧區(qū)頁面數(shù)目 VM_GROWSUP/DOWN */
  unsigned long def_flags;
  unsigned long start_code, end_code, start_data, end_data;  /* 代碼段、數(shù)據(jù)段 起始地址和結(jié)束地址 */
  unsigned long start_brk, brk, start_stack;          /* 棧區(qū) 的起始地址,堆區(qū) 起始地址和結(jié)束地址 */
  unsigned long arg_start, arg_end, env_start, env_end;    /* 命令行參數(shù) 和 環(huán)境變量的 起始地址和結(jié)束地址 */
  ...
  /* Architecture-specific MM context */
  mm_context_t context;         /* 體系結(jié)構(gòu)特殊數(shù)據(jù) */

  /* Must use atomic bitops to access the bits */
  unsigned long flags;          /* 狀態(tài)標志位 */
  ...
  /* Coredumping and NUMA and HugePage 相關(guān)結(jié)構(gòu)體 */
};

e0f4936a-edeb-11ec-ba43-dac502259ad0.jpg

【擴展閱讀】:進程棧的動態(tài)增長實現(xiàn)

進程在運行的過程中,通過不斷向棧區(qū)壓入數(shù)據(jù),當超出棧區(qū)容量時,就會耗盡棧所對應的內(nèi)存區(qū)域,這將觸發(fā)一個缺頁異常 (page fault)。通過異常陷入內(nèi)核態(tài)后,異常會被內(nèi)核的expand_stack()函數(shù)處理,進而調(diào)用acct_stack_growth()來檢查是否還有合適的地方用于棧的增長。

如果棧的大小低于RLIMIT_STACK(通常為8MB),那么一般情況下棧會被加長,程序繼續(xù)執(zhí)行,感覺不到發(fā)生了什么事情,這是一種將棧擴展到所需大小的常規(guī)機制。然而,如果達到了最大??臻g的大小,就會發(fā)生棧溢出(stack overflow),進程將會收到內(nèi)核發(fā)出的段錯誤(segmentation fault)信號

動態(tài)棧增長是唯一一種訪問未映射內(nèi)存區(qū)域而被允許的情形,其他任何對未映射內(nèi)存區(qū)域的訪問都會觸發(fā)頁錯誤,從而導致段錯誤。一些被映射的區(qū)域是只讀的,因此企圖寫這些區(qū)域也會導致段錯誤。

二、線程棧

從 Linux 內(nèi)核的角度來說,其實它并沒有線程的概念。Linux 把所有線程都當做進程來實現(xiàn),它將線程和進程不加區(qū)分的統(tǒng)一到了 task_struct 中。線程僅僅被視為一個與其他進程共享某些資源的進程,而是否共享地址空間幾乎是進程和 Linux 中所謂線程的唯一區(qū)別。線程創(chuàng)建的時候,加上了 CLONE_VM 標記,這樣線程的內(nèi)存描述符 將直接指向 父進程的內(nèi)存描述符。

 if (clone_flags & CLONE_VM) {
  /*
   * current 是父進程而 tsk 在 fork() 執(zhí)行期間是共享子進程
   */
  atomic_inc(¤t->mm->mm_users);
  tsk->mm = current->mm;
 }

雖然線程的地址空間和進程一樣,但是對待其地址空間的 stack 還是有些區(qū)別的。對于 Linux 進程或者說主線程,其 stack 是在 fork 的時候生成的,實際上就是復制了父親的 stack 空間地址,然后寫時拷貝 (cow) 以及動態(tài)增長。然而對于主線程生成的子線程而言,其 stack 將不再是這樣的了,而是事先固定下來的,使用 mmap 系統(tǒng)調(diào)用,它不帶有 VM_STACK_FLAGS 標記。這個可以從 glibc 的nptl/allocatestack.c中的allocate_stack()函數(shù)中看到:

mem = mmap (NULL, size, prot,
      MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);

由于線程的mm->start_stack棧地址和所屬進程相同,所以線程棧的起始地址并沒有存放在task_struct中,應該是使用pthread_attr_t中的stackaddr來初始化task_struct->thread->sp(sp 指向struct pt_regs對象,該結(jié)構(gòu)體用于保存用戶進程或者線程的寄存器現(xiàn)場)。這些都不重要,重要的是,線程棧不能動態(tài)增長,一旦用盡就沒了,這是和生成進程的 fork 不同的地方。由于線程棧是從進程的地址空間中 map 出來的一塊內(nèi)存區(qū)域,原則上是線程私有的。但是同一個進程的所有線程生成的時候淺拷貝生成者的 task_struct 的很多字段,其中包括所有的vma,如果愿意,其它線程也還是可以訪問到的,于是一定要注意。

三、進程內(nèi)核棧

在每一個進程的生命周期中,必然會通過到系統(tǒng)調(diào)用陷入內(nèi)核。在執(zhí)行系統(tǒng)調(diào)用陷入內(nèi)核之后,這些內(nèi)核代碼所使用的棧并不是原先進程用戶空間中的棧,而是一個單獨內(nèi)核空間的棧,這個稱作進程內(nèi)核棧。進程內(nèi)核棧在進程創(chuàng)建的時候,通過 slab 分配器從thread_info_cache緩存池中分配出來,其大小為THREAD_SIZE,一般來說是一個頁大小 4K;

union thread_union {                  
    struct thread_info thread_info;        
    unsigned long stack[THREAD_SIZE/sizeof(long)];
};                          

thread_union進程內(nèi)核棧 和task_struct進程描述符有著緊密的聯(lián)系。由于內(nèi)核經(jīng)常要訪問task_struct,高效獲取當前進程的描述符是一件非常重要的事情。因此內(nèi)核將進程內(nèi)核棧的頭部一段空間,用于存放thread_info結(jié)構(gòu)體,而此結(jié)構(gòu)體中則記錄了對應進程的描述符,兩者關(guān)系如下圖(對應內(nèi)核函數(shù)為dup_task_struct()):

e0fc8ed0-edeb-11ec-ba43-dac502259ad0.jpg

有了上述關(guān)聯(lián)結(jié)構(gòu)后,內(nèi)核可以先獲取到棧頂指針 esp,然后通過 esp 來獲取thread_info。這里有一個小技巧,直接將 esp 的地址與上~(THREAD_SIZE - 1)后即可直接獲得thread_info的地址。由于thread_union結(jié)構(gòu)體是從thread_info_cache的 Slab 緩存池中申請出來的,而thread_info_cachekmem_cache_create創(chuàng)建的時候,保證了地址是THREAD_SIZE對齊的。因此只需要對棧指針進行 THREAD_SIZE 對齊,即可獲得thread_union的地址,也就獲得了thread_union的地址。成功獲取到thread_info后,直接取出它的 task 成員就成功得到了task_struct。其實上面這段描述,也就是current宏的實現(xiàn)方法:

register unsigned long current_stack_pointer asm ("sp");

static inline struct thread_info *current_thread_info(void) 
{                              
    return (struct thread_info *)            
        (current_stack_pointer & ~(THREAD_SIZE - 1));
}                              

#define get_current() (current_thread_info()->task)

#define current get_current()           

四、中斷棧

進程陷入內(nèi)核態(tài)的時候,需要內(nèi)核棧來支持內(nèi)核函數(shù)調(diào)用。中斷也是如此,當系統(tǒng)收到中斷事件后,進行中斷處理的時候,也需要中斷棧來支持函數(shù)調(diào)用。由于系統(tǒng)中斷的時候,系統(tǒng)當然是處于內(nèi)核態(tài)的,所以中斷棧是可以和內(nèi)核棧共享的。但是具體是否共享,這和具體處理架構(gòu)密切相關(guān)。

X86 上中斷棧就是獨立于內(nèi)核棧的;獨立的中斷棧所在內(nèi)存空間的分配發(fā)生在arch/x86/kernel/irq_32.cirq_ctx_init()函數(shù)中(如果是多處理器系統(tǒng),那么每個處理器都會有一個獨立的中斷棧),函數(shù)使用__alloc_pages在低端內(nèi)存區(qū)分配2個物理頁面,也就是8KB大小的空間。有趣的是,這個函數(shù)還會為softirq分配一個同樣大小的獨立堆棧。如此說來,softirq將不會在hardirq的中斷棧上執(zhí)行,而是在自己的上下文中執(zhí)行。

e10efa66-edeb-11ec-ba43-dac502259ad0.jpg

而 ARM 上中斷棧和內(nèi)核棧則是共享的;中斷棧和內(nèi)核棧共享有一個負面因素,如果中斷發(fā)生嵌套,可能會造成棧溢出,從而可能會破壞到內(nèi)核棧的一些重要數(shù)據(jù),所以??臻g有時候難免會捉襟見肘。


Linux 為什么需要區(qū)分這些棧?

為什么需要區(qū)分這些棧,其實都是設計上的問題。這里就我看到過的一些觀點進行匯總,供大家討論:

  1. 為什么需要單獨的進程內(nèi)核棧?

  • 所有進程運行的時候,都可能通過系統(tǒng)調(diào)用陷入內(nèi)核態(tài)繼續(xù)執(zhí)行。假設第一個進程 A 陷入內(nèi)核態(tài)執(zhí)行的時候,需要等待讀取網(wǎng)卡的數(shù)據(jù),主動調(diào)用schedule()讓出 CPU;此時調(diào)度器喚醒了另一個進程 B,碰巧進程 B 也需要系統(tǒng)調(diào)用進入內(nèi)核態(tài)。那問題就來了,如果內(nèi)核棧只有一個,那進程 B 進入內(nèi)核態(tài)的時候產(chǎn)生的壓棧操作,必然會破壞掉進程 A 已有的內(nèi)核棧數(shù)據(jù);一但進程 A 的內(nèi)核棧數(shù)據(jù)被破壞,很可能導致進程 A 的內(nèi)核態(tài)無法正確返回到對應的用戶態(tài)了;

  • 為什么需要單獨的線程棧?

  • 此時 A1 的棧指針 esp 如果為初始值 0x7ffc80000000,則線程 A1 一但出現(xiàn)函數(shù)調(diào)用,必然會破壞父進程 A 已入棧的數(shù)據(jù)。

  • 如果此時線程 A1 的棧指針和父進程最后更新的值一致,esp 為 0x7ffc8000FF00,那線程 A1 進行一些函數(shù)調(diào)用后,棧指針 esp 增加到 0x7ffc8000FFFF,然后線程 A1 休眠;調(diào)度器再次換成父進程 A 執(zhí)行,那這個時候父進程的棧指針是應該為 0x7ffc8000FF00 還是 0x7ffc8000FFFF 呢?無論棧指針被設置到哪個值,都會有問題不是嗎?

  • Linux 調(diào)度程序中并沒有區(qū)分線程和進程,當調(diào)度程序需要喚醒”進程”的時候,必然需要恢復進程的上下文環(huán)境,也就是進程棧;但是線程和父進程完全共享一份地址空間,如果棧也用同一個那就會遇到以下問題。假如進程的棧指針初始值為 0x7ffc80000000;父進程 A 先執(zhí)行,調(diào)用了一些函數(shù)后棧指針 esp 為 0x7ffc8000FF00,此時父進程主動休眠了;接著調(diào)度器喚醒子線程 A1:

  • 進程和線程是否共享一個內(nèi)核棧?

  • No,線程和進程創(chuàng)建的時候都調(diào)用dup_task_struct來創(chuàng)建 task 相關(guān)結(jié)構(gòu)體,而內(nèi)核棧也是在此函數(shù)中alloc_thread_info_node出來的。因此雖然線程和進程共享一個地址空間mm_struct,但是并不共享一個內(nèi)核棧。

  • 為什么需要單獨中斷棧?

  • 這個問題其實不對,ARM 架構(gòu)就沒有獨立的中斷棧。

大家還有什么觀點,可以在留言下來 :-D

審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • Linux
    +關(guān)注

    關(guān)注

    88

    文章

    11576

    瀏覽量

    216636
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4400

    瀏覽量

    66361
  • 線程
    +關(guān)注

    關(guān)注

    0

    文章

    508

    瀏覽量

    20653

原文標題:Linux 中的各種棧:進程棧 線程棧 內(nèi)核棧 中斷棧

文章出處:【微信號:yikoulinux,微信公眾號:一口Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    自動駕駛中常提的“全”是個啥?必要“全”嗎?

    [首發(fā)于智駕最前沿微信公眾號]隨著自動駕駛技術(shù)落地,越來越多車企公布了自己的自動駕駛方案,在很多車企的宣傳中,會使用“全自研”的說法來證明自己的實力。所謂“全”,字面意思是全套技術(shù)的自主開發(fā)
    的頭像 發(fā)表于 08-27 09:43 ?498次閱讀
    自動駕駛中常提的“全<b class='flag-5'>棧</b>”是個啥?<b class='flag-5'>有</b>必要“全<b class='flag-5'>棧</b>”嗎?

    黑芝麻智能AI全機器人計算平臺榮膺國際大獎

    黑芝麻智能AI全機器人計算平臺榮膺新加坡年度"GO! Technology Utilisation Winner",作為面向新一代機器人實時AI推理打造的全計算平臺,該方案已成功部署于清潔、巡檢及移動機器人平臺。
    的頭像 發(fā)表于 08-07 17:35 ?1518次閱讀

    AI應用創(chuàng)新與全技術(shù)融合分論壇即將召開

    2025開放原子開源生態(tài)大會即將啟幕,其中 “AI應用創(chuàng)新與全技術(shù)融合分論壇”將于 7月24日重磅亮相。論壇聚焦人工智能技術(shù)與開源生態(tài)的深度融合,邀請各領(lǐng)域用戶、技術(shù)專家、開發(fā)者分享AI應用創(chuàng)新實踐,旨在探索AI技術(shù)從底層算力到AI應用場景的全創(chuàng)新路徑,為行業(yè)帶來一場
    的頭像 發(fā)表于 07-23 09:54 ?541次閱讀

    51+單片機TCP-IP+協(xié)議ZLIP源碼

    和 KeilC51 編譯器其自身的特點:存儲類型、函數(shù)指針、重入函數(shù)等,ZLIP 就是針對這些特點設計的 TCP/IP 協(xié)議。
    發(fā)表于 04-22 15:11

    深入淺出解析低功耗藍牙協(xié)議

    Bluetooth LE協(xié)議為什么要分層?怎么理解Bluetooth LE“連接”?如果Bluetooth LE協(xié)議只有ATT層沒有GATT層會發(fā)生什么? 一、協(xié)議框架 一般而言,我們把某個
    的頭像 發(fā)表于 04-09 14:49 ?825次閱讀
    深入淺出解析低功耗藍牙協(xié)議<b class='flag-5'>棧</b>

    三種藍牙架構(gòu)實現(xiàn)方案(藍牙協(xié)議方案)

    藍牙架構(gòu)實現(xiàn)方案哪幾種?我們一般把整個藍牙實現(xiàn)方案叫做藍牙協(xié)議,因此這個問題也可以這么闡述:藍牙協(xié)議哪些具體的架構(gòu)方案?在藍牙協(xié)議
    的頭像 發(fā)表于 04-08 15:35 ?989次閱讀
    三種藍牙架構(gòu)實現(xiàn)方案(藍牙協(xié)議<b class='flag-5'>棧</b>方案)

    商湯科技領(lǐng)跑2024年中國GenAI技術(shù)市場

    創(chuàng)新實力強、應用落地廣,GenAI(生成式AI)技術(shù)領(lǐng)域,商湯科技位居國內(nèi)榜首!
    的頭像 發(fā)表于 12-27 16:07 ?951次閱讀

    曙光云開啟全智能時代

    近日,“全可信 云中生智”曙光云戰(zhàn)略發(fā)布會召開。曙光云從首創(chuàng)“城市云”進化到實現(xiàn)“全智能云”,打造“云智、云安、云算、云數(shù)”四位一體能力體系,深度賦能千行百業(yè)數(shù)智化轉(zhuǎn)型升級。
    的頭像 發(fā)表于 12-19 15:11 ?823次閱讀

    基于飛騰平臺的國內(nèi)首家全信創(chuàng)安檢管理系統(tǒng)投入試運行

    基于飛騰平臺的國內(nèi)首家全信創(chuàng)安檢管理系統(tǒng)在哈爾濱太平國際機場初步建設完畢,進入試運行驗證階段,測試通道已面向旅客開放,期間運行穩(wěn)定,標志著全國首個全信創(chuàng)安檢管理系統(tǒng)已初具雛形。
    的頭像 發(fā)表于 12-04 16:23 ?1044次閱讀

    λ-IO:存儲計算下的IO設計

    動機和背景? ? 存儲計算存儲資源的充分利用。IO是管理存儲器的的基本組件,包括設備驅(qū)動、塊接口層、文件系統(tǒng),目前一些用戶空間IO庫(如SPDK)有效降低了延遲,但是io仍然不可或缺。這是因為1
    的頭像 發(fā)表于 12-02 10:35 ?807次閱讀
    λ-IO:存儲計算下的IO<b class='flag-5'>棧</b>設計

    明達技術(shù)為您剖析軟&amp;硬協(xié)議

    在當今這個科技日新月異的時代,每一個細微之處都蘊含著無限可能。今天,讓我們一同深入探索網(wǎng)絡協(xié)議領(lǐng)域的兩大核心實現(xiàn)方式——軟協(xié)議與硬協(xié)議,它們各具特色,適用于多樣化的應用場景。本文將為您剖析這兩種協(xié)議
    的頭像 發(fā)表于 11-23 16:28 ?630次閱讀
    明達技術(shù)為您剖析軟&amp;硬協(xié)議<b class='flag-5'>棧</b>

    RT-Thread USB協(xié)議-CherryUSB快速上手指南

    CherryUSB是一個小而美、可移植性高的USB主從協(xié)議。它是由國內(nèi)開發(fā)者開發(fā)的一款USB協(xié)議,以其優(yōu)秀的性能效率得到了開發(fā)者的青睞,特別是在高速USB外設上表現(xiàn)出色。值得注意
    的頭像 發(fā)表于 11-22 01:09 ?1983次閱讀
    RT-Thread USB協(xié)議<b class='flag-5'>棧</b>-CherryUSB快速上手指南

    DoIP協(xié)議簡介及主要功能

    的修改,使用單對雙絞線即可實現(xiàn)穩(wěn)定的100Mb/s全雙工通信。正是由于車載以太網(wǎng)的普及應用,對應的車輛診斷協(xié)議DoIP應運而生,本文將基于ISO13400、ISO14229和求遠電子在DoCAN協(xié)議的基礎(chǔ)上,介紹求遠電子自研DoIP協(xié)議。
    的頭像 發(fā)表于 11-13 15:35 ?2811次閱讀
    DoIP協(xié)議<b class='flag-5'>棧</b>簡介及主要功能

    CC256x TI藍牙協(xié)議HIDDemo應用

    電子發(fā)燒友網(wǎng)站提供《CC256x TI藍牙協(xié)議HIDDemo應用.pdf》資料免費下載
    發(fā)表于 11-11 15:21 ?3次下載
    CC256x TI藍牙協(xié)議<b class='flag-5'>棧</b>HIDDemo應用

    CC256x TI藍牙協(xié)議基礎(chǔ)HFGAGDemo應用

    電子發(fā)燒友網(wǎng)站提供《CC256x TI藍牙協(xié)議基礎(chǔ)HFGAGDemo應用.pdf》資料免費下載
    發(fā)表于 11-11 15:18 ?3次下載
    CC256x TI藍牙協(xié)議<b class='flag-5'>棧</b>基礎(chǔ)HFGAGDemo應用