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

C語言與操作系統的內存布局

開發 前端
C語言適合寫操作系統,我覺得跟丹尼斯-里奇發明它的目的就是為了寫Unix有關:不好用的地方已經被優化過了。

?C語言之所以適合寫操作系統,就在于它的內存布局簡單:

1,所有的全局變量都被常量初始化,

2,不需要運行時的狀態,

3,也不需要在main()函數之前運行額外的初始化代碼。

操作系統的初始化是很復雜的。

在C語言寫成的內核main()函數運行之前,操作系統要運行一段很復雜的匯編代碼,以完成內核的內存初始化。

這段匯編代碼包含著很多重要的內核全局數據,它是由內核作者精心定制的,沒法由編譯器自動生成。

對于內核程序員來說,編譯器做的事越少越好,但是又不能像匯編器那么少?

C語言適合寫操作系統,我覺得跟丹尼斯-里奇發明它的目的就是為了寫Unix有關:不好用的地方已經被優化過了。

1970年,丹尼斯-里奇怎么一邊改unix系統的代碼、一邊改cc編譯器的代碼的咱就不回憶了。

這里說說C語言和操作系統的內存布局。

1.C語言的內存布局。

C語言編譯連接之后的可執行文件,分為:

1) 代碼段(.text),

2) 只讀數據段(.rodata),

3) 數據段(.data),

4) 堆 (heap),

5) 棧 (stack),

其中需要存儲在文件里的只有前3個,

后2個在進程運行期間是動態變化的臨時數據,并不需要存儲在文件里。

代碼段的權限是只讀+可執行,

只讀數據段的權限是只讀,

數據段、堆、棧的權限都是可讀可寫的,但不能運行。

如果系統內核發現了進程的內存權限是錯誤的,那么就是段錯誤:信號是SIGSEGV。

*("hello") = 1;

這種代碼肯定是“段錯誤”的,因為常量字符串位于只讀數據段,它的內容是不可寫的。

通過緩沖區溢出來覆蓋棧的返回地址的黑客代碼,也會被系統內核發現運行地址不在代碼段,所以也是段錯誤。

2.內核的內存布局。

內核的內存布局,包含這幾個重要的全局數據:

1)內核頁表

它是內核的虛擬內存與物理內存的映射。

在開啟分頁機制之前,就要設置好內核頁表的前幾頁:

至少要把內核代碼所在的內存空間映射到頁表里,否則開啟分頁機制時就直接出錯了。

在32位機上,它是由頁目錄-頁表構成的2級數組:

頁目錄里的每一項記錄每個頁表的物理地址,頁表里的每一項記錄每個內存頁的物理地址。

在64位機上頁表的結構更為復雜,intel手冊上有:我沒仔細看過,有興趣的可以看看。

1個內存頁是4096字節,所以物理地址的最低12位全是0,用來記錄每個頁的讀寫權限。

頁目錄里每項的最低12位,用于記錄它對應的整個頁表的讀寫權限。

1個頁表記錄1024個頁,每個頁4096字節,所以1個頁表管理4M的物理內存。

2)中斷向量表

它存放各種硬件中斷、以及int 0x80軟件中斷的處理函數,也叫中斷服務例程(irq)。

int 0x80軟件中斷,就是Linux系統調用的中斷號。

當然,在64位機上,直接使用syscall匯編指令就行。

syscall的軟件中斷機制,是intel在64位上又新造的一種進入CPU ring0特權級的指令,使用方式跟之前的int指令不大一樣。

我懷疑intel的CPU研發也是有KPI的,怪不得Linus大牛也經常吐槽intel的CPU設計。

一個版本加一個新的指令,純屬給系統軟件的開發者找難題?

中斷向量表,也是個256項的數組,每項都是某個中斷的函數指針。

在中斷被觸發之后,CPU就是靠這個數組去查找對應的中斷處理函數的。

3)全局描述符表

它描述的是內核的內存布局,每項8個字節,共256項。

但實際上,只需要使用前5項就行:

0x0,不使用,

0x8,內核代碼段,

0x10,內核數據段,內核堆棧段,它們2個的權限一樣,可以共用一項。

0x20,任務門的描述項,

0x28,局部描述符表的描述項。

siska內核demo的內存布局

因為每項都是8字節,所以地址都是8的倍數。

4)局部描述符表

它是用于進程的,進程因為跟內核的權限不同,所以進程的段選擇符都在局部描述符表里:

內核的段選擇符是0x8,進程的是0xf。

段寄存器CS、DS、SS,到了保護模式下都成了段選擇符,真正的內存地址在GDT表里。

在16位的實模式下,它們才存儲真正的段的內存地址。

5)任務門

CPU把每個進程看做一個任務,所以要切換進程時需要任務門的描述結構。

它是104個字節。

但是,Linux系統的進程切換是軟切換:任務門的描述結構只在系統初始化時加載一次,具體的進程切換時只切換頁表和內核棧,然后就可以騙過CPU了?

重新加載任務門的時間消耗比較大,而軟切換的時間消耗比較小。

intel的這個設計,也是不受Linus大牛待見的設計之一?

6)系統調用表

它也是一個大數組,它的每一項也是函數指針。

系統調用的入口是int 0x80軟件中斷(64位機上是syscall指令)。

進入內核之后,每個號碼對應一個系統調用。

open()、close()、write()、read(),這些系統調用都有各自的號碼,這些號碼就是系統調用表的數組索引。

如果open()的系統調用號碼是i,那么open()在內核里實際運行的就是這行代碼:

syscall_table[i]();

7)物理內存的管理數組

物理內存的管理結構,是一個很大的一維數組。

假設物理內存有4G,1個內存頁是4K,那么這個數組的元素個數就是1024x1024,1M。

數組的每一項,記錄1個物理內存頁的狀態。

如果每項是4個字節的話,那么管理效率就是:(4096-4) / 4096。

管理數據所占的字節數越多,對物理內存的浪費越大。

get_free_pages()函數,就是通過查看這個數組來分配物理內存頁的。

因為內核是一個高并發環境,這個管理結構里必須要有自旋鎖,以控制多個CPU的并發訪問。

自旋鎖+引用計數就至少8字節,所以這個數組也是非常浪費內存的。

如果多個線程之間要共享內存,那么只要把同一個物理內存頁映射到這幾個線程的頁表里,然后增加物理內存頁的引用計數就行:

這就是共享內存在內核里的本質。

8)進程的頁表和內核棧

進程的頁表和內核棧,不屬于內核的全局數據,而是附屬于進程的局部數據。

內核在調度某個進程的時候,就把頁目錄基地址寄存器cr3和棧寄存器rsp切換成這個進程的頁表和內核棧。

不同的進程之間,之所以有各自的虛擬內存空間,互相不干擾,就是因為每個進程的頁表不一樣。

要在進程之間共享內存,也跟線程之間共享內存一樣,把同一個物理內存頁映射到它們各自的頁表就行。

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

2022-11-01 11:22:38

2022-11-28 07:21:53

操作系統內存管理

2021-06-11 07:26:16

操作系統內存管理Cpu

2022-06-26 00:24:57

C語言操作系統語言

2025-01-06 08:28:45

C語言操作系統程序

2009-08-17 08:32:56

Linux操作系統內存管理Linux

2021-06-22 09:09:34

V語言Vinix操作系統

2010-04-20 14:17:21

Unix操作系統

2023-11-06 08:47:52

操作系統物理內存

2021-03-28 13:54:31

操作系統內存管理

2012-08-13 10:19:03

IBMdW

2018-08-09 16:12:59

操作系統內存分配

2010-04-22 15:14:12

Aix操作系統

2012-05-04 09:49:34

進程

2014-07-29 10:12:38

LinuxC語言編程

2010-04-15 14:40:26

Unix操作系統

2009-12-23 17:47:15

Linux操作系統

2010-04-19 18:13:48

Unix操作系統

2009-12-09 17:25:19

Linux操作系統

2019-07-05 10:04:10

操作系統Android OS
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久久久久国产精品 | 午夜欧美 | 黄网站涩免费蜜桃网站 | 99精品久久久国产一区二区三 | 国产不卡在线观看 | 中文字幕蜜臀av | 亚洲成av| av网站在线看 | 欧亚av在线| 欧美成人免费 | 91一区二区| 成年人在线视频 | 91精品国产91久久久久青草 | 成人乱人乱一区二区三区软件 | av先锋资源 | 日本亚洲欧美 | av中文字幕在线 | av色站| 国产视频一区二区在线观看 | 成人av鲁丝片一区二区小说 | 岛国av一区二区三区 | 欧美日韩高清 | 理论片免费在线观看 | 日本久久综合 | 欧美成人精品一区二区三区 | 久久久久国产 | 亚洲综合一区二区三区 | 黄色大全免费看 | 精品三级在线观看 | 欧美成人精品二区三区99精品 | 亚洲精品成人 | 欧美视频 | 99国产视频| 毛片日韩 | 麻豆视频在线免费看 | 在线观看亚洲专区 | 亚洲国产区 | 日韩欧美不卡 | 国产精品一区在线播放 | 精品视频一区二区三区在线观看 | 国产精品一区二区三 |