Linux記憶體管理

来源:http://www.cnblogs.com/xiaojiang1025/archive/2016/10/06/5933476.html
-Advertisement-
Play Games

MMU MMU=Segmentation Unit+Paging Unit //MMU: Memory Management Unit logical address = Segmentation Unit= linear address = Paging Unit= physical addres ...


MMU

MMU=Segmentation Unit+Paging Unit //MMU: Memory Management Unit

logical address=>Segmentation Unit=>linear address=>Paging Unit=>physical address

  • Linux系統中採用虛擬記憶體管理技術來進行記憶體空間的管理, 即: 每個進程都可以擁有0~4G-1的虛擬記憶體地址空間(虛擬的,並不是真實存在的), 其中0~3G-1之間的地址空間叫做用戶空間,而3G~4G-1之間的地址空間叫做內核空間,由操作系統(Paging Unit)負責建立虛擬記憶體地址到真實物理記憶體/文件的映射, 因此不同進程中的虛擬記憶體地址看起來是一樣的, 但所對應的物理記憶體/文件是不一樣的,%p列印出來的是虛擬記憶體地址(線性地址), 不是真實的地址
  • 一般來說,絕大多數程式都運行在用戶空間中, 不能直接訪問內核空間, 但是內核提供了一些相關的函數可以用於訪問內核空間,
  • 虛擬記憶體技術可以解決物理記憶體不夠用的問題eg:我們需要4G物理記憶體=>1G 的真實物理記憶體,3G的硬碟
  • 記憶體地址的基本單位是位元組, 記憶體映射的基本單位是記憶體頁, 目前主流的OS一個記憶體頁的大小是4Kb;
  • Segmentation Fault:
    試圖操作沒有操作許可權的記憶體區域時可能引發段錯誤, eg: 試圖修改只讀常量區中的數據內容時
    試圖訪問沒有經過映射的虛擬地址時可能引發段錯誤, eg: 讀取頂定義地址(無效地址)中的數據內容時

malloc()

#include <stdlib.h>
void *malloc(size_t size);
  • 使用malloc()申請動態記憶體時, 除了申請參數指定大小的記憶體空間之外, 還會申請額外的12byte(也可能不是12)用於存儲該動態記憶體的管理信息, eg:大小, 是否空閑etc.
  • 使用malloc()申請動態記憶體時, 註意避免對記憶體空間的越界訪問, 以避免破壞該動態記憶體的管理信息, 也就避免Segmentation fault的產生
  • 一般來說, 使用malloc()函數申請比較小塊的動態記憶體時, 操作系統會一次性映射33個記憶體頁的存儲空間, 以防止多次malloc, 提高系統效率
  • malloc()在linux平臺的實現會調用sbrk()

eg

 #include<stdlib.h>
int *p1=(int*)malloc(sizeof(int));  //會一口氣分配33頁, 把p1的管理信息放在p1之後的12byte
int *p2=(int*)malloc(sizeof(int));  //因為p1時分配的33頁還沒用完, 所以直接分配在p1的管理信息後
//管理信息的區域可以寫入,但是你寫了之後free(p1)就會段錯誤,所以不要寫
//超出33page的記憶體你訪問都不行, 直接段錯誤
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
int main(){
    printf("當前進程進程號:%d\n",getpid());
    int *pi=(int*)malloc(sizeof(int));
    printf("pi=%p\n",pi);   //0x21000 就是33個記憶體頁//0x 1000 就是 1個記憶體頁
    
    //故意越界一下試試, 不超過33記憶體頁的範圍
    *(pi+1024*30)=250;
    printf("*(pi+1024*30)=%d\n",*(pi+1024*30));
    //沒有發生段錯誤
    
    //故意越界一下試試, 超過33記憶體頁的範圍
    *(pi+1024*33)=250;  //ATTENTION:pi是int*, 所以pi+1024*33可是pi+4*1024*33byte啊
    printf("*(pi+1024*33)=%d\n",*(pi+1024*33));
    //發生段錯誤
    while(1);
    return 0;
}
/*
$ ./a.out 
當前進程進程號:2787
pi=0x9c40008
*(pi+1024*30)=250
Segmentation fault (core dumped)
*/

free()

a) #include <stdlib.h>
void free(void *ptr);

frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(), calloc() or realloc()

Note:

  • 使用free釋放多少, 則從映射的總量中減去多少,當所有的動態記憶體全部釋放時, 系統可能會保留33個記憶體頁, 以提高效率
    free() does not check the pointer passed to see whether it is NULL and does not set the pointer to NULL before it returns, while setting a pointer to NULL after freeing is a good practice.If the pointer is NULL, then no action is performed and the program will execute without terminating abnormally;
void safefree(void **pp){
    if(pp!=NULL&&*pp!=NULL){
    free(*pp);
    *pp=NULL;
    }
}
#define safeFree(p) safeFree((void**)&(p))
int main(){
    int *pi;
    pi=(int*)malloc(sizeof(int));
    *pi=5;
    printf(“Before:%p\n”,pi);
    safefree(pi);
    printf(“After:%p\n”,pi);
    safefree(pi);
    return 0;
}

getpagesize()

#include <unistd.h>
int getpagesize(void);

returns the number of bytes in a memory page, where "page" is a fixed-length block, the unit for memory allocation and file mapping performed by mmap(2).

sbrk()

 #include <unistd.h>
void *sbrk(intptr_t increment); //intptr_t 是int的別名, _t都可以認為是int的別名,偶爾是short 或long etc

調整動態記憶體/虛擬記憶體的大小, increments the program's data space by increment bytes. Calling sbrk() with an increment of 0 can be used to find the current location of the program break.(當前動態分配記憶體的末尾位置)(程式斷點,program break,可以理解為offset的位置),成功返回the previous program break,失敗返回(void*)-1
increment>0表示申請動態記憶體, 就是記憶體空間變大

increment=0表示獲取當前動態記憶體的末尾地址, 就是記憶體空間不變

increment<0表示釋放動態記憶體, 就是記憶體空間變小

#include<stdlib.h>
#include<unistd.h>
//使用sbrk()獲取一個有效地址
void* pv=sbrk(0);
if((void*)-1==pv)
    perror("sbrk"),exit(-1);
//使用sbrk()在當前位置基礎上申請一個int長度的記憶體
void* p2=sbrk(sizeof(int));
if((void*)-1==p2)
    perror("sbrk"),exit(-1);

Note:雖然sbrk()既能申請動態記憶體, 也能釋放動態記憶體, 但是使用sbrk函數申請動態記憶體時更加方便,一般來說, 使用sbrk函數申請比較小塊的動態記憶體時, 操作系統會映射1個記憶體頁大小的記憶體空間, 當申請的動態記憶體超過1個記憶體也時, 系統會再次映射1個記憶體頁的大小, 當所有動態記憶體釋放完畢時, 系統不會保留任何的動態記憶體映射, 與malloc()相比, 比較節省記憶體空間, 也不會申請額外的存儲空間, 但是頻繁分配時效率沒有malloc()高

brk()

#include <unistd.h>
int brk(void *addr);

調整動態記憶體/虛擬記憶體的大小, sets the end of the data segment to the value specified by addr,成功返回0,失敗返回-1, 設errno為ENOMEM

  • 如果addr>原來的末尾地址,申請動態記憶體, 記憶體空間變大
  • 如果addr=原來的末尾地址,記憶體空間不變
  • 如果addr<原來的末尾地址,釋放動態記憶體, 記憶體空間變小
//使用brk()釋放動態記憶體  
#include<stdlib.h>   
#include<unistd.h>   
int res=brk(pv);
if(-1==res)
    perror("brk"),exit(-1);

Note:雖然brk()既能申請動態記憶體, 又能釋放動態記憶體, 但是釋放動態記憶體更加方便, 而sbrk()申請動態記憶體更加方便, 因此一般情況下兩個函數搭配使用, sbrk()專門用於申請, brk()專門用於釋放

Memory Leak

A memory leak occurs when allocated memory is never used again but is not freed !!!A problem with memory leaks is that the memory cannot be reclaimed and used later. The amount of memory available to the heap manager is decreased. If memory is repeatedly allocated and then lost, then the program may terminate when more memory is needed but malloc() cannot allocate it because it ran out of memory. In extreme cases, the operationg system may crash

  1. losing the address:

    int *pi=(int*)malloc(sizeof(int));
    *pi=5;  //之前申請的記憶體已經無法釋放了,因為address已經丟了
    …
    pi=(int*)malloc(sizeof(int);
  2. Hidden memory leaks,Memory leaks can also occur when the program should release memory but does not. A hidden memory leak occurs when an object is kept in the heap even thouth the object is no longrer needed. This is frequently the result of programmer oversight. The primary problem with this type of leak is that the obkect is using memory that is no longer nedded and should be returned to the heap. In the worst case, the heap manager may not be able to allocate memory when requested, possibly forcing the program to terminate. At best, we are holding unneeded memory.
  3. Structure deallocation without free pointers defined in it. When memory is allocated for a strcture, the rentime system will not aytomaticallu allocate memory for any pointers defined within it. Likewise, when the structure goes away, the runtime system will not automatically deallocate memory asigned to the structure’s pointers
    The correct way to allocate and deallocate a structure pointer with pointer fields:

    void initializePerson(Person *person, sonst char* fn,const char * ln, const chat* title,uint age){ 
    person->firstName=(char*)malloc(strlen(fn)+1); 
        strcpy(person->firstName,fn); 
        ... 
        person->age=age; 
    } 
    void deallocatePerson(Person* person){ 
        free(person->firstName); 
        free(person->lastName); 
        free(person->title); 
    } 
    void processPerson(){ 
        Person* pPerson; 
        pPerson=(Person*)malloc(sizeof(Person)); 
        initilizePerson(pPterson,"Peter","Underwood","Manager",36); 
        ... 
        deallocatePerson(pPerson); 
        free(pPerson); 
    }

Dangling Pointers

a pointer still references the original memory after it has been freed. The use of dangling pointer can result in:

  1. Unpredicatable behavior if the memory is accessed
  2. Segmentation fauts if the memory is no longer accessible
  3. Potential security risks

Several approaches for pointer-induced problem:

  1. Setting a pointer to NULL after freeing it.
  2. Writing special functions to replace the free function.
  3. Some systems(rumtime/debugger) will overwrite data when it is freed
  4. Use third-party tools to detect dangling pointers and other problem
  5. Displaying pointer values can be helpful in debugging dangling pointer

Note:When a pointer is passed to a function, it is always good practice to verify it is not NULL before using it;

2 ways returning a pointer referencing heap

  1. Allocate memory within the function using malloc and return its address. The caller is responsible for deallocating the memory returned;
  2. Pass an object to the function where it is modified.This makes the allocation and deallocation of the object's memory the caller's responsibility.

Several potential problems can occur when returning a pointer from a function:

  • Return an uninitialized pointer
  • Returning a pointer to an invalid address
  • Returning a pointer to a local variable
  • Returning a pointer but failing to free it =>memory leak

A pointer to one function can be cast to another type.

This should be done carefully since the runtime system doesn’t verify that parameters used by a function pointer are correct.It is also possible to cast a function pointer to a different function pointer and then back. The resulting pointer will be equal to the original pointer, The size of function pointers used are not necessarily the same.
Note that conversion between function pointers and pointers to data is not guaranted to work;

Always make sure you use the correct argument list for function pointers, Failure to do so will result in indeterminate behavior


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 環境:虛擬機VMware10 首先瞭解幾個註意的地方: 一、分區類型: 1、主分區:最多只能有四個; 2、擴展分區:最多只能有一個,且主分區加上擴展分區最多只能有四個,擴展分區不能寫入數據,只能包含邏輯分區 3、邏輯分區:可以寫入數據和格式化 舉個例子如圖: 其中1、2、3為主分區,4為擴展分區,5 ...
  • 向一個/一些進程發送一個信號 $kill [ slL] [...] 指定發送的信號,可以使用名稱或者信號編號 列出當前系統的所有信號 ...
  • 概述 多進程代碼區模型(其他區參見copy on write): getpid()、getppid() getuid()、geteuid() getgid(),getegid() fork() include include if(0==pid){ int res=execl("./proc","p ...
  • ps
    查看當前終端所啟動的進程, 不加選項只查看當前終端的進程 ps aux 查看所有進程,ps aux是BSD syntax,ps aux是standard syntax, 但二者的意義完全不同= $man ps ps ef 以全格式的方式顯示所有進程(every)查看當前終端所啟動的進程, 不加選項只 ...
  • 本文首先從巨集觀的角度對進程間的通信方式之一,消息隊列進行闡述,然後以代碼實例對消息隊列進行更近一步的闡述,最後試著暢想消息隊列的潛在應用 ...
  • access() fstat()、stat()、lstat() 獲取文件大小 1. fseek()把offset移到SEEK_END, 再用ftell()返迴文件的大小 2. lseek() , 返迴文件的大小 3.stat(), struct stat st; st.st_size的數值就是文件大 ...
  • 文件描述符(File Descriptor) a small, nonnegative integer for use in subsequent system calls (read(2), write(2), lseek(2), fcntl(2), etc.) ($man 2 open). 每個 ...
  • #include "stm32f10x.h"#include "key.h" //按鍵初始化函數void KEY_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...