商湯2019年秋招面經總結

介紹:

該博客對商湯2019年秋招開發崗所見大部分面經進行了整理。對大多數題目進行了答案的查找。當然沒有做到盡善盡美,所以有些問題還是需要讀者自行查找體會。

目錄

算法

SQL&&NOSQL

網絡

設計模式

python

linux&&併發

 內存管理

C++

git


算法

各種排序,重點快排,堆排(複雜度、穩定性)

兩個很大的身份證集,找出重複的身份證(屬於大數據集求交集)

哈希表的實現

給出很多關係對,求出關係鏈數(不知道是啥)

兩個鏈表的第一個公共節點(劍指offer原題)

二分查找(比較基礎,當數據集有序或者時間複雜度爲log時應該考慮二分查找)

手寫實現求兩個集合A, B 的差集,<A-B>和<B-A>都要給出

一個普通list  翻轉一下。不需要寫代碼,思路說出來就行。除了依靠第三方變量還有改指針

實現一個二叉樹的插入節點和刪除節點。

數組最大子串累加和動態規劃經典題目

求一個數組中累加和在一個區間的種數

top k劍指offer原題

深度搜索廣度搜索

查找一個無序數組中的中位數,要最優時間複雜度。

最長無重複字符子串,要最優時間複雜度。

揹包

LRU緩存

總結:(算法不難,劍指offer,常見算法以及leetcode中medium及以下難度)

 

SQL&&NOSQL

SQL 和 noSQL 的區別,nosql場景 sql場景

Redis 的應用,場景

Kafka 的原理與特點

redis過期策略是怎麼實現的呢

mysql存儲引擎,更適合做哪種應用

哪些情況下索引會失效

建立索引需要注意哪些情況

數據庫集羣之間怎麼保持同步

若不一致會有什麼問題(扯了CAP)

網絡

TCP和UDP

常用的狀態響應碼

soket阻塞模式 阻塞和非阻塞的優缺點

new發生了什麼this對象的指向類別

http1.0 和 http2.0 的區別

https 的詳細過程,從握手到SSL協議,爲什麼https並沒有得到大規模應用

http 三次握手及細節,和https握手

如果用UDP,應該在應用層做什麼

設計UDP應用層重傳策略

cookie和sessionStorage、localStorage的區別

下面的都看看吧,一般網絡方面就是這些了

http請求全過程

https://blog.csdn.net/yezitoo/article/details/78193794

常見面試題

https://blog.csdn.net/weiyuefei/article/details/50413543

https://blog.csdn.net/chencheng126/article/details/44407901

https://blog.csdn.net/u014800094/article/details/60591852

DNS

https://www.cnblogs.com/morethink/p/8120327.html

https://blog.csdn.net/yipiankongbai/article/details/24025633

https://www.cnblogs.com/xymqx/p/4442465.html

TCP首部TCP僞首部

get和post區別

 

(8)、從輸入網址到顯示網頁,過程

http://blog.csdn.net/donggx/article/details/71402871?locationNum=1&fps=1

http://blog.csdn.net/xiangriikui/article/details/52207153

TCP和UDP邊界性區別

HTTP和HTTPS的區別https://www.zhihu.com/question/19577317

(9)、http的格式

HTTP請求方法

tcp三次握手四次揮手(及原因)詳解

HTTP請求、響應報文格式

https://www.jianshu.com/p/8fe93a14754c

(10)、http有狀態嗎

與HTTP無狀態請求相對的有狀態請求的例子? - 知乎 https://www.zhihu.com/question/29731567

http協議無狀態中的 "狀態" 到底指的是什麼?! https://www.cnblogs.com/bellkosmos/p/5237146.html

https://blog.csdn.net/chollima/article/details/38864701

(11)、cookie是什麼?

https://jingyan.baidu.com/article/4f7d5712a67b671a2019278f.html

(12)、session是什麼?

http://blog.csdn.net/sixteen_cicle/article/details/52355332

http://blog.csdn.net/hjc1984117/article/details/53995816

和cookie的區別https://www.zhihu.com/question/19786827

(13)tcp和udp區別

http://blog.csdn.net/li_ning_/article/details/52117463

設計模式

單例模式注意線程安全

所有的設計模式,C++實現

MVC模式是怎麼交互的

手寫一個工廠類方法,並在它的基礎上給出裝飾者模式

實現在一個方法A的調用之前都會先調用方法B, 不允許更改接口和調用方式(鉤子和職責鏈)

手寫一個觀察者模式,然後再說一下怎麼實現發佈訂閱模式(不能依賴任何第三方框架或已有的實現)

python

Python有哪些數據類型。分別寫出。 list與tuple的區別

python: 1.深拷貝和淺拷貝copy()函數是深拷貝還是淺拷貝 2.python生成器

linux&&併發

進程線程

http://blog.csdn.net/yaosiming2011/article/details/44280797

https://blog.csdn.net/zhou753099943/article/details/51771220

https://blog.csdn.net/yanxiaolx/article/details/51763372

https://blog.csdn.net/dazhong159/article/details/7896070

讓你設計一個cache如何設計(算法裏的LRU?)

介紹多線程的實現方式

如何設計緩存策略

 內存管理

講一下虛擬地址如何映射成物理地址,TLB瞭解麼,分頁機制說一下,頁面大小是多少。(內存管理)

虛擬內存

6、什麼是虛擬內存?

解析:是將進程部分裝入內存中,從而能實現一個很大的程序能在一個比它小的內存中運行,它的主要實現是靠程序的換進換出來實現的,因爲內存中0~3G是用戶使用,3~4G纔是內存使用,通過映射來實現來進行邏輯地址到物理地址的映射

 

7、虛擬地址、邏輯地址、線性地址、物理地址的區別?

解析: 分段機制把一個邏輯地址轉換爲線性地址;接着,分頁機制把一個線性地址轉換爲物理地址。

(1)虛擬地址:虛擬內存映射出來的地址

(2)邏輯地址:程序的段加偏移量形成的,C/C++程序中取地址求出來的地址就是邏輯地址

(3)線性地址:是邏輯地址到物理地址的中間層,只有啓動分頁機制的時候纔有線性地址,如果沒有分頁機制,那麼線性地址就是物理地址

(4)物理地址:是內存中實實在在存在的硬件地址,

邏輯地址(啓動分段)--》線性地址(啓動分頁)--》物理地址

內存存在的問題:

1.進程地址空間不能隔離

  由於程序直接訪問的是物理內存,這個時候程序所使用的內存空間不是隔離的。舉個例子,就像上面說的A的地址空間是0-10M這個範圍內,但是如果A中有一段代碼是操作10M-128M這段地址空間內的數據,那麼程序B和程序C就很可能會崩潰(每個程序都可以訪問系統的整個地址空間)。這樣很多惡意程序或者是木馬程序可以輕而易舉地破快其他的程序,系統的安全性也就得不到保障了,這對用戶來說也是不能容忍的。

  2. 內存使用的效率低

  如上面提到的,如果我們要像讓程序A、B、C同時運行,那麼唯一的方法就是使用虛擬內存技術將一些程序暫時不用的數據寫到磁盤上,在需要的時候再從磁盤讀回內存。這裏程序C要運行,將A交換到磁盤上去顯然是不行的,因爲程序是需要連續的地址空間的,程序C需要20M的內存,而A只有10M的空間,所以需要將程序B交換到磁盤上去,而B足足有100M,可以看到爲了運行程序C我們需要將100M的數據從內存寫到磁盤,然後在程序B需要運行的時候再從磁盤讀到內存,我們知道IO操作比較耗時,所以這個過程效率將會十分低下。

  3. 程序運行的地址不能確定

  程序每次需要運行時,都需要在內存中分配一塊足夠大的空閒區域,而問題是這個空閒的位置是不能確定的,這會帶來一些重定位的問題,重定位的問題確定就是程序中引用的變量和函數的地址

內存管理無非就是想辦法解決上面三個問題,如何使進程的地址空間隔離,如何提高內存的使用效率,如何解決程序運行時的重定位問題

虛擬內存

現在的內存管理方法就是在程序和物理內存之間引入了虛擬內存這個概念。虛擬內存位於程序和物理內存之間,程序只能看見虛擬內存,再也不能直接訪問物理內存。每個程序都有自己獨立的進程地址空間,這樣就做到了進程隔離。這裏的進程地址空間是指虛擬地址。顧名思義,既然是虛擬地址,也就是虛的,不是現實存在的地址空間。

 既然我們在程序和物理地址空間之間增加了虛擬地址,那麼就要解決怎麼從虛擬地址映射到物理地址,因爲程序最終肯定是運行在物理內存中的,主要有分段和分頁兩種技術。

 分段(Segmentation):這種方法是人們最開始使用的一種方法,基本思路是將程序所需要的內存地址空間大小的虛擬空間映射到某個物理地址空間。

  段映射機制

  每個程序都有其獨立的虛擬的獨立的進程地址空間,可以看到程序A和B的虛擬地址空間都是從0x00000000開始的。我們將兩塊大小相同的虛擬地址空間和實際物理地址空間一一映射,即虛擬地址空間中的每個字節對應於實際地址空間中的每個字節,這個映射過程由軟件來設置映射的機制,實際的轉換由硬件來完成。

  這種分段的機制解決了文章一開始提到的3個問題中的進程地址空間隔離和程序地址重定位的問題。程序A和程序B有自己獨立的虛擬地址空間,而且該虛擬地址空間被映射到了互相不重疊的物理地址空間,如果程序A訪問虛擬地址空間的地址不在0x00000000-0x00A00000這個範圍內,那麼內核就會拒絕這個請求,所以它解決了隔離地址空間的問題。我們應用程序A只需要關心其虛擬地址空間0x00000000-0x00A00000,而其被映射到哪個物理地址我們無需關心,所以程序永遠按照這個虛擬地址空間來放置變量,代碼,不需要重新定位。

  無論如何分段機制解決了上面兩個問題,是一個很大的進步,但是對於內存效率問題仍然無能爲力。因爲這種內存映射機制仍然是以程序爲單位,當內存不足時仍然需要將整個程序交換到磁盤,這樣內存使用的效率仍然很低。那麼,怎麼纔算高效率的內存使用呢。事實上,根據程序的局部性運行原理,一個程序在運行的過程當中,在某個時間段內,只有一小部分數據會被經常用到。所以我們需要更加小粒度的內存分割和映射方法,此時是否會想到Linux中的Buddy算法和slab內存分配機制呢,哈哈。另一種將虛擬地址轉換爲物理地址的方法分頁機制應運而生了。

  分頁機制:

  分頁機制就是把內存地址空間分爲若干個很小的固定大小的頁,每一頁的大小由內存決定,就像Linux中ext文件系統將磁盤分成若干個Block一樣,這樣做是分別是爲了提高內存和磁盤的利用率。試想一下,如果將磁盤空間分成N等份,每一份的大小(一個Block)是1M,如果我想存儲在磁盤上的文件是1K字節,那麼其餘的999字節是不是浪費了。所以需要更加細粒度的磁盤分割方式,我們可以將Block設置得小一點,這當然是根據所存放文件的大小來綜合考慮的,好像有點跑題了,我只是想說,內存中的分頁機制跟ext文件系統中的磁盤分割機制非常相似。

  Linux中一般頁的大小是4KB,我們把進程的地址空間按頁分割,把常用的數據和代碼頁裝載到內存中,不常用的代碼和數據保存在磁盤中,我們還是以一個例子來說明,如下圖:

進程虛擬地址空間、物理地址空間和磁盤之間的頁映射關係

  我們可以看到進程1和進程2的虛擬地址空間都被映射到了不連續的物理地址空間內(這個意義很大,如果有一天我們的連續物理地址空間不夠,但是不連續的地址空間很多,如果沒有這種技術,我們的程序就沒有辦法運行),甚至他們共用了一部分物理地址空間,這就是共享內存。

  進程1的虛擬頁VP2和VP3被交換到了磁盤中,在程序需要這兩頁的時候,Linux內核會產生一個缺頁異常,然後異常管理程序會將其讀到內存中。

  這就是分頁機制的原理,當然Linux中的分頁機制的實現還是比較複雜的,通過了頁全局目錄,頁上級目錄,頁中級目錄,頁表等幾級的分頁機制來實現的,但是基本的工作原理是不會變的。

  分頁機制的實現需要硬件的實現,這個硬件名字叫做MMU(Memory Management Unit),他就是專門負責從虛擬地址到物理地址轉換的,也就是從虛擬頁找到物理頁。

因爲linux內核中內存管理中:所有的段的基地址均爲0,即每個段的邏輯地址與線性地址保持一致(即邏輯地址的偏移量值與線性線性的地址值相同),而完成利用了分頁機制。

虛擬地址又叫線性地址。linux沒有采用分段機制,所以邏輯地址和虛擬地址(線性地址)(在用戶態,內核態邏輯地址專指下文說的線性偏移前的地址)是一個概念。物理地址自不必提。內核的虛擬地址和物理地址,大部分只差一個線性偏移量。用戶空間的虛擬地址和物理地址則採用了多級頁表進行映射,但仍稱之爲線性地址。

[地址映射](圖:左中)

linux內核使用頁式內存管理,應用程序給出的內存地址是虛擬地址,它需要經過若干級頁表一級一級的變換,才變成真正的物理地址。

想一下,地址映射還是一件很恐怖的事情。當訪問一個由虛擬地址表示的內存空間時,需要先經過若干次的內存訪問,得到每一級頁表中用於轉換的頁表項(頁表是存放在內存裏面的),才能完成映射。也就是說,要實現一次內存訪問,實際上內存被訪問了N+1次(N=頁表級數),並且還需要做N次加法運算。

所以,地址映射必須要有硬件支持,mmu(內存管理單元)就是這個硬件。並且需要有cache來保存頁表,這個cache就是TLB(Translation lookaside buffer)。

儘管如此,地址映射還是有着不小的開銷。假設cache的訪存速度是內存的10倍,命中率是40%,頁表有三級,那麼平均一次虛擬地址訪問大概就消耗了兩次物理內存訪問的時間。

於是,一些嵌入式硬件上可能會放棄使用mmu,這樣的硬件能夠運行VxWorks(一個很高效的嵌入式實時操作系統)、linux(linux也有禁用mmu的編譯選項)、等系統。

但是使用mmu的優勢也是很大的,最主要的是出於安全性考慮。各個進程都是相互獨立的虛擬地址空間,互不干擾。而放棄地址映射之後,所有程序將運行在同一個地址空間。於是,在沒有mmu的機器上,一個進程越界訪存,可能引起其他進程莫名其妙的錯誤,甚至導致內核崩潰。

在地址映射這個問題上,內核只提供頁表,實際的轉換是由硬件去完成的。那麼內核如何生成這些頁表呢?這就有兩方面的內容,虛擬地址空間的管理和物理內存的管理。(實際上只有用戶態的地址映射才需要管理,內核態的地址映射是寫死的。)

 

[虛擬地址管理](圖:左下)

每個進程對應一個task結構,它指向一個mm結構,這就是該進程的內存管理器。(對於線程來說,每個線程也都有一個task結構,但是它們都指向同一個mm,所以地址空間是共享的。)

mm->pgd指向容納頁表的內存,每個進程有自已的mm,每個mm有自己的頁表。於是,進程調度時,頁表被切換(一般會有一個CPU寄存器來保存頁表的地址,比如X86下的CR3,頁表切換就是改變該寄存器的值)。所以,各個進程的地址空間互不影響(因爲頁表都不一樣了,當然無法訪問到別人的地址空間上。但是共享內存除外,這是故意讓不同的頁表能夠訪問到相同的物理地址上)。

用戶程序對內存的操作(分配、回收、映射、等)都是對mm的操作,具體來說是對mm上的vma(虛擬內存空間)的操作。這些vma代表着進程空間的各個區域,比如堆、棧、代碼區、數據區、各種映射區、等等。

用戶程序對內存的操作並不會直接影響到頁表,更不會直接影響到物理內存的分配。比如malloc成功,僅僅是改變了某個vma,頁表不會變,物理內存的分配也不會變。

假設用戶分配了內存,然後訪問這塊內存。由於頁表裏面並沒有記錄相關的映射,CPU產生一次缺頁異常。內核捕捉異常,檢查產生異常的地址是不是存在於一個合法的vma中。如果不是,則給進程一個"段錯誤",讓其崩潰;如果是,則分配一個物理頁,併爲之建立映射。

 

[物理內存管理](圖:右上)

那麼物理內存是如何分配的呢?

首先,linux支持NUMA(非均質存儲結構),物理內存管理的第一個層次就是介質的管理。pg_data_t結構就描述了介質。一般而言,我們的內存管理介質只有內存,並且它是均勻的,所以可以簡單地認爲系統中只有一個pg_data_t對象。

每一種介質下面有若干個zone。一般是三個,DMA、NORMAL和HIGH。

DMA:因爲有些硬件系統的DMA總線比系統總線窄,所以只有一部分地址空間能夠用作DMA,這部分地址被管理在DMA區域(這屬於是高級貨了);

HIGH:高端內存。在32位系統中,地址空間是4G,其中內核規定3~4G的範圍是內核空間,0~3G是用戶空間(每個用戶進程都有這麼大的虛擬空間)(圖:中下)。前面提到過內核的地址映射是寫死的,就是指這3~4G的對應的頁表是寫死的,它映射到了物理地址的0~1G上。(實際上沒有映射1G,只映射了896M。剩下的空間留下來映射大於1G的物理地址,而這一部分顯然不是寫死的)。所以,大於896M的物理地址是沒有寫死的頁表來對應的,內核不能直接訪問它們(必須要建立映射),稱它們爲高端內存(當然,如果機器內存不足896M,就不存在高端內存。如果是64位機器,也不存在高端內存,因爲地址空間很大很大,屬於內核的空間也不止1G了);

NORMAL:不屬於DMA或HIGH的內存就叫NORMAL。

在zone之上的zone_list代表了分配策略,即內存分配時的zone優先級。一種內存分配往往不是只能在一個zone裏進行分配的,比如分配一個頁給內核使用時,最優先是從NORMAL裏面分配,不行的話就分配DMA裏面的好了(HIGH就不行,因爲還沒建立映射),這就是一種分配策略。

每個內存介質維護了一個mem_map,爲介質中的每一個物理頁面建立了一個page結構與之對應,以便管理物理內存。

每個zone記錄着它在mem_map上的起始位置。並且通過free_area串連着這個zone上空閒的page。物理內存的分配就是從這裏來的,從 free_area上把page摘下,就算是分配了。(內核的內存分配與用戶進程不同,用戶使用內存會被內核監督,使用不當就"段錯誤";而內核則無人監督,只能靠自覺,不是自己從free_area摘下的page就不要亂用。)

 

[建立地址映射]

內核需要物理內存時,很多情況是整頁分配的,這在上面的mem_map中摘一個page下來就好了。比如前面說到的內核捕捉缺頁異常,然後需要分配一個page以建立映射。

說到這裏,會有一個疑問,內核在分配page、建立地址映射的過程中,使用的是虛擬地址還是物理地址呢?首先,內核代碼所訪問的地址都是虛擬地址,因爲CPU指令接收的就是虛擬地址(地址映射對於CPU指令是透明的)。但是,建立地址映射時,內核在頁表裏面填寫的內容卻是物理地址,因爲地址映射的目標就是要得到物理地址。

那麼,內核怎麼得到這個物理地址呢?其實,上面也提到了,mem_map中的page就是根據物理內存來建立的,每一個page就對應了一個物理頁。

於是我們可以說,虛擬地址的映射是靠這裏page結構來完成的,是它們給出了最終的物理地址。然而,page結構顯然是通過虛擬地址來管理的(前面已經說過,CPU指令接收的就是虛擬地址)。那麼,page結構實現了別人的虛擬地址映射,誰又來實現page結構自己的虛擬地址映射呢?沒人能夠實現。

這就引出了前面提到的一個問題,內核空間的頁表項是寫死的。在內核初始化時,內核的地址空間就已經把地址映射寫死了。page結構顯然存在於內核空間,所以它的地址映射問題已經通過“寫死”解決了。

由於內核空間的頁表項是寫死的,又引出另一個問題,NORMAL(或DMA)區域的內存可能被同時映射到內核空間和用戶空間。被映射到內核空間是顯然的,因爲這個映射已經寫死了。而這些頁面也可能被映射到用戶空間的,在前面提到的缺頁異常的場景裏面就有這樣的可能。映射到用戶空間的頁面應該優先從HIGH區域獲取,因爲這些內存被內核訪問起來很不方便,拿給用戶空間再合適不過了。但是HIGH區域可能會耗盡,或者可能因爲設備上物理內存不足導致系統裏面根本就沒有HIGH區域,所以,將NORMAL區域映射給用戶空間是必然存在的。

但是NORMAL區域的內存被同時映射到內核空間和用戶空間並沒有問題,因爲如果某個頁面正在被內核使用,對應的page應該已經從free_area被摘下,於是缺頁異常處理代碼中不會再將該頁映射到用戶空間。反過來也一樣,被映射到用戶空間的page自然已經從free_area被摘下,內核不會再去使用這個頁面。

 

[內核空間管理](圖:右下)

除了對內存整頁的使用,有些時候,內核也需要像用戶程序使用malloc一樣,分配一塊任意大小的空間。這個功能是由slab系統來實現的。

slab相當於爲內核中常用的一些結構體對象建立了對象池,比如對應task結構的池、對應mm結構的池、等等。

而slab也維護有通用的對象池,比如"32字節大小"的對象池、"64字節大小"的對象池、等等。內核中常用的kmalloc函數(類似於用戶態的malloc)就是在這些通用的對象池中實現分配的。

slab除了對象實際使用的內存空間外,還有其對應的控制結構。有兩種組織方式,如果對象較大,則控制結構使用專門的頁面來保存;如果對象較小,控制結構與對象空間使用相同的頁面。

除了slab,linux 2.6還引入了mempool(內存池)。其意圖是:某些對象我們不希望它會因爲內存不足而分配失敗,於是我們預先分配若干個,放在mempool中存起來。正常情況下,分配對象時是不會去動mempool裏面的資源的,照常通過slab去分配。到系統內存緊缺,已經無法通過slab分配內存時,纔會使用 mempool中的內容。

 

[頁面換入換出](圖:左上)(圖:右上)

頁面換入換出又是一個很複雜的系統。內存頁面被換出到磁盤,與磁盤文件被映射到內存,是很相似的兩個過程(內存頁被換出到磁盤的動機,就是今後還要從磁盤將其載回內存)。所以swap複用了文件子系統的一些機制。

頁面換入換出是一件很費CPU和IO的事情,但是由於內存昂貴這一歷史原因,我們只好拿磁盤來擴展內存。但是現在內存越來越便宜了,我們可以輕鬆安裝數G的內存,然後將swap系統關閉。於是swap的實現實在讓人難有探索的慾望,在這裏就不贅述了。(另見:《linux內核頁面回收淺析》)

 

[用戶空間內存管理]

malloc是libc的庫函數,用戶程序一般通過它(或類似函數)來分配內存空間。

libc對內存的分配有兩種途徑,一是調整堆的大小,二是mmap一個新的虛擬內存區域(堆也是一個vma)。

在內核中,堆是一個一端固定、一端可伸縮的vma(圖:左中)。可伸縮的一端通過系統調用brk來調整。libc管理着堆的空間,用戶調用malloc分配內存時,libc儘量從現有的堆中去分配。如果堆空間不夠,則通過brk增大堆空間。

當用戶將已分配的空間free時,libc可能會通過brk減小堆空間。但是堆空間增大容易減小卻難,考慮這樣一種情況,用戶空間連續分配了10塊內存,前9塊已經free。這時,未free的第10塊哪怕只有1字節大,libc也不能夠去減小堆的大小。因爲堆只有一端可伸縮,並且中間不能掏空。而第10塊內存就死死地佔據着堆可伸縮的那一端,堆的大小沒法減小,相關資源也沒法歸還內核。

當用戶malloc一塊很大的內存時,libc會通過mmap系統調用映射一個新的vma。因爲對於堆的大小調整和空間管理還是比較麻煩的,重新建一個vma會更方便(上面提到的free的問題也是原因之一)。

那麼爲什麼不總是在malloc的時候去mmap一個新的vma呢?第一,對於小空間的分配與回收,被libc管理的堆空間已經能夠滿足需要,不必每次都去進行系統調用。並且vma是以page爲單位的,最小就是分配一個頁;第二,太多的vma會降低系統性能。缺頁異常、vma的新建與銷燬、堆空間的大小調整、等等情況下,都需要對vma進行操作,需要在當前進程的所有vma中找到需要被操作的那個(或那些)vma。vma數目太多,必然導致性能下降。(在進程的vma較少時,內核採用鏈表來管理vma;vma較多時,改用紅黑樹來管理。)

malloc函數的實質體現在,它有一個將可用的內存塊連接爲一個長長的列表的所謂空閒鏈表。調用malloc函數時,它沿連接表尋找一個大到足以滿足用戶請求所需要的內存塊。然後,將該內存塊一分爲二(一塊的大小與用戶請求的大小相等,另一塊的大小就是剩下的字節)。接下來,將分配給用戶的那塊內存傳給用戶,並將剩下的那塊(如果有的話)返回到連接表上。調用free函數時,它將用戶釋放的內存塊連接到空閒鏈上。到最後,空閒鏈會被切成很多的小內存片段,如果這時用戶申請一個大的內存片段,那麼空閒鏈上可能沒有可以滿足用戶要求的片段了。於是,malloc函數請求延時,並開始在空閒鏈上翻箱倒櫃地檢查各內存片段,對它們進行整理,將相鄰的小空閒塊合併成較大的內存塊。如果無法獲得符合要求的內存塊,malloc函數會返回NULL指針,因此在調用malloc動態申請內存塊時,一定要進行返回值的判斷。

 

C++

malloc和new的區別

malloc內存分配怎麼進行(見上面內存管理)

繼承靜態函數

多態和繼承的關係虛函數,虛表

git

git相關操作

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章