W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
從這里開(kāi)始,將深入挖掘關(guān)于運(yùn)行時(shí)庫(kù)部分對(duì)于cgo的支持。還記得前面那個(gè)test.go嗎?這里將繼續(xù)以它為例子進(jìn)行分析。
從Go中調(diào)用C的函數(shù)test,cgo生成的代碼調(diào)用是runtime.cgocall(_cgo_Cfunc_test, frame):
void
·_Cfunc_test(struct{uint8 x[8];}p)
{
runtime·cgocall(_cgo_1b9ecf7f7656_Cfunc_test, &p);
}
其中cgocall的第一個(gè)參數(shù)_cgo_Cfunc_test是一個(gè)由cgo生成并由gcc編譯的函數(shù):
void
_cgo_1b9ecf7f7656_Cfunc_test(void *v)
{
struct {
int p0;
char __pad4[4];
} __attribute__((__packed__)) *a = v;
test(a->p0);
}
runtime.cgocall將g鎖定到m,調(diào)用entersyscall,這樣不會(huì)阻塞其它的goroutine或者垃圾回收,然后調(diào)用runtime.asmcgocall(_cgo_Cfunc_test, frame)。
void
runtime·cgocall(void (*fn)(void*), void *arg)
{
runtime·lockOSThread();
runtime·entersyscall();
runtime·asmcgocall(fn, arg);
runtime·exitsyscall();
endcgo();
}
將g鎖定到m是保證如果在cgo內(nèi)又回調(diào)了Go代碼,切換回來(lái)時(shí)還是在同一個(gè)棧中的。關(guān)于C調(diào)用Go,具體到下一節(jié)再分析。
runtime.entersyscall宣布代碼進(jìn)入了系統(tǒng)調(diào)用,這樣調(diào)度器知道在我們運(yùn)行外部代碼,于是它可以創(chuàng)建一個(gè)新的M來(lái)運(yùn)行g(shù)oroutine。調(diào)用asmcgocall是不會(huì)分裂棧并且不會(huì)分配內(nèi)存的,因此可以安全地在"syscall call"時(shí)調(diào)用,不用考慮GOMAXPROCS計(jì)數(shù)。
runtime.asmcgocall是用匯編實(shí)現(xiàn)的,它會(huì)切換到m的g0棧,然后調(diào)用_cgo_Cfunc_test函數(shù)。由于m的g0棧不是分段棧,因此切換到m->g0棧(這個(gè)棧是操作系統(tǒng)分配的棧)后,可以安全地運(yùn)行g(shù)cc編譯的代碼以及執(zhí)行_cgo_Cfunc_test(frame)函數(shù)。
_cgo_Cfunc_test使用從frame結(jié)構(gòu)體中取得的參數(shù)調(diào)用實(shí)際的C函數(shù)test,將結(jié)果記錄在frame中,然后返回到runtime.asmcgocall。
重獲控制權(quán)之后,runtime.asmcgocall切回之前的g(m->curg)的棧,并且返回到runtime.cgocall。
當(dāng)runtime.cgocall重獲控制權(quán)之后,它調(diào)用exitsyscall,然后將g從m中解鎖。exitsyscall后m會(huì)阻塞直到它可以運(yùn)行Go代碼而不違反$GOMAXPROCS限制。
以上就是Go調(diào)用C時(shí),運(yùn)行時(shí)庫(kù)方面所做的事情,是不是很簡(jiǎn)單呢?因?yàn)榭偨Y(jié)起來(lái)就兩點(diǎn),第一點(diǎn)是runtime.entersyscall,讓cgo產(chǎn)生的外部代碼脫離goroutine調(diào)度系統(tǒng)。第二點(diǎn)就是切換m的g0棧,這樣就不必?fù)?dān)憂分段棧方面的問(wèn)題。
前面講到m的g0棧時(shí),留了個(gè)疑問(wèn)的。那就是新建M的函數(shù)newm只給m的g0棧分配了8K內(nèi)存,好像并不是一個(gè)“無(wú)窮”的棧,怎么回事呢?這里回答這個(gè)問(wèn)題......不過(guò)我會(huì)再額外提兩個(gè)新問(wèn)題,希望讀者跟著思考(好賤哦,哈哈)。
其實(shí)m的g0棧的大小并不在調(diào)用newm時(shí)分配的8K。在newm函數(shù)的最后一步是調(diào)用runtime·newosproc,這個(gè)函數(shù)會(huì)調(diào)用到操作系統(tǒng)的系統(tǒng)調(diào)用,分配一條系統(tǒng)線程。并且做了一個(gè)后處理過(guò)程--它將m的g0棧指針改掉了!m的g0棧指針會(huì)被重新設(shè)置為線程的棧,所以前面說(shuō)m的g0棧是一個(gè)“無(wú)窮”的棧是正確的,那個(gè)分配8K內(nèi)存的地方只是一個(gè)煙霧彈迷惑人的。
好吧,提兩個(gè)疑問(wèn)結(jié)束這一節(jié)內(nèi)容:
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: