Golang后端面試匯總-001


基礎面試

  • go的調度
GPM是Go語言運行時(runtime)層面的實現,是go語言自己實現的一套調度系統。區別於操作系統調度OS線程。

G很好理解,就是個goroutine的,里面除了存放本goroutine信息外 還有與所在P的綁定等信息。
P管理着一組goroutine隊列,P里面會存儲當前goroutine運行的上下文環境(函數指針,堆棧地址及地址邊界),P會對自己管理的goroutine隊列做一些調度(比如把占用CPU時間較長的goroutine暫停、運行后續的goroutine等等)當自己的隊列消費完了就去全局隊列里取,如果全局隊列里也消費完了會去其他P的隊列里搶任務。
M(machine)是Go運行時(runtime)對操作系統內核線程的虛擬, M與內核線程一般是一一映射的關系, 一個groutine最終是要放到M上執行的;
P與M一般也是一一對應的。他們關系是: P管理着一組G掛載在M上運行。當一個G長久阻塞在一個M上時,runtime會新建一個M,阻塞G所在的P會把其他的G 掛載在新建的M上。當舊的G阻塞完成或者認為其已經死掉時 回收舊的M。

P的個數是通過runtime.GOMAXPROCS設定(最大256),Go1.5版本之后默認為物理線程數。 在並發量大的時候會增加一些P和M,但不會太多,切換太頻繁的話得不償失。

單從線程調度講,Go語言相比起其他語言的優勢在於OS線程是由OS內核來調度的,goroutine則是由Go運行時(runtime)自己的調度器調度的,這個調度器使用一個稱為m:n調度的技術(復用/調度m個goroutine到n個OS線程)。 其一大特點是goroutine的調度是在用戶態下完成的, 不涉及內核態與用戶態之間的頻繁切換,包括內存的分配與釋放,都是在用戶態維護着一塊大的內存池, 不直接調用系統的malloc函數(除非內存池需要改變),成本比調度OS線程低很多。 另一方面充分利用了多核的硬件資源,近似的把若干goroutine均分在物理線程上, 再加上本身goroutine的超輕量,以上種種保證了go調度方面的性能。

Go的調度器使用了三種結構:M,P,S

M代表內核線程,類似於標准的POSIX線程,M代表machine。
G代表goroutine,它擁有自己的棧,程序計數器(instruction counter)和一些關於goroutine調度的信息(如正在阻塞的channel)。
P代表processor,表示調度的上下文。可以把它看作是一個局部的調度器,讓Go代碼跑在一個單獨的線程上。這是讓Go從一個N:1調度器映射到一個M:N調度器的關鍵。

參考地址: 
a. https://www.cnblogs.com/Real-m/p/13885918.html
b. https://studygolang.com/articles/20991
  • 為什么在內核的線程調度器之外Go還需要一個自己的調度器?
1. POSIX線程API是對已有的UNIX進程模型的邏輯擴展,因此線程和進程在很多方面都類似。例如,線程有自己的信號掩碼,CPU affinity(進程要在某個給定的 CPU 上盡量長時間地運行而不被遷移到其他處理器的傾向性
),cgroups。但是有很多特性對於Go程序來說都是累贅。 
2. 另外一個問題是基於Go語言模型,OS的調度決定並不一定合理。例如,Go的垃圾回收需要內存處於一致性的狀態,這需要所有運行的線程都停止。垃圾回收的時間點是不確定的,如果僅由OS來調度,將會由大量的線程停止工作。
單獨開發一個Go的調度器能讓我們知道什么時候內存處於一致性的狀態。也就是說,當開始垃圾回收時,運行時只需要為當時正在CPU核上運行的那個線程等待即可,而不是等待所有的線程
  • go struct能不能比較
struct因為是強類型語言,所以不同類型的結構不能作比較,但是同一類型的實例值是可以比較的,實例不可以比較,因為是指針類型
  • go defer(for defer)
先進后出,后進先出
1,defer是什么?
defer 就像它的字面意思一樣,就是延遲執行的意思,但是需要注意的是 defer 只能作用於函數,像變量賦值defer i = 10這種編譯是會報錯的。

2,defer函數的執行順序
被 defer 的函數會放入一個棧中,所以是先進后出的執行順序,而被 defer 的函數在 return 之后執行。

3,清理釋放資源
當打開一個文件時,用完之后我們需要 close 這個文件,否則會導致文件描述符泄露;

4, 執行recover
被 defer 的函數在 return 之后執行,這個時機點正好可以捕獲函數拋出的 panic,因而defer 的另一個重要用途就是執行 recover ,而 recover 也只有在 defer 中才會起作用。

Go 語言中的閉包就是在函數內引用函數體之外的數據,這樣就會產生一種結果,雖然數據定義是在函數外,但是在函數內部操作數據也會對數據產生影響。

參考鏈接: https://blog.csdn.net/HYZX_9987/article/details/103727233
  • select可以用於什么
用於gorotine的完美退出
golang 的 select 就是監聽 IO 操作,當 IO 操作發生時,觸發相應的動作每個case語句里必須是一個IO操作,確切的說,應該是一個面向channel的IO操作

參考鏈接: https://www.cnblogs.com/gwyy/p/13629999.html
  • context包的用途
context包的用途Context通常被譯作上下文,它是一個比較抽象的概念,其本質,是【上下上下】存在上下層的傳遞,上會把內容傳遞給下。在Go語言中,程序單元也就指的是Goroutine

參考鏈接: https://blog.csdn.net/qq_33296108/article/details/82791691
  • client如何實現長連接
server是設置超時時間,for循環遍歷的
  • 主協程如何等其余協程完再操作
使用channel進行通信,context,select

參考鏈接: https://www.cnblogs.com/secondtonone1/p/11803961.html
  • slice,len,cap,共享,擴容
append函數,因為slice底層數據結構是,由數組、len、cap組成,所以,在使用append擴容時,會查看數組后面有沒有連續內存快,有就在后面添加,沒有就重新生成一個大的數組
  • map如何順序讀取
map不能順序讀取,是因為他是無序的,想要有序讀取,首先的解決的問題就是,把key變為有序,所以可以把key放入切片,對切片進行排序,遍歷切片,通過key取值。
  • 實現set
參考鏈接:  http://www.zzvips.com/article/70251.html
  • 實現消息隊列(多生產者,多消費者)
使用切片加鎖可以實現

參考鏈接: https://www.cnblogs.com/asong2020/articles/13697817.html
  • 大文件排序
歸並排序,分而治之,拆分為小文件,在排序

參考鏈接: https://zhuanlan.zhihu.com/p/124356219
  • 基本排序,哪些是穩定的
快速排序、希爾排序、堆排序、直接選擇bai排序不是穩定的排序算法。

基數排序、冒泡排序、直接插入排序、折半插入排序、歸並排序是穩定的排序算法。

鏈接地址: https://zhuanlan.zhihu.com/p/83756377
  • http get跟head
HEAD: 只請求頁面的首部。

GET: 請求指定的頁面信息,並返回實體主體。

POST: 請求服務器接受所指定的文檔作為對所標識的URL的新的從屬實體。

參考鏈接: https://blog.csdn.net/ysh1042436059/article/details/80985574
  • http 401,403
401 unauthorized,表示發送的請求需要有通過 HTTP 認證的認證信息
403 forbidden,表示對請求資源的訪問被服務器拒絕

參考鏈接: https://www.cnblogs.com/purewhite/p/10871851.html
  • http keep-alive
另一個問題就是在使用keepalive的情況,客戶端依然有同時發送多個請求的情況,比如網頁加載是需要同時load多個靜態資源。比如 瀏覽器默認最大連接數是6,現在有十個資源同時加載,那么這十個里會有6個並行,4個與前6個串行。

在keepalive里有個問題就是如果能知道每個repose與其對應的request的話,並發的請求可以只需要一次TCP連接,這也就是http2.0實現的多路復用。

參考鏈接: https://www.jianshu.com/p/347416aafd3f
  • http能不能一次連接多次請求,不等后端返回
http本質上市使用socket連接,因此發送請求,接寫入tcp緩沖,是可以多次進行的,這也是http是無狀態的原因
  • tcp與udp區別,udp優點,適用場景
tcp傳輸的是數據流,而udp是數據包,tcp會經過三次握手,udp不需要

參考鏈接: https://www.cnblogs.com/GuoXinxin/p/11657676.html
  • time-wait的作用
主動關閉的Socket端會進入TIME_WAIT狀態,並且持續2MSL時間長度,MSL就是maximum segment lifetime(最大分節生命期),這是一個IP數據包能在互聯網上生存的最長時間,超過這個時間將在網絡中消失。MSL在RFC 1122上建議是2分鍾,而源自berkeley的TCP實現傳統上使用30秒,因而,TIME_WAIT狀態一般維持在1-4分鍾。

參考鏈接: https://www.cnblogs.com/li-hao/archive/2011/12/08/2280678.html
參考地址: https://blog.csdn.net/qq_16077957/article/details/80112397
  • 數據庫如何建索引
索引的優點如下:

快速訪問數據表中的特定信息,提高檢索速度。
創建唯一性索引,保證數據表中每一行數據的唯一性。
加速表與表之間的連接。
使用分組和排序進行數據檢索時,可以顯著減少查詢中分組和排序的時間。
索引的缺點:

雖然提高了的查詢速度,但卻降低了更新表的速度,比如 update、insert,因為更新數據時,MySQL 不僅要更新數據,還要更新索引文件;
建立索引會占用磁盤文件的索引文件。

參考鏈接: https://www.cnblogs.com/lianhaifeng/p/13544893.html
  • 孤兒進程,僵屍進程
我們知道在unix/linux中,正常情況下,子進程是通過父進程創建的,子進程在創建新的進程。子進程的結束和父進程的運行是一個異步過程,即父進程永遠無法預測子進程 到底什么時候結束。 當一個 進程完成它的工作終止之后,它的父進程需要調用wait()或者waitpid()系統調用取得子進程的終止狀態。

  孤兒進程:一個父進程退出,而它的一個或多個子進程還在運行,那么那些子進程將成為孤兒進程。孤兒進程將被init進程(進程號為1)所收養,並由init進程對它們完成狀態收集工作。

  僵屍進程:一個進程使用fork創建子進程,如果子進程退出,而父進程並沒有調用wait或waitpid獲取子進程的狀態信息,那么子進程的進程描述符仍然保存在系統中。這種進程稱之為僵死進程。

參考鏈接: https://www.cnblogs.com/yadongliang/p/13407666.html
  • 死鎖條件,如何避免
死鎖條件:
互斥條件:一個資源每次只能被一個進程使用,即在一段時間內某 資源僅為一個進程所占有。此時若有其他進程請求該資源,則請求進程只能等待。
請求與保持條件:進程已經保持了至少一個資源,但又提出了新的資源請求,而該資源 已被其他進程占有,此時請求進程被阻塞,但對自己已獲得的資源保持不放。
不可剝奪條件:進程所獲得的資源在未使用完畢之前,不能被其他進程強行奪走,即只能 由獲得該資源的進程自己來釋放(只能是主動釋放)。
循環等待條件: 若干進程間形成首尾相接循環等待資源的關系

如何避免:
我們可以通過破壞死鎖產生的4個必要條件來 預防死鎖,由於資源互斥是資源使用的固有特性是無法改變的。

1 破壞“不可剝奪”條件:一個進程不能獲得所需要的全部資源時便處於等待狀態,等待期間他占有的資源將被隱式的釋放重新加入到 系統的資源列表中,可以被其他的進程使用,而等待的進程只有重新獲得自己原有的資源以及新申請的資源才可以重新啟動,執行。
2 破壞”請求與保持條件“:第一種方法靜態分配即每個進程在開始執行時就申請他所需要的全部資源。第二種是動態分配即每個進程在申請所需要的資源時他本身不占用系統資源。
3 破壞“循環等待”條件:采用資源有序分配其基本思想是將系統中的所有資源順序編號,將緊缺的,稀少的采用較大的編號,在申請資源時必須按照編號的順序進行,一個進程只有獲得較小編號的進程才能申請較大編號的進程。

這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之一不滿足,就不會發生死鎖。

參考鏈接: https://www.cnblogs.com/bopo/p/9228834.html
  • linux命令,查看端口占用,cpu負載,內存占用,如何發送信號給一個進程
參考鏈接: https://www.cnblogs.com/Paul-watermelon/p/10407502.html
參考鏈接: https://www.cnblogs.com/mufengforward/p/9282889.html
  • git文件版本,使用順序,merge跟rebase
git rebase 你其實可以把它理解成是“重新設置基線”,將你的當前分支重新設置開始點。這個時候才能知道你當前分支於你需要比較的分支之間的差異。
原理很簡單:rebase需要基於一個分支來設置你當前的分支的基線,這基線就是當前分支的開始時間軸向后移動到最新的跟蹤分支的最后面,這樣你的當前分支就是最新的跟蹤分支。這里的操作是基於文件事務處理的,所以你不用怕中間失敗會影響文件的一致性。在中間的過程中你可以隨時取消rebase 事務。

參考鏈接: https://www.jianshu.com/p/4079284dd970


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM