1、棧:FILO先進(jìn)后出的數(shù)據(jù)結(jié)構(gòu)
創(chuàng)新互聯(lián)公司專業(yè)網(wǎng)站建設(shè)、成都網(wǎng)站制作,集網(wǎng)站策劃、網(wǎng)站設(shè)計(jì)、網(wǎng)站制作于一體,網(wǎng)站seo、網(wǎng)站優(yōu)化、網(wǎng)站營(yíng)銷、軟文發(fā)稿等專業(yè)人才根據(jù)搜索規(guī)律編程設(shè)計(jì),讓網(wǎng)站在運(yùn)行后,在搜索中有好的表現(xiàn),專業(yè)設(shè)計(jì)制作為您帶來(lái)效益的網(wǎng)站!讓網(wǎng)站建設(shè)為您創(chuàng)造效益。
棧底是第一個(gè)進(jìn)棧的數(shù)據(jù)的位置(壓箱?底)
棧頂是最后一個(gè)進(jìn)棧的數(shù)據(jù)位置
2、根據(jù)SP指針指向的位置,??煞譃?滿棧和空棧
滿棧:當(dāng)sp指針總是指向最后壓入堆棧?的數(shù)據(jù)(ARM采用滿棧)
空棧:當(dāng)堆棧指針SP總是指向下一個(gè)將?要放入數(shù)據(jù)的空位置。
3、根據(jù)SP指針移動(dòng)的方向,可分為升?棧和降棧
升棧:隨數(shù)據(jù)的入棧,SP由低地址--?高地址
降棧:隨數(shù)據(jù)的入棧,SP由高地址--?低地址(ARM采用降棧)
4、棧幀:存儲(chǔ)在用戶棧上的(當(dāng)然內(nèi)核棧同樣適用)每一次函數(shù)調(diào)用涉及的相關(guān)信息的記錄單元 ;?棧幀(stack frame)就是一個(gè)函數(shù)所使用的那部分棧,所有函數(shù)的棧幀串起來(lái)就組成了一個(gè)完整的棧。
棧幀的兩個(gè)邊界分別有FP(R11)和SP(R13)L來(lái)限定。
棧幀
棧的作用:
1)保存局部變量
分析代碼:
[html]?view plain?copy
#include?stdio.h
int?main()
{
int?a;
a++;
return?a;
}/span
反匯編之后的代碼;
[html]?view plain?copy
stack:?file?format?elf32-littlearm
Disassembly?of?section?.text:
00000000?main:
#include?stdio.h
int?main()
{
0:???e52db004?push???{fp}?????;?(str?fp,?[sp,?#-4]!)?@將棧幀底部指針FP壓入棧中;創(chuàng)建屬于main函數(shù)的棧幀。
4:???e28db000?add????fp,?sp,?#0??;?0x0?@fp指針為函數(shù)棧幀的底部,
8:???e24dd00c?sub????sp,?sp,?#12?;?0xc???@sp指針為棧幀的頂部,同時(shí)為棧的棧頂。
int?a;
a++;
c:???e51b3008?ldr????r3,?[fp,?#-8]???@由此三句可知變量a在棧幀中執(zhí)行了加法操作,及棧幀具有保存局部變量的作用
10:???e2833001?add????r3,?r3,?#1??;?0x1
14:???e50b3008?str????r3,?[fp,?#-8]
return?a;
18:???e51b3008?ldr????r3,?[fp,?#-8]
}
/span
2)保存函數(shù)的參數(shù)
分析代碼:
[html]?view plain?copy
span?style="font-size:18px;"#include?stdio.h
void?func1(int?a,int?b,int?c,int?d,int?e,int?f)
{
int?k;
k=e+f;
}
int?main()
{
func1(1,2,3,4,5,6);
return?0;
}
反匯編之后的代碼;
void?func1(int?a,int?b,int?c,int?d,int?e,int?f)?@多于4個(gè)參數(shù)
{
0:???e52db004?push???{fp}?????;?(str?fp,?[sp,?#-4]!)@保存main函數(shù)的棧幀底部指針FP
4:???e28db000?add????fp,?sp,?#0??;?0x0
8:???e24dd01c?sub????sp,?sp,?#28?;?0x1c?@由棧幀頂部指針SP創(chuàng)建一片棧幀保存子函數(shù)的前四個(gè)參數(shù)
c:???e50b0010?str????r0,?[fp,?#-16]??@?a
10:???e50b1014?str????r1,?[fp,?#-20]??@?b
14:???e50b2018?str????r2,?[fp,?#-24]??@?c
18:???e50b301c?str????r3,?[fp,?#-28]??@?d
int?k;
k=e+f;
1c:???e59b3004?ldr????r3,?[fp,?#4]????@在子函數(shù)的棧幀中實(shí)現(xiàn)第五個(gè)參數(shù)與第六個(gè)參數(shù)的運(yùn)算
20:???e59b2008?ldr????r2,?[fp,?#8]?@由ldr??r2,?[fp,?#8]知參數(shù)保存在main函數(shù)的棧幀中,并運(yùn)算
24:???e0833002?add????r3,?r3,?r2???@以子函數(shù)的棧幀底部指針(fp)做參考坐標(biāo)實(shí)現(xiàn)對(duì)參數(shù)的查找
28:???e50b3008?str????r3,?[fp,?#-8]
}
2c:???e28bd000?add????sp,?fp,?#0??;?0x0
30:???e8bd0800?pop????{fp}
34:???e12fff1e?bx?lr
00000038?main:
int?main()
{
38:???e92d4800?push???{fp,?lr}????@由于調(diào)用子函數(shù),先保存main函數(shù)的棧幀底部指針FP和返回地址LR(當(dāng)前PC指針的下一地址)
3c:???e28db004?add????fp,?sp,?#4??;?0x4?@可知先壓入FP,后壓入lr.把此時(shí)子函數(shù)(被調(diào)用者)的棧幀底部指針FP指向保存在子函數(shù)棧幀的main函數(shù)(調(diào)用者)的棧幀底部指針FP
40:???e24dd008?sub????sp,?sp,?#8??;?0x8???@創(chuàng)建棧
func1(1,2,3,4,5,6);
44:???e3a03005?mov????r3,?#5??;?0x5
48:???e58d3000?str????r3,?[sp]
4c:???e3a03006?mov????r3,?#6??;?0x6
50:???e58d3004?str????r3,?[sp,?#4]
54:???e3a00001?mov????r0,?#1??;?0x1?@用通用寄存器保存前四個(gè)參數(shù)的值
58:???e3a01002?mov????r1,?#2??;?0x2
5c:???e3a02003?mov????r2,?#3??;?0x3
60:???e3a03004?mov????r3,?#4??;?0x4
64:???ebfffffe?bl?0?func1
return?0;
68:???e3a03000?mov????r3,?#0??;?0x0
}
6c:???e1a00003?mov????r0,?r3
70:???e24bd004?sub????sp,?fp,?#4??;?0x4
74:???e8bd4800?pop????{fp,?lr}
78:???e12fff1e?bx?lr/span
注:C中,若函數(shù)的參數(shù)小于等于4個(gè),則用通用寄存器保存其參數(shù)值,多于4個(gè)的參數(shù)保存在棧中
3)保存寄存器的值
分析代碼:
[html]?view plain?copy
span?style="font-size:18px;"include?stdio.h
void?func2(int?a,int?b)
{
int?k;
k=a+b;
}
void?func1(int?a,int?b)
{
int?c;
func2(3,4);
c=a+b;
}
int?main()
{
func1(1,2);
return?0;
}/span
反匯編之后的代碼;
[html]?view plain?copy
span?style="font-size:18px;"void?func2(int?a,int?b)
{
0:???e52db004?push???{fp}?????;?(str?fp,?[sp,?#-4]!)
4:???e28db000?add????fp,?sp,?#0??;?0x0
8:???e24dd014?sub????sp,?sp,?#20?;?0x14
c:???e50b0010?str????r0,?[fp,?#-16]?@保存寄存器的值
10:???e50b1014?str????r1,?[fp,?#-20]
int?k;
k=a+b;
14:???e51b3010?ldr????r3,?[fp,?#-16]
18:???e51b2014?ldr????r2,?[fp,?#-20]
1c:???e0833002?add????r3,?r3,?r2
20:???e50b3008?str????r3,?[fp,?#-8]
}
24:???e28bd000?add????sp,?fp,?#0??;?0x0
28:???e8bd0800?pop????{fp}
2c:???e12fff1e?bx?lr
00000030?func1:
void?func1(int?a,int?b)
{
30:???e92d4800?push???{fp,?lr}
34:???e28db004?add????fp,?sp,?#4??;?0x4
38:???e24dd010?sub????sp,?sp,?#16?;?0x10
3c:???e50b0010?str????r0,?[fp,?#-16]?@代碼44行調(diào)用func2函數(shù)后,又使用r0\r1保存參數(shù),所以此時(shí)將r0\r1寄存器的
40:???e50b1014?str????r1,?[fp,?#-20]??@值放入棧中
int?c;
func2(3,4);
44:???e3a00003?mov????r0,?#3??;?0x3
48:???e3a01004?mov????r1,?#4??;?0x4
4c:???ebfffffe?bl?0?func2
c=a+b;
50:???e51b3010?ldr????r3,?[fp,?#-16]
54:???e51b2014?ldr????r2,?[fp,?#-20]
58:???e0833002?add????r3,?r3,?r2
5c:???e50b3008?str????r3,?[fp,?#-8]
}
60:???e24bd004?sub????sp,?fp,?#4??;?0x4
64:???e8bd4800?pop????{fp,?lr}
68:???e12fff1e?bx?lr
0000006c?main:
int?main()
{
6c:???e92d4800?push???{fp,?lr}
70:???e28db004?add????fp,?sp,?#4??;?0x4
func1(1,2);
74:???e3a00001?mov????r0,?#1??;?0x1
78:???e3a01002?mov????r1,?#2??;?0x2
7c:???ebfffffe?bl?30?func1
return?0;
80:???e3a03000?mov????r3,?#0??;?0x0
}
84:???e1a00003?mov????r0,?r3
88:???e24bd004?sub????sp,?fp,?#4??;?0x4
8c:???e8bd4800?pop????{fp,?lr}
90:???e12fff1e?bx?lr/span
初始化棧:即對(duì)SP指針賦予一個(gè)內(nèi)存地址(統(tǒng)一標(biāo)準(zhǔn):2440、6410、210)
在內(nèi)存的64MB位置即ldr sp, =0x34000000(2440)
ldr sp, =0x54000000(6410)
ldr sp, =0x24000000(210)
由上可知ARM采用滿棧(指向剛?cè)霔5臄?shù)據(jù))、降棧(由高地址向低地址入棧)
問(wèn)題:因?yàn)锳RM不同工作模式有不同的棧,定義棧的技巧是什么,避免定義相同的地址使用不同棧?
轉(zhuǎn)自:
棧幀也叫過(guò)程活動(dòng)記錄,是編譯器用來(lái)實(shí)現(xiàn)過(guò)程或函數(shù)調(diào)用的一種數(shù)據(jù)結(jié)構(gòu)。
C語(yǔ)言中,每個(gè)棧幀對(duì)應(yīng)著一個(gè)未運(yùn)行完的函數(shù)。棧幀中保存了該函數(shù)的返回地址和局部變量。
棧幀,顧名思義,就是棧中的一幀,棧分成很多幀,就如同一個(gè)視頻動(dòng)作分成好多幀一樣。每個(gè)棧幀,對(duì)應(yīng)一個(gè)函數(shù),就是這個(gè)函數(shù)在棧中占用的部分。
通過(guò)var聲明或者make函數(shù)創(chuàng)建的channel變量是一個(gè)存儲(chǔ)在函數(shù)棧幀上的指針,占用8個(gè)字節(jié),指向堆上的hchan結(jié)構(gòu)體
源碼包中src/runtime/chan.go定義了hchan的數(shù)據(jù)結(jié)構(gòu)如下:
hchan結(jié)構(gòu)體的主要組成部分有四個(gè):
用來(lái)保存goroutine之間傳遞數(shù)據(jù)的循環(huán)數(shù)組:buf
用來(lái)記錄此循環(huán)數(shù)組當(dāng)前發(fā)送或接收數(shù)據(jù)的下標(biāo)值:sendx和recvx
用于保存向該chan發(fā)送和從該chan接收數(shù)據(jù)被阻塞的goroutine隊(duì)列: sendq 和 recvq
保證channel寫入和讀取數(shù)據(jù)時(shí)線程安全的鎖:lock
環(huán)形數(shù)組作為channel 的緩沖區(qū) 數(shù)組的長(zhǎng)度就是定義channnel 時(shí)channel 的緩沖大小
在hchan 中包括了讀/寫 等待隊(duì)列, waitq是一個(gè)雙向隊(duì)列,包括了一個(gè)頭結(jié)點(diǎn)和尾節(jié)點(diǎn)。 每個(gè)節(jié)點(diǎn)是一個(gè)sudog結(jié)構(gòu)體變量
channel有2種類型:無(wú)緩沖、有緩沖, 在創(chuàng)建時(shí) make(chan type cap) 通過(guò)cap 設(shè)定緩沖大小
channel有3種模式:寫操作模式(單向通道)、讀操作模式(單向通道)、讀寫操作模式(雙向通道)
channel有3種狀態(tài):未初始化、正常、關(guān)閉
如下幾種狀態(tài)會(huì)引發(fā)panic
channel 是線程安全的,channel的底層實(shí)現(xiàn)中,hchan結(jié)構(gòu)體中采用Mutex鎖來(lái)保證數(shù)據(jù)讀寫安全。在對(duì)循環(huán)數(shù)組buf中的數(shù)據(jù)進(jìn)行入隊(duì)和出隊(duì)操作時(shí),必須先獲取互斥鎖,才能操作channel數(shù)據(jù)