Go語言 main.main之前的準備

2018-07-25 17:24 更新

main.main就是用戶的main函數(shù)。這里是指Go的runtime在進入用戶main函數(shù)之前做的一些事情。

前面已經(jīng)介紹了從Go程序執(zhí)行后的第一條指令,到啟動runtime.main的主要流程,比如其中要設(shè)置好本地線程存儲,設(shè)置好main函數(shù)參數(shù),根據(jù)環(huán)境變量GOMAXPROCS設(shè)置好使用的procs,初始化調(diào)度器和內(nèi)存管理等等。

接下來將是從runtime.main到main.main之間的一些過程。注意,main.main是在runtime.main函數(shù)里面調(diào)用的。不過在調(diào)用main.main之前,還有一些工作要做。

sysmon

在main.main執(zhí)行之前,Go語言的runtime庫會初始化一些后臺任務,其中一個任務就是sysmon。

newm(sysmon, nil);

newm新建一個結(jié)構(gòu)體M,第一個參數(shù)是這個結(jié)構(gòu)體M的入口函數(shù),也就說會在一個新的物理線程中運行sysmon函數(shù)。由此可見sysmon是一個地位非常高的后臺任務,整個函數(shù)體一個死循環(huán)的形式,目前主要處理兩個事件:對于網(wǎng)絡的epoll以及搶占式調(diào)度的檢測。大致過程如下:

for(;;) {
    runtime.usleep(delay);
    if(lastpoll != 0 && lastpoll + 10*1000*1000 > now) {
        runtime.netpoll();
    }
    retake(now);    // 根據(jù)每個P的狀態(tài)和運行時間決定是否要進行搶占
}

sysmon會根據(jù)系統(tǒng)當前的繁忙程度睡一小段時間,然后每隔10ms至少進行一次epoll并喚醒相應的goroutine。同時,它還會檢測是否有P長時間處于Psyscall狀態(tài)或Prunning狀態(tài),并進行搶占式調(diào)度。

scavenger

scavenger是另一個后臺任務,但是它的創(chuàng)建跟sysmon有點區(qū)別:

runtime·newproc(&scavenger, nil, 0, 0, runtime·main);

newproc創(chuàng)建一個goroutine,第一個參數(shù)是goroutine運行的函數(shù)。scavenger的地位是沒有sysmon那么高的——sysmon是由物理線程運行的,而scavenger只是由goroutine運行的。接下來的章節(jié)會說明goroutine與物理線程的區(qū)別。

那么,scavenger執(zhí)行什么工作?它又為什么不像sysmon那樣呢?其實scavenger執(zhí)行的是runtime·MHeap_Scavenger函數(shù)。它將一些不再使用的內(nèi)存歸還給操作系統(tǒng)。Go是一門垃圾回收的語言,垃圾回收會在系統(tǒng)運行過程中被觸發(fā),內(nèi)存會被歸還到Go的內(nèi)存管理系統(tǒng)中,Go的內(nèi)存管理是基于內(nèi)存池進行重用的,而這個函數(shù)會真正地將內(nèi)存歸還給操作系統(tǒng)。

scavenger顯然沒有sysmon要求那么高,所以它僅僅是一個普通的goroutine而不是一個線程。

main.main在這些后臺任務運行起來之后執(zhí)行,不過在它執(zhí)行之前,還有最后一個:main.init,每個包的init函數(shù)會在包使用之前先執(zhí)行。

links


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號