成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

內核如何阻塞與喚醒進程?

商務辦公
我們先從 Linux 的進程談起,操作系統要運行一個可執行程序,首先要將程序文件加載到內存,然后 CPU 去讀取和執行程序指令,而一個進程就是“一次程序的運行過程”,內核會給每一個進程創建一個名為task_struct的數據結構,而內核也是一段程序,系統啟動時就被加載到內存中了。

  [[328663]]

進程和線程

我們先從 Linux 的進程談起,操作系統要運行一個可執行程序,首先要將程序文件加載到內存,然后 CPU 去讀取和執行程序指令,而一個進程就是“一次程序的運行過程”,內核會給每一個進程創建一個名為task_struct的數據結構,而內核也是一段程序,系統啟動時就被加載到內存中了。

進程在運行過程中要訪問內存,而物理內存是有限的,比如 16GB,那怎么把有限的內存分給不同的進程使用呢?跟 CPU 的分時共享一樣,內存也是共享的,Linux 給每個進程虛擬出一塊很大的地址空間,比如 32 位機器上進程的虛擬內存地址空間是 4GB,從 0x00000000 到 0xFFFFFFFF。但這 4GB 并不是真實的物理內存,而是進程訪問到了某個虛擬地址,如果這個地址還沒有對應的物理內存頁,就會產生缺頁中斷,分配物理內存,MMU(內存管理單元)會將虛擬地址與物理內存頁的映射關系保存在頁表中,再次訪問這個虛擬地址,就能找到相應的物理內存頁。每個進程的這 4GB 虛擬地址空間分布如下圖所示:


 

 

進程的虛擬地址空間總體分為用戶空間和內核空間,低地址上的 3GB 屬于用戶空間,高地址的 1GB 是內核空間,這是基于安全上的考慮,用戶程序只能訪問用戶空間,內核程序可以訪問整個進程空間,并且只有內核可以直接訪問各種硬件資源,比如磁盤和網卡。那用戶程序需要訪問這些硬件資源該怎么辦呢?答案是通過系統調用,系統調用可以理解為內核實現的函數,比如應用程序要通過網卡接收數據,會調用 Socket 的 read 函數:

  1. ssize_t read(int fd,void *buf,size_t nbyte) 

CPU 在執行系統調用的過程中會從用戶態切換到內核態,CPU 在用戶態下執行用戶程序,使用的是用戶空間的棧,訪問用戶空間的內存;當 CPU 切換到內核態后,執行內核代碼,使用的是內核空間上的棧。

從上面這張圖我們看到,用戶空間從低到高依次是代碼區、數據區、堆、共享庫與 mmap 內存映射區、棧、環境變量。其中堆向高地址增長,棧向低地址增長。

請注意用戶空間上還有一個共享庫和 mmap 映射區,Linux 提供了內存映射函數 mmap, 它可將文件內容映射到這個內存區域,用戶通過讀寫這段內存,從而實現對文件的讀取和修改,無需通過 read/write 系統調用來讀寫文件,省去了用戶空間和內核空間之間的數據拷貝,Java 的 MappedByteBuffer 就是通過它來實現的;用戶程序用到的系統共享庫也是通過 mmap 映射到了這個區域。

我在開始提到的task_struct結構體本身是分配在內核空間,它的vm_struct成員變量保存了各內存區域的起始和終止地址,此外task_struct中還保存了進程的其他信息,比如進程號、打開的文件、創建的 Socket 以及 CPU 運行上下文等。

在 Linux 中,線程是一個輕量級的進程,輕量級說的是線程只是一個 CPU 調度單元,因此線程有自己的task_struct結構體和運行棧區,但是線程的其他資源都是跟父進程共用的,比如虛擬地址空間、打開的文件和 Socket 等。

阻塞與喚醒

我們知道當用戶線程發起一個阻塞式的 read 調用,數據未就緒時,線程就會阻塞,那阻塞具體是如何實現的呢?

Linux 內核將線程當作一個進程進行 CPU 調度,內核維護了一個可運行的進程隊列,所有處于TASK_RUNNING狀態的進程都會被放入運行隊列中,本質是用雙向鏈表將task_struct鏈接起來,排隊使用 CPU 時間片,時間片用完重新調度 CPU。所謂調度就是在可運行進程列表中選擇一個進程,再從 CPU 列表中選擇一個可用的 CPU,將進程的上下文恢復到這個 CPU 的寄存器中,然后執行進程上下文指定的下一條指令。

 

而阻塞的本質就是將進程的task_struct移出運行隊列,添加到等待隊列,并且將進程的狀態的置為TASK_UNINTERRUPTIBLE或者TASK_INTERRUPTIBLE,重新觸發一次 CPU 調度讓出 CPU。

那線程怎么喚醒呢?線程在加入到等待隊列的同時向內核注冊了一個回調函數,告訴內核我在等待這個 Socket 上的數據,如果數據到了就喚醒我。這樣當網卡接收到數據時,產生硬件中斷,內核再通過調用回調函數喚醒進程。喚醒的過程就是將進程的task_struct從等待隊列移到運行隊列,并且將task_struct的狀態置為TASK_RUNNING,這樣進程就有機會重新獲得 CPU 時間片。

這個過程中,內核還會將數據從內核空間拷貝到用戶空間的堆上。

 

當 read 系統調用返回時,CPU 又從內核態切換到用戶態,繼續執行 read 調用的下一行代碼,并且能從用戶空間上的 Buffer 讀到數據了。

小結

今天我們談到了一次 Socket read 系統調用的過程:首先 CPU 在用戶態執行應用程序的代碼,訪問進程虛擬地址空間的用戶空間;read 系統調用時 CPU 從用戶態切換到內核態,執行內核代碼,內核檢測到 Socket 上的數據未就緒時,將進程的task_struct結構體從運行隊列中移到等待隊列,并觸發一次 CPU 調度,這時進程會讓出 CPU;當網卡數據到達時,內核將數據從內核空間拷貝到用戶空間的 Buffer,接著將進程的task_struct結構體重新移到運行隊列,這樣進程就有機會重新獲得 CPU 時間片,系統調用返回,CPU 又從內核態切換到用戶態,訪問用戶空間的數據。

責任編輯:武曉燕 來源: 今日頭條
相關推薦

2017-03-01 16:40:12

Linux驅動技術設備阻塞

2023-05-08 12:03:14

Linux內核進程

2017-08-02 15:28:58

Linux進程睡眠和喚醒

2011-08-18 10:26:52

Linux 3.1內核

2018-03-28 08:52:53

阻塞非阻塞I

2021-04-15 05:51:25

Linux

2012-10-10 10:00:27

同步異步開發Java

2011-01-14 14:49:05

2019-04-10 13:43:19

Linux內核進程負載

2017-12-06 10:50:50

Linux自動喚醒系統運行時間

2010-04-21 12:54:46

Unix內核

2025-05-15 09:12:27

2021-06-24 08:37:34

網絡安全內核代碼

2012-05-14 14:09:53

Linux內核調度系統

2023-03-15 08:39:07

遠程服務調用

2010-01-20 18:10:48

2010-02-02 09:36:38

2018-03-19 08:32:16

Linux 進程睡眠喚醒

2010-04-30 16:19:17

Unix內核

2017-03-27 18:05:49

Linux內核編譯與開發
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 涩涩视频在线看 | 久久精品网 | 91资源在线观看 | 精品少妇一区二区三区在线播放 | 色婷综合网 | 国产免费播放视频 | 特一级毛片 | 亚洲综合第一页 | 国产传媒 | 99热精品在线| 欧美h| 天天干亚洲 | 成人在线中文字幕 | 99tv成人影院| 91精品国产99久久 | 国产欧美精品一区二区三区 | 精品国产视频 | 久草在线 | 不卡一二区| 91精品国产91久久综合桃花 | 欧美在线天堂 | 日韩伦理一区二区 | 午夜国产| 日韩精品一区二区三区在线观看 | 精品国产一区二区三区性色av | 欧美色影院 | 中文字幕免费观看 | 国产午夜精品视频 | 一级免费在线视频 | 亚洲欧洲一区 | 中文字幕在线国产 | 天天操夜夜操 | 中文字幕精品一区 | 97在线播放 | 黄色av免费 | 中文字幕av中文字幕 | a级黄色毛片免费播放视频 国产精品视频在线观看 | 亚洲欧洲激情 | 亚洲高清视频一区 | 国产成人精品久久二区二区91 | 免费污视频 |