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

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

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

3天內不再提示

內存剖析:從用戶態到內核態內存都做了什么?

Linux閱碼場 ? 來源:Linux閱碼場 ? 作者:Linux閱碼場 ? 2023-01-06 11:04 ? 次閱讀

編者按:本文順著c++關鍵字new向下,旨在分析介紹底層各層到底做了什么,為什么這么做。

1.c++用戶層

1.1提供的接口

1.1.1new

l 調用operator new 從自由存儲區分配一塊足夠大的內存(sizeof(結構))

l 調用相應的構造函數

l 構造完成后返回指向該對象的指針

1.1.2delete

l 調用相應的析構函數

l 調用operator delete將內存歸還給自由存儲區

1.1.3new數組

l 調用operator new[] 從自由存儲區分配一塊足夠大的內存(sizeof(結構)+用區分對象數組指針和對象指針以及對象數組大小的額外數據),注意簡單對象(即不需要構造函數的類型)將不會有額外數據的申請。

l 依次在內存中調用相應的構造函數

l 構造完成后返回指向該對象數組的起始地址,不包括前面的額外數據部分。

1.1.4delete數組

l 獲取數組起始地址前面的額外數據,計算出數組長度

l 根據數據長度依次調用相應的析構函數

l調用operator delete將內存歸還給自由存儲區

1.2operator new 的三種形式

形式1.void* operator new (std::size_t size)throw (std::bad_alloc);

形式2.void* operator new (std::size_t size,const std::nothrow_t& nothrow_value) throw();

形式3.void* operator new (std::size_t size,void* ptr) throw();

形式1跟形式2的區別僅僅是是否拋出異常,當分配失敗時,前者會拋出bad_alloc異常,后者返回NULL,不會拋出異常。它們都分配一個固定大小的連續內存。

形式3又被稱為placement new,它多接收一個ptr參數,并且只是簡單地返回該ptr。調用形式為 A* a=new(ptr)A()。在內存池中有廣泛應用,ptr即來自自由存儲區,可以是堆、?;蛘哳A分配的內存塊。

上述形式1和形式2都可以被重載,遵循作用域覆蓋原則,即在里向外尋找operator new的重載時,只要找到operator new()函數就不再向外查找,如果參數符合則通過,如果參數不符合則報錯,而不管全局是否還有相匹配的函數原型。

注意在形式1中,如果new分配異常,將拋出異常導致后續代碼不能被正常執行。即如果在new操作后有解鎖操作,該解鎖操作將不會執行導致死鎖。

1.3設定內存分配失敗入口函數

poYBAGO3kQCAC5egAACcaTL7jkU638.jpg

1.4自由存儲區和堆的區別

從技術上來說,堆是C語言操作系統的術語。堆是操作系統所維護的一塊特殊內存,它提供了動態分配的功能,當運行程序調用malloc()時就會從中分配,稍后調用free可把內存交還。而自由存儲是C++中通過new和delete動態分配和釋放對象的抽象概念,通過new來申請的內存區域可稱為自由存儲區?;旧?,所有的C++編譯器默認使用堆來實現自由存儲,也即是缺省的全局運算符new和delete也許會按照malloc和free的方式來被實現,這時藉由new運算符分配的對象,說它在堆上也對,說它在自由存儲區上也正確。但程序員也可以通過重載操作符,改用其他內存來實現自由存儲,例如全局變量做的對象池,這時自由存儲區就區別于堆了。

我們只需要記?。憾咽遣僮飨到y維護的一塊內存,而自由存儲是C++中通過new與delete動態分配和釋放對象的抽象概念。堆與自由存儲區并不等價。這種區分大概是不同語言背景造成的。

1.5默認內存初始值

在vs2008(32bit)的debug模式下,由堆分配的內存初始值為0xcdcd,中文“屯”;由棧分配的內存初始值為0xcccc,中文“燙”。

1.6重載::operator new的理由

l 定位檢查代碼中內存錯誤

l 優化內存分配性能

l 獲得內存使用統計數據

1.7重載::operator new的兩種方式

方式1:不改變簽名,替換系統現有版本

void* operator new(size_t size);

void operator delete(void* p);

使用方不需要包含任何特殊的頭文件,也就是說不需要看見這兩個函數聲明。“性能優化”通常用這種方式。

方式2:增加新參數

// 其返回的指針必須能被普通的 ::operator delete(void*) 釋放

void* operator new(size_t size, const char* file, int line);

Foo* p = new (__FILE, __LINE__) Foo;

也可以用宏替換 new 來節省打字。此種方式使用方需要看到這兩個函數聲明,也就是說要主動包含提供的頭文件。“檢測內存錯誤”和“統計內存使用情況”通常會用這種方式重載。

1.8重載::operator new的困境

1.8.1絕不能在library中重載::operator new

如果以上文提到的方式1來重載全局的::operator new,非常具有侵略性。使用該library的程序被迫使用了被重載的::operator new,并且一旦有另外的library也同樣重載了::operator new,就將會導致鏈接問題。

那么如果采用上文提到的方式2來額外提供一個::operator new 版本呢,那就需要考慮重載后的::operator new 返回的指針能否被系統默認的::operator delete釋放。如果不兼容系統則需要以方式1重載::operator new ,回到了上文提過的問題。如果兼容,那么在新版本的::operator new中能做的事比較有限,比如不能額外申請內存記錄統計信息,除非定義一個包含統計信息的基類來作為所有申請對象的父類,但這樣就相當于設定了開發規范,稍有不注意可能就會出錯。

1.8.2使用重載帶新參數的版本會有什么影響

如果使用方式1重載::operator new 使用起來似乎沒有什么問題,但要考慮上節中提到的鏈接問題。

如果使用方式2來重載::operator new,分成以下兩種場合。

對于以頭文件形式提供的library,可以在所有的cpp實現文件起始部分包含重載::operator new 的頭文件,但這具有侵略性。

對于以頭文件加二進制庫提供的library,實際上帶新參數的版本并不會被這些庫使用。

1.9單獨為特定類重載成員函數operator new怎么樣

與全局 ::operator new() 不同,per-class operator new() 和 operator delete () 的影響面要小得多,它只影響本 class 及其派生類。似乎重載 member operator new() 是可行的。但是我并不贊同這種做法。

如果一個類需要重載成員函數operator new(),說明它用到了特殊的內存分配策略,常見的情況是使用了內存池或對象池。寧愿把這一事實明顯地擺出來,而不是改變 new的默認行為。

這可以歸結為最小驚訝原則:如果我們在代碼里讀到 Node* p = new Node,通常我們會認為它在堆上分配了內存,如果 Node 類重載了成員函數operator new(),那么就需要事先仔細閱讀 node.h 才能發現其實這行代碼使用了私有的內存池。為什么不寫得明確一點呢?如果寫成Node*p = Node::createNode(),那么我們可能能猜到 Node::createNode() 肯定做了什么與 new不一樣的事情,免得將來大吃一驚。

1.10代替重載::operator new的方案

從glibc的malloc入手,替換掉malloc。具體方式參考tcmalloc中的override方式,點此鏈接[1]。

主要使用了gcc提供的alias別名屬性和weak屬性,我們能實現替換掉系統默認的malloc原因在于系統提供的malloc系列函數都是被weak屬性修飾的。

對于全局函數,如果沒有顯示修飾稱weak屬性,那么他屬于強符號;對于全局變量,已初始化完畢的屬于強符號,沒有初始化完畢的則屬于弱符號。

有如下3點規則:

l 鏈接時強弱符號都存在時以強符號為準;

l 鏈接時如果只有弱符號時以弱符號為準;

l 鏈接時如兩個都是弱符號,則以內存占用大小較大的那個符號為準;

2.glibc層

2.1概述

實際上glibc采用了一種批發和零售的方式來管理內存。glibc每次通過系統調用的方式申請一大塊內存(虛擬內存),當進程申請內存時,glibc就從自己獲得的內存中取出一塊給進程。

glibc對于heap內存申請大于128k的內存申請,glibc采用mmap的方式向內核申請內存,也就是此時的malloc是由mmap來實現的,這不能保證內存地址向上增長;小于128k的則采用brk,malloc調用系統調用brk來實現向內核批發虛擬內存,對于它來講是正確的。128k的閥值,可以通過glibc的庫函數進行設置。

審核編輯:湯梓紅

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

    關注

    3

    文章

    1379

    瀏覽量

    40353
  • 內存
    +關注

    關注

    8

    文章

    3043

    瀏覽量

    74194
  • 函數
    +關注

    關注

    3

    文章

    4344

    瀏覽量

    62843
  • C++
    C++
    +關注

    關注

    22

    文章

    2114

    瀏覽量

    73764

原文標題:內存剖析:從用戶態到內核態內存都做了什么?

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Linux 內存管理知識學習經驗總結

    內核用戶兩部分,經典比例如下:用戶
    發表于 02-25 17:08

    Linux內核下如何讀寫IIC

    目前在Linux3.12上,想在內核下讀取LM75溫度傳感器的溫度值,做了如下操作,但是讀數據的時候i2c_transfer一直報錯。先將LM75設備掛到IIC總線上:在sys下可以發現已經添加成功:但是讀數據的時候就一直報錯
    發表于 11-29 19:07

    Linux內存系統:內存使用場景

    文件映射、共享內存)· 程序的內存 map(棧、堆、code、data)· 內核用戶的數據傳遞(copy_from_user、copy
    發表于 08-25 07:42

    Linux內存系統---走進Linux 內存

    內存區域· MMAP:共享庫及匿名文件的映射區域· STACK:用戶進程棧7、內核地址空間 · 直接映射區:線性空間中 3G 開始最大
    發表于 08-26 08:05

    操作系統為什么分內核用戶?這兩者如何切換?

    操作系統為什么分內核用戶,這兩者如何切換?進程在地址空間會劃分為哪些區域?堆和棧有什么區別?
    發表于 07-23 09:01

    請問CPU與寄存器,內核用戶及如何切換?

    計算機硬件系統由哪幾部分構成?編程語言的作用及與操作系統和硬件的關系是什么?請問CPU與寄存器,內核用戶及如何切換?
    發表于 10-25 06:31

    OpenHarmony喂狗源碼解讀之用戶源碼

    timeout\n"); } else {// 用戶設置喂狗超時時間為大于gap 用戶喂狗間隔時間為// 獲取內核的超時間 - gap
    發表于 01-26 10:57

    鴻蒙內核實現用戶快速互斥鎖Futex設計資料合集

    Futex(Fast userspace mutex,用戶快速互斥鎖),系列篇簡稱 快鎖 ,是一個在 Linux 上實現鎖定和構建高級抽象鎖如信號量和POSIX互斥的基本工具,它第一次出現在
    發表于 03-23 14:12

    Linux虛擬內存和物理內存的深刻分析

    內存,用戶進程總是先獲得一個虛擬內存區的使用權,最終通過缺頁異常獲得一塊真正的物理內存。物理內存內核
    發表于 05-31 08:00

    一個內核Key-Value存儲系統

    的數據存儲需求中尤為突出。針對該問題,給出了一個內核Key-Value存儲系統的實現-KStore:提供內核空間的索引和內存分配機制,并在此基礎上,通過基于
    發表于 01-19 16:37 ?0次下載
    一個<b class='flag-5'>內核</b><b class='flag-5'>態</b>Key-Value存儲系統

    詳解Linux的物理內存

    內核申請內存比在用戶申請內存要更為直接,它沒有采用用戶
    的頭像 發表于 01-18 17:45 ?2446次閱讀
    詳解Linux的物理<b class='flag-5'>內存</b>

    Linux內核缺頁會發生什么 - 玩轉Exception fixup表

    Linux內核的做法是提供了一張 異常處理表 ,使用專有的函數來訪問用戶內存。類似 try-catch塊一般。具體詳情可參見copy_to_user/copy_from_user的實
    的頭像 發表于 06-03 15:08 ?2990次閱讀

    探究slab在內核內存管理和用戶Memcached的雙重存在

    ,但是作為內核的堆用戶本身,經常只是調用kmalloc()申請一個小內存,或者調用kmem_cache_alloc()申請一個數據結構,2^n頁給它,會形成大量碎片浪費。所以slab找buddy要了2
    的頭像 發表于 08-13 14:55 ?1484次閱讀
    探究slab在<b class='flag-5'>內核</b><b class='flag-5'>內存</b>管理和<b class='flag-5'>用戶</b><b class='flag-5'>態</b>Memcached的雙重存在

    Linux內核用戶是如何睡眠的

    clock_nanosleep系統調用來進行睡眠(也就是說用戶任務睡眠需要調用系統調用陷入內核)。 下面我們來研究下clock_nanosleep的實現(這里集中睡眠的實現,先忽略
    的頭像 發表于 08-16 15:06 ?1940次閱讀

    如何實現一個高性能內存

    ,按照慣例先說內存池的應用場景。 為什么我們需要內存池? 因為malloc等分配內存的方式,需要涉及系統調用sbrk,頻繁的malloc和free會消耗系統資源。 既然如此,我們就預
    的頭像 發表于 11-10 11:11 ?723次閱讀
    如何實現一個高性能<b class='flag-5'>內存</b>池
    主站蜘蛛池模板: 美女扒开尿口给男人捅| 亚洲国产精品丝袜在线观看| 亚洲综合激情| 天天天天做夜夜夜做| 天天操天天噜| 国内黄色录像| 欧美在线性| 午夜视频网址| 一级做a爱| 五月婷婷六月天| 日韩欧美不卡片| 美女被拍拍拍拍拍拍拍拍| 国产性片在线| 亚洲大黑香蕉在线观看75| 奇米777狠狠| 亚洲haose在线观看| 天天躁夜夜躁狠狠躁2024| 在线视频人人视频www| 四虎最新紧急入口| 免费一级视频在线播放| 大黄香蕉| 欧美jizz大又粗| 黄色的视频网站| 成年人污视频| 5月婷婷6月丁香| 天天爽夜夜爽夜夜爽| 国产亚洲papapa| 亚洲一区二区免费看| 在线国产高清| 人操人摸| 4hc44四虎www亚洲| 国产精品天天操| 男人j进入女人j在线视频| 色五月在线视频| 国产成人精品亚洲日本在线| 日日干干夜夜| 84pao强力永久免费高清| 亚洲三级免费观看| 久久黄色精品视频| 色橹橹| 亚洲免费视频播放|