了解 Linux 記憶體分配的細節非常重要,尤其是在核心和系統架構中。讓我們深入了解 Linux 記憶體分配並了解幕後發生的事情。
在電腦中,要讓進程可執行,就需要將其放置在記憶體中。為此,必須將欄位分配給記憶體中的進程。記憶體分配是一個需要注意的重要問題,尤其是在核心和系統架構中。
讓我們詳細了解 Linux 記憶體分配,並了解幕後發生的事情。
大多數軟體工程師不知道這個過程的細節。但如果你是系統程式設計師候選人,你應該對它了解更多。在看分配過程的時候,有必要對 Linux 和glibc庫做一個小細節。
當應用程式需要記憶體時,它們必須從作業系統請求它。來自核心的這個請求自然需要係統呼叫。您不能在使用者模式下自己分配記憶體。
**malloc()**系列函數負責 C 語言中的記憶體分配。這裡要問的問題是 malloc() 作為 glibc 函數是否進行直接系統呼叫。
Linux 核心中沒有稱為 malloc 的系統呼叫。但是,對於應用程式的記憶體需求有兩個系統調用,它們是brk和mmap。
由於您將透過 glibc 函數在應用程式中請求內存,您可能想知道此時 glibc 正在使用哪些系統呼叫。答案是兩者兼具。
#每個行程都有一個連續的資料欄位。透過 brk 系統調用,增加了決定資料域限制的程式中斷值,並執行了分配過程。
儘管使用這種方法分配記憶體非常快,但並非總是可以將未使用的空間傳回系統。
例如,假設您透過 malloc() 函數為 brk 系統呼叫分配了五個字段,每個字段大小為 16KB。當您完成其中第二個欄位時,無法傳回相關資源(解除指派)以便系統可以使用它。因為如果您減少位址值以顯示第二個欄位的開始位置,並呼叫 brk,您將完成第三、四個和五個欄位的釋放。
為了防止這種情況下的記憶體遺失,glibc 中的malloc 實作會監控進程資料欄位中分配的位置,然後透過free() 函數指定將其傳回給系統,以便系統可以將空閒空間用於進一步的記憶體分配。
也就是說,在分配了5 個16KB 的區域之後,如果用free() 函數傳回第二個區域,過一段時間再請求另一個16KB 的區域,而不是透過brk 系統呼叫擴大資料區域,之前的地址是回來。
但是,如果新要求的區域大於 16KB,則資料區域將透過 brk 系統呼叫指派新區域來擴大,因為區域 2 無法使用。雖然二號區域沒有使用,但由於大小不同,應用程式無法使用它。因為這樣的場景,有一種情況叫做內部碎片,實際上你很少能充分利用記憶體的所有部分。
為了更好地理解,請嘗試編譯並執行以下範例應用程式:
#include #include #include int main(int argc, char* argv[]) { char *ptr[7]; int n; printf("Pid of %s: %d", argv[0], getpid()); printf("Initial program break : %p", sbrk(0)); for(n=0; nprintf("After 5 x 16kB malloc : %p", sbrk(0)); free(ptr[1]); printf("After free of second 16kB : %p", sbrk(0)); ptr[5] = malloc(16 * 1024); printf("After allocating 6th of 16kB : %p", sbrk(0)); free(ptr[5]); printf("After freeing last block : %p", sbrk(0)); ptr[6] = malloc(18 * 1024); printf("After allocating a new 18kB : %p", sbrk(0)); getchar(); return 0; }
執行應用程式時,您將獲得類似於以下輸出的結果:
Pid of ./a.out: 31990 Initial program break : 0x55ebcadf4000 After 5 x 16kB malloc : 0x55ebcadf4000 After free of second 16kB : 0x55ebcadf4000 After allocating 6th of 16kB : 0x55ebcadf4000 After freeing last block : 0x55ebcadf4000 After allocating a new 18kB : 0x55ebcadf4000
有 strace 的 brk 的輸出如下:
brk(NULL) = 0x5608595b6000 brk(0x5608595d7000) = 0x5608595d7000
如您所見,0x21000 已新增至資料欄位的結束位址。您可以從值 0x5608595d7000 中理解這一點。因此分配了大約 0x21000 或 132KB 的記憶體。
這裡有兩點要考慮。第一種是分配超過範例程式碼中指定的數量。另一個是哪一行程式碼導致了提供分配的 brk 呼叫。
當您一個接一個地執行上述範例應用程式時,您每次都會看到不同的位址值。以這種方式隨機更改位址空間會使安全攻擊的工作變得非常複雜,並提高了軟體的安全性。
但是,在 32 位架构中,通常使用 8 位来随机化地址空间。增加位数将不合适,因为剩余位上的可寻址区域将非常低。此外,仅使用 8 位组合不会使攻击者的事情变得足够困难。
另一方面,在 64 位体系结构中,由于可以为 ASLR 操作分配的位太多,因此提供了更大的随机性,并且提高了安全程度。
Linux 内核还支持基于 Android 的设备,并且 ASLR 功能在 Android 4.0.3 及更高版本上完全激活。即使仅出于这个原因,也可以说 64 位智能手机比 32 位版本具有显着的安全优势。
通过使用以下命令暂时禁用 ASLR 功能,之前的测试应用程序每次运行时都会返回相同的地址值:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
要将其恢复到以前的状态,在同一个文件中写入 2 而不是 0 就足够了。
mmap 是 Linux 上用于内存分配的第二个系统调用。通过 mmap 调用,内存中任何区域的空闲空间都映射到调用进程的地址空间。
在以这种方式完成的内存分配中,当您想使用前面 brk 示例中的 free() 函数返回第二个 16KB 分区时,没有机制可以阻止此操作。从进程的地址空间中删除相关的内存段。它被标记为不再使用并返回系统。
因为与使用 brk 相比,使用 mmap 的内存分配非常慢,所以需要分配 brk。
使用 mmap,内存的任何空闲区域都映射到进程的地址空间,因此在该进程完成之前,已分配空间的内容被重置。如果没有以这种方式进行重置,则属于先前使用相关内存区域的进程的数据也可以被下一个不相关的进程访问。这样就不可能谈论系统中的安全性。
内存分配非常重要,尤其是在优化和安全问题上。如上面的示例所示,不完全理解此问题可能意味着破坏系统的安全性。
甚至许多编程语言中存在的类似于 push 和 pop 的概念也是基于内存分配操作的。能够很好地使用和掌握系统内存对于嵌入式系统编程和开发安全和优化的系统架构都是至关重要的。
如果您还想涉足 Linux 内核开发,请考虑首先掌握 C 编程语言。
综上所述,Linux 中的内存分配是一个需要注意和理解的重要问题,特别是对于程序员、内核开发人员和系统架构师而言。熟练掌握内存分配可以提高软件性能和安全性,并在嵌入式系统编程和系统架构方面提供更好的支持。同时,C 编程语言的掌握也是涉足 Linux 内核开发的关键。
以上是Linux 上記憶體分配的工作原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!