在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

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

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

3天內不再提示

malloc 申請內存的兩種方式

科技綠洲 ? 來源:Linux開發架構之路 ? 作者:Linux開發架構之路 ? 2023-11-13 11:42 ? 次閱讀

我們知道malloc() 并不是系統調用,也不是運算符,而是 C 庫里的函數,用于動態分配內存。

malloc 申請內存的時候,會有兩種方式向操作系統申請堆內存:

  • 方式一:通過 brk() 系統調用從堆分配內存
  • 方式二:通過 mmap() 系統調用在文件映射區域分配內存;

一、brk()系統調用

1、brk()的申請方式

一般如果用戶分配的內存小于 128 KB,則通過 brk() 申請內存。而brk()的實現的方式很簡單,就是通過 brk() 函數將堆頂指針向高地址移動,獲得新的內存空間。如下圖:

圖片

malloc 通過 brk() 方式申請的內存,free 釋放內存的時候,并不會把內存歸還給操作系統,而是緩存在 malloc 的內存池中,待下次使用,這樣就可以重復使用。

2、brk()系統調用的優缺點

所以使用brk()方式的點很明顯:可以減少缺頁異常的發生,提高內存訪問效率。

但它的缺點也同樣明顯:由于申請的內存沒有歸還系統,在內存工作繁忙時,頻繁的內存分配和釋放會造成內存碎片。brk()方式之所以會產生內存碎片,是由于brk通過移動堆頂的位置來分配內存,并且使用完不會立即歸還系統,重復使用,如果高地址的內存不釋放,低地址的內存是得不到釋放的。

正是由于使用brk()會出現內存碎片,所以在我們申請大塊內存的時候才會使用mmap()方式,mmap()是以頁為單位進行內存分配和管理的,釋放后就直接歸還系統了,所以不會出現這種小碎片的情況。

3、brk()系統調用的優化

一、Ptmalloc :malloc采用的是內存池的管理方式,Ptmalloc 采用邊界標記法將內存劃分成很多塊,從而對內存的分配與回收進行管理。為了內存分配函數malloc的高效性,ptmalloc會預先向操作系統申請一塊內存供用戶使用,當我們申請和釋放內存的時候,ptmalloc會將這些內存管理起來,并通過一些策略來判斷是否將其回收給操作系統。這樣做的最大好處就是,使用戶申請和釋放內存的時候更加高效,避免產生過多的內存碎片。

二、Tcmalloc:Ptmalloc在性能上還是存在一些問題的,比如不同分配區(arena)的內存不能交替使用,比如每個內存塊分配都要浪費8字節內存等等,所以一般傾向于使用第三方的malloc。

Tcmalloc是Google gperftools里的組件之一。全名是 thread cache malloc(線程緩存分配器)其內存管理分為線程內存和中央堆兩部分。

1.小塊內部的分配:對于小塊內存分配,其內部維護了60個不同大小的分配器(實際源碼中看到的是86個),和ptmalloc不同的是,它的每個分配器的大小差是不同的,依此按8字節、16字節、32字節等間隔開。在內存分配的時候,會找到最小符合條件的,比如833字節到1024字節的內存分配請求都會分配一個1024大小的內存塊。如果這些分配器的剩余內存不夠了,會向中央堆申請一些內存,打碎以后填入對應分配器中。同樣,如果中央堆也沒內存了,就向中央內存分配器申請內存。

在線程緩存內的60個分配器分別維護了一個大小固定的自由空間鏈表,直接由這些鏈表分配內存的時候是不加鎖的。但是中央堆是所有線程共享的,在由其分配內存的時候會加自旋鎖(spin lock)。

2.大內存的分配:對于大內存分配(大于8個分頁, 即32K),tcmalloc直接在中央堆里分配。中央堆的內存管理是以分頁為單位的,同樣按大小維護了256個空閑空間鏈表,前255個分別是1個分頁、2個分頁到255個分頁的空閑空間,最后一個是更多分頁的小的空間。這里的空間如果不夠用,就會直接從系統申請了。

3.ptmalloc與tcmalloc的不足:都是針對小內存分配和管理;對大塊內存還是直接用了系統調用。應該盡量避免大內存的malloc/new、free/delete操作。頻繁分配小內存,例如:對bool、int、short進行new的時候,造成內存浪費。

三、Jemalloc: jemalloc 是由 Jason Evans 在 FreeBSD 項目中引入的新一代內存分配器。它是一個通用的malloc實現,側重于減少內存碎片和提升高并發場景下內存的分配效率,其目標是能夠替代 malloc。下面是Jemalloc的兩個重要部分:

1.arena:arena 是 jemalloc 最重要的部分,內存由一定數量的 arenas 負責管理。每個用戶線程都會被綁定到一個 arena 上,線程采用 round-robin 輪詢的方式選擇可用的 arena 進行內存分配,為了減少線程之間的鎖競爭,默認每個 CPU 會分配 4 個 arena,各個 arena 所管理的內存相互獨立。

struct arena_s {

	atomic_u_t		nthreads[2];
	tsdn_t		*last_thd;

	arena_stats_t		stats;  // arena的狀態

	ql_head(tcache_t)	tcache_ql;
	ql_head(cache_bin_array_descriptor_t)	cache_bin_array_descriptor_ql;
	malloc_mutex_t				tcache_ql_mtx;

	prof_accum_t		prof_accum;
	uint64_t		prof_accumbytes;

	atomic_zu_t		offset_state;

	atomic_zu_t		extent_sn_next;  // extent的序列號生成器狀態

	atomic_u_t		dss_prec;   

	atomic_zu_t		nactive;    // 激活的extents的page數量

	extent_list_t	large;      // 存放 large extent 的 extents

	malloc_mutex_t	large_mtx;  // large extent的鎖

	extents_t extents_dirty;    // 剛被釋放后空閑 extent 位于的地方

	extents_t extents_muzzy;    // extents_dirty 進行 lazy purge 后位于的地方,dirty - > muzzy

	extents_t extents_retained; // extents_muzzy 進行 decommit 或 force purge 后 extent 位于的地方,muzzy - > retained

	arena_decay_t	decay_dirty; // dirty -- > muzzy 

	arena_decay_t	decay_muzzy; // muzzy -- > retained 

	pszind_t		extent_grow_next;
	pszind_t		retain_grow_limit;
	malloc_mutex_t		extent_grow_mtx;

	extent_tree_t		extent_avail;     // heap,存放可用的 extent 元數據

	malloc_mutex_t		extent_avail_mtx; // extent_avail的鎖

	bin_t			bins[NBINS];      // 所有用于分配小內存的 bin

	base_t			*base;            // 用于分配元數據的 base

	nstime_t		create_time;      // 創建時間
};

2.extent:管理 jemalloc 內存塊(即用于用戶分配的內存)的結構,每一個內存塊大小可以是 N * page_size(4KB)(N >= 1)。每個 extent 有一個序列號(serial number)。一個 extent 可以用來分配一次 large_class 的內存申請,但可以用來分配多次 small_class 的內存申請。

struct extent_s {
    uint64_t		e_bits; // 8字節長,記錄多種信息

    void			*e_addr; // 管理的內存塊的起始地址

    union {
		size_t	e_size_esn; // extent和序列號的大小
		size_t	e_bsize;    // 基本extent的大小
	};

    union {
		/* 
         * S位圖,當此 extent 用于分配 small_class 內存時,用來記錄這個 extent 的分配情況,        
         * 此時每個 extent 的內的小內存稱為 region 
         */
		arena_slab_data_t	e_slab_data; 

		atomic_p_t		e_prof_tctx; // 一個計數器,用于large object
	};
}

二、mmap()系統調用

1、mmap基礎概念

mmap 是一種內存映射文件的方法,即將一個文件或者其他對象映射到進程的地址空間,實現文件磁盤地址和進程虛擬地址空間中一段虛擬地址的一一映射關系。

實現這樣的映射關系后,進程就可以采用指針的方式讀寫操作這一段內存,而系統會自動回寫臟頁面到對應的文件磁盤上,即完成了對文件的操作而不必調用read,write等系統調用函數。相反,內核空間的這段區域的修改也直接反映用戶空間,從而可以實現不同進程的文件共享。如下圖所示:

圖片

由上圖可以看出,進程的虛擬地址空間,由多個虛擬內存區域構成。虛擬內存區域是進程的虛擬地址空間中的一個同質區間,即具有同樣特性的連續地址范圍。上圖中所示的text數據段、初始數據段、Bss數據段、堆、棧、內存映射,都是一個獨立的虛擬內存區域。而為內存映射服務的地址空間處在堆棧之間的空余部分。

linux 內核使用的vm_area_struct 結構來表示一個獨立的虛擬內存區域,由于每個不同質的虛擬內存區域功能和內部機制不同;因此同一個進程使用多個vm_area_struct 結構來分別表示不同類型的虛擬內存區域。各個vm_area_struct 結構使用鏈表或者樹形結構鏈接,方便進程快速訪問。如下圖所示:

圖片

vm_area_struct 結構中包含區域起始和終止地址以及其他相關信息,同時也包含一個vm_ops 指針,其內部可引出所有針對這個區域可以使用的系統調用函數。這樣,進程對某一虛擬內存區域的任何操作都需要的信息,都可以從vm_area_struct 中獲得。mmap函數就是要創建一個新的vm_area_struct結構 ,并將其與文件的物理磁盤地址相連。

2、mmap 內存映射原理

mmap 內存映射實現過程,總的來說可以分為三個階段:

(一)進程啟動映射過程,并在虛擬地址空間中為映射創建虛擬映射區域

1、進程在用戶空間調用函數mmap ,原型:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);

2、在當前進程虛擬地址空間中,尋找一段空閑的滿足要求的連續的虛擬地址

3、為此虛擬區分配一個vm_area_struct 結構,接著對這個結構各個區域進行初始化

4、將新建的虛擬區結構(vm_area_struct)插入進程的虛擬地址區域鏈表或樹中

(二)調用內核空間的系統調用函數mmap (不同于用戶空間函數),實現文件物理地址和進程虛擬地址的一一映射關系

5、為映射分配新的虛擬地址區域后,通過待映射的文件指針,在文件描述符表中找到對應的文件描述符,通過文件描述符,鏈接到內核“已打開文集”中該文件結構體,每個文件結構體維護者和這個已經打開文件相關各項信息。

6、通過該文件的文件結構體,鏈接到file_operations模塊,調用內核函數mmap,其原型為:int mmap(struct file *filp, struct vm_area_struct *vma),不同于用戶空間庫函數。

7、內核mmap函數通過虛擬文件系統inode模塊定位到文件磁盤物理地址。

8、通過remap_pfn_range函數建立頁表,即實現了文件地址和虛擬地址區域的映射關系。此時,這片虛擬地址并沒有任何數據關聯到主存中。

(三)進程發起對這片映射空間的訪問,引發缺頁異常,實現文件內容到物理內存(主存)的拷貝。

前兩個階段僅在于創建虛擬區間并完成地址映射,但是并沒有將任何文件數據拷貝至主存。真正的文件讀取是當進程發起讀或者寫操作時。

9、進程的讀寫操作訪問虛擬地址空間這一段映射地址后,通過查詢頁表,先這一段地址并不在物理頁面。因為目前只建立了映射,真正的硬盤數據還沒有拷貝到內存中,因此引發缺頁異常。

10、缺頁異常進行一系列判斷,確定無法操作后,內核發起請求掉頁過程。

11、調頁過程先在交換緩存空間中尋找需要訪問的內存頁,,如果沒有則調用nopage函數把所缺的頁從磁盤裝入到主存中。

12、之后進程即可對這片主存進行讀或者寫的操作了,如果寫操作改變了內容,一定時間后系統自動回寫臟頁面到對應的磁盤地址,也即完成了寫入到文件的過程。

注:修改過的臟頁面并不會立即更新回文件,而是有一段時間延遲,可以調用msync() 來強制同步,這樣所寫的內容就能立即保存到文件里了。

3、mmap優點

1、對文件的讀取操作跨過了頁緩存,減少了數據的拷貝次數,用內存讀寫取代了I/O讀寫,提高了讀取的效率。

2、實現了用戶空間和內核空間的高校交互方式,兩空間的各自修改操作可以直接反映在映射的區域內,從而被對方空間及時捕捉。

3、提供進程間共享內存及互相通信的方式。不管是父子進程還是無親緣關系進程,都可以將自身空間用戶映射到同一個文件或者匿名映射到同一片區域。從而通過各自映射區域的改動,打到進程間通信和進程間共享的目的。

同時,如果進程A和進程 B 都映射了區域C,當A第一次讀取C時候,通過缺頁從磁盤復制文件頁到內存中,但當B再讀C的相同頁面時,雖然也會產生缺頁異常,但是不會從磁盤中復制文件過來,而是直接使用已經保存再內存中的文件數據。

4、適用場景

可用于實現高效的大規模數據傳輸。內存空間不足,是制約大數據操作的一個方面,解決方案往往是借助于硬盤空間的協助,補充內存的不足。但是進一步造成大量的文件I/O操作,極大影響效率。這個問題可以通過mmap映射很好地解決。換句話說,但凡需要磁盤空間代替內存的時候,mmap都可以發揮功效。

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

    關注

    8

    文章

    3055

    瀏覽量

    74329
  • 操作系統
    +關注

    關注

    37

    文章

    6892

    瀏覽量

    123744
  • 函數
    +關注

    關注

    3

    文章

    4346

    瀏覽量

    62972
  • malloc
    +關注

    關注

    0

    文章

    53

    瀏覽量

    82
收藏 人收藏

    評論

    相關推薦

    C語言malloc申請內存時的碎片問題

    解決問題:malloc申請內存的時候,內存碎片問題會導致原本內存大小足夠,卻申請
    發表于 08-06 16:58 ?1689次閱讀
    C語言<b class='flag-5'>malloc</b><b class='flag-5'>申請</b><b class='flag-5'>內存</b>時的碎片問題

    可以在RTOS的任何位置使用malloc申請內存了嗎?

    今天看了CubeIDE 1.7.0的release note ,里面寫的一條新特性是:Thread-safe malloc solution 這是否意味著我可以在RTOS的任何位置使用malloc申請
    發表于 04-03 07:23

    兩種采樣方式

    兩種采樣方式.....................................
    發表于 08-08 15:04

    為什么要使用 malloc()是動態內存分配函數?

    1、相對于自動分配內存,malloc()函數申請分配的內存地址有什么不同?比如:int x[100] int * x = (int *)malloc
    發表于 06-26 16:41

    簡單的內存管理方式

    這些內存,這種方式和我們學習 C 語言時,使用 malloc 和 free 函數來申請內存內存
    發表于 01-14 17:17

    SQL語言的兩種使用方式

    SQL語言的兩種使用方式在終端交互方式下使用,稱為交互式SQL嵌入在高級語言的程序中使用,稱為嵌入式SQL―高級語言如C、Java等,稱為宿主語言嵌入式SQL的實現方式源程序(用主語言
    發表于 12-20 06:51

    程序內存分布RW Size是否包含malloc申請的大小?

    程序內存分布RW Size是否包含malloc申請的大小
    發表于 10-16 06:46

    逆變器的兩種電流型控制方式

    逆變器的兩種電流型控制方式 摘要:研究分析了逆變器的兩種雙環瞬時反饋控制方式——電流型準PWM控制方式和三態DPM
    發表于 07-10 11:21 ?4064次閱讀
    逆變器的<b class='flag-5'>兩種</b>電流型控制<b class='flag-5'>方式</b>

    Wincc如何與PLC進行通訊兩種常用的方式介紹

    西門子WINCC與SiemensPLC通訊連接有多種方式,下面介紹兩種常用的通訊方式
    的頭像 發表于 02-17 09:27 ?3w次閱讀
    Wincc如何與PLC進行通訊<b class='flag-5'>兩種</b>常用的<b class='flag-5'>方式</b>介紹

    關于stm32 MCU申請動態內存malloc的認識

    首先,malloc( )屬于標準C語言函數,當然可以在單片機上使用,如STM32可以先在啟動文件中設置heap的大小,再使用動態內存分配: Heap_Size EQU 0x00000200 \\也就
    發表于 11-18 16:21 ?14次下載
    關于stm32 MCU<b class='flag-5'>申請</b>動態<b class='flag-5'>內存</b><b class='flag-5'>malloc</b>的認識

    記錄單片機使用malloc產生內存泄露的問題及解決方法

    項目場景:單片機使用malloc產生內存泄露的問題問題描述:bug1:創建了一個結構體指針,通過malloc動態開辟內存方式開辟了一段
    發表于 12-03 10:21 ?8次下載
    記錄單片機使用<b class='flag-5'>malloc</b>產生<b class='flag-5'>內存</b>泄露的問題及解決方法

    malloc和free簡介及實現方式說明

    malloc 分配指定大小的內存空間,返回一個指向該空間的指針。大小以字節為單位。返回 void* 指針,需要強制類型轉換后才能引用其中的值。 free 釋放一個由 malloc 所分配的內存
    的頭像 發表于 05-14 09:56 ?4622次閱讀
    <b class='flag-5'>malloc</b>和free簡介及實現<b class='flag-5'>方式</b>說明

    在MATLAB/simulink中建模時的兩種不同實現方式

    導讀:本期文章主要介紹在MATLAB/simulink中建模時的兩種不同實現方式,一是直接用現成的文件庫中的模塊進行搭建,一是用Sfunction代碼實現。接下來以電壓型磁鏈觀測器
    的頭像 發表于 09-15 10:07 ?1978次閱讀

    MATLAB/simulink中兩種實現建模方式的優勢

    導讀:本期文章主要介紹在MATLAB/simulink中建模時的兩種不同實現方式,一是直接用現成的文件庫中的模塊進行搭建,一是用Sfunction代碼實現。接下來以電壓型磁鏈觀測器
    的頭像 發表于 09-15 10:10 ?5210次閱讀

    什么是堆內存?存儲方式是什么樣的?

    的存儲方式。 C 代碼中動態申請內存申請函數是 malloc ,常見的內存代碼如下圖所示:
    的頭像 發表于 06-22 10:29 ?1231次閱讀
    什么是堆<b class='flag-5'>內存</b>?存儲<b class='flag-5'>方式</b>是什么樣的?
    主站蜘蛛池模板: 天天操精品 | 天天摸天天澡天天碰天天弄 | 亚洲精品欧洲久久婷婷99 | 资源视频在线观看 | 天天躁夜夜 | 国产尤物在线视频 | 四虎影院永久网址 | 国产青草 | 亚洲福利一区二区三区 | 国产乱理论片在线观看理论 | 成人的天堂视频一区二区三区 | 国产一级特黄全黄毛片 | 欧美高清成人videosex | 国产成人毛片视频不卡在线 | 婷婷丁香四月 | 亚洲欧美国产视频 | 国产caoni111在线观看视频 | 免费黄色欧美 | 天堂在线中文无弹窗全文阅读 | 福利社藏经阁 | 国产精品成人在线播放 | 中日韩精品视频在线观看 | 久久99精品久久久久久秒播 | brazzersvideosexhd欧美高清 | 婷婷 综合网站 | 成人免费的性色视频 | 亚洲狠狠97婷婷综合久久久久 | 99久久无色码中文字幕 | 一级毛片西西人体44rt高清 | 精品一区二区影院在线 | 月夜免费观看完整视频 | 黄视频在线观看免费 | 日本天堂影院在线播放 | 美女色黄一男一女 | 神马午夜在线观看 | 手机看片1024精品日韩 | 亚洲入口 | 天天综合亚洲国产色 | 天天操天天擦 | 天天艹天天操 | 717影院理论午夜伦不卡久久 |