先前準備 Golang 面試用的筆記混移,僅供參考廓译。
前言
本文結(jié)構(gòu):
1. └──計算機基礎(chǔ)
2. ├── 計算機網(wǎng)絡
3. ├── 數(shù)據(jù)結(jié)構(gòu)
4. ├── 算法
5. ├── 操作系統(tǒng)
6. ├── 數(shù)據(jù)庫
7. └── OOP 與設計模式
8. └── Golang 面試題
參考資料:筆試面試知識整理、Golang 面試題解析乙濒、Go面試題答案與解析
這篇文章的內(nèi)容力大多一筆帶過陕赃,細節(jié)上我參考的書籍有:
- 計算機網(wǎng)絡:《計算計網(wǎng)絡(謝仁希)》、《圖解TCP/IP》颁股、《TCP/IP 卷1: 協(xié)議》么库、《自頂向下方法》
- 數(shù)據(jù)結(jié)構(gòu):《數(shù)據(jù)結(jié)構(gòu)與算法分析:C語言描述》
- 算法:《算法:C語言實現(xiàn)》
- 操作系統(tǒng):《現(xiàn)代操作系統(tǒng)》
計算機網(wǎng)絡
- TCP / UDP 傳輸層:端到端的服務
- IP 網(wǎng)絡層:點到點的服務
HTTP 協(xié)議
請求報文
1. <method> <request-URL> <version> # 狀態(tài)行 # 狀態(tài)行
2. <headers> # 請求頭 # 響應頭
3. <entity-body> # 請求實體 # 響應體
HTTP 協(xié)議不限制 GET URL 長度,但瀏覽器限制字符數(shù)(Chrome 8K)& 也不限制 POST 資源大小
- GET:查
- 安全的:獲取信息甘有,非修改信息诉儒。一般不會產(chǎn)生副作用
- 冪等的:同一 URL 多個請求返回同一結(jié)果
- POST:改,服務端通過請求 header 的 Content-Type 字段解析實體數(shù)據(jù)亏掀。提交數(shù)據(jù)的方式:
- application/x-www-form-urlencoded:瀏覽器原生的 form 表單忱反,提交的數(shù)據(jù)按 url 編碼,Ajax大多默認使用
- form_data:表單文件上傳
- application/json:API 使用較多
響應報文
狀態(tài)行:協(xié)議版本滤愕、狀態(tài)碼缭受、狀態(tài)描述
1: Informational
- 100 continue:POST 提交數(shù)據(jù)大于 100KB 時候發(fā)的第一個請求,允許上傳則返回 100
2:Success
- 200 OK:請求被成功處理该互,GET 返回資源米者、POST 返回對請求處理的結(jié)果
- 204 No Content:請求處理完畢,但不返回響應體宇智,客戶端頁面不刷新
3:Redirection
- 301 Moved Permanently:永久重定向蔓搞,資源分配了新的 URI,處理:
- HEAD:響應頭的
Location
指明新的 URI - GET:
Location
指明新 URI & 在響應體中附上 URI
- HEAD:響應頭的
- 302 Moved Temporarily:臨時性重定向随橘,希望用戶本次能訪問新的 URI喂分。重定向后的請求方法不變
- 303 See Other:請求資源有另一個 URI,重定向后的方法變?yōu)?GET 獲取新 URI注意: 很多瀏覽器將 303 理解為 302机蔗,直接使用 GET 請求
Location
中的 URI - 304 Not Modified:請求頭中帶有
If-Modified
蒲祈、If-Match
,自從上次請求后資源并未更新萝嘁,則不發(fā)送響應體梆掸。
4:Client Error
- 400 Bad Request:請求報文有語法錯誤
- 401 Unauthorized:請求需要有 HTTP 認證(nginx 的 auth_basic),返回 401 時頭部中
www-authenticate
指明認證方式牙言,再次請求時也需帶上authorization
認證信息 - 403 Forbidden:訪問被拒絕酸钦,響應實體中可說明原因
- 404 Not Found:請求資源不存在、403 不想說明原因
5:Server Error
- 500 Internel Error:服務器處理請求出錯
- 502 Bad Gateway: 服務器作為代理咱枉,從 upstream 收到無效響應
- 503 Server Unavailable:服務器暫時無法處理請求卑硫,恢復時間在
Retry-After
Conditional Get (條件 GET)
用戶訪問過該網(wǎng)頁徒恋,再次訪問。
GET 頭部帶有 If-Modified-Since:
欢伏,若響應 304入挣,則直接使用瀏覽器的緩存。否則返回正常實體
持久連接
HTTP 1.0 中:客戶端請求頭添加 Connection: Keep-alive
硝拧,服務端同樣在響應頭中添加径筏,保持連接
HTTP 1.1 中:默認所有連接都是長連接,添加 Connection: Close
才關(guān)閉河爹,設置
-
Keep-Alive: timeout=5, max=100
:長連接保持 5s匠璧,最多接收 100 次請求后斷開 - 注意:
Keep-Alive
連接也是無狀態(tài)的 - 傳輸結(jié)束的條件:傳輸?shù)臄?shù)據(jù)達到 Content-Length
HTTP Pipelining 管線化
批量提交 HTTP 請求,不排隊等待響應才發(fā)送下一個請求:請求1 -> 響應1 -> 請求2 -> 響應2 -> 請求3 -> 響應3
變?yōu)?請求1 -> 請求2 -> 請求3 -> 響應1 -> 響應2 -> 響應3
- 管線化機制僅 HTTP1.1 支持
- 只支持 GET咸这、POST
HTTP1.0 中發(fā)下一個請求前夷恍,必須等待響應。
會話追蹤
HTTP 請求是無狀態(tài)的協(xié)議媳维,不會保存客戶端信息酿雪,實現(xiàn):
cookie
- 服務端發(fā)送給客戶端的一小段信息,客戶端每次請求都會帶上侄刽,在有效期內(nèi)識別身份指黎。
- 分為保存在瀏覽器的臨時 cookie 和保存在內(nèi)存的永久 cookie
- 可被禁用
session
- 服務端創(chuàng)建 session 對象并用 sessionID 標識,將 sessionID 放到 cookie 中州丹,發(fā)送給客戶端
- cookie 被禁用醋安,session 也會生效
token(重寫 URL)
- 在 URL 中添加標識每個用戶的 token(cookie 被禁用時,將 sessionID 重寫到 URL 中)
HTTP 安全
CSRF 跨站請求偽造:攻擊者知道所有參數(shù)墓毒、構(gòu)造合法請求
偽造成其他用戶吓揪,發(fā)起請求:如拼接惡意鏈接給其他用戶點擊。防范:
- 關(guān)鍵操作限制 POST
- 合適的使用驗證碼
- 添加 token:發(fā)起請求時添加附加的 token 參數(shù)所计,值隨機柠辞。服務端做驗證
- header refer:外部來源可拒絕請求
XSS 跨站腳本攻擊:在客戶端不知情的情況下運行 JS 腳本。防范:
- 過濾和轉(zhuǎn)義用戶的輸入內(nèi)容:
htmlspecialchars()
- 限制參數(shù)類型
- 文件上傳做大小主胧、類型限制
TCP 協(xié)議
特點
- 面向連接叭首、可靠、字節(jié)流服務:有 TCP 緩沖踪栋,可切割較長數(shù)據(jù)塊焙格、累積較短數(shù)據(jù)塊。與 UDP 每次數(shù)據(jù)報不同
- 校驗和己英、確認和重傳機制來保證可靠傳輸
- 動態(tài)改變滑動窗口來控制流量
- 一對一的通信间螟,不能用于多播個廣播
應用:HTTP、FTP损肛、SMTP厢破、SSH(數(shù)據(jù)準確性要求高)
優(yōu)點:穩(wěn)定可靠
缺點:慢、效率低治拿、占資源多摩泪、易攻擊(DDOS)
三次握手 Three-way Handshake
客戶端執(zhí)行 connect()
主動連接:
1. A:聽得到嗎?
2. B:聽得到劫谅,你能聽到我嗎见坑?
3. A:可以,我們可以交流了233
4.
5. 前兩次:保證 B 能接收到 A 的信息捏检,并作出正確響應
6. 第三次:為了防止 A 的延遲的連接請求荞驴,B 一直在等待 A 的數(shù)據(jù)而浪費資源
理解:傳輸?shù)男诺啦皇墙^對可靠的。為了不可靠的信道上可靠的傳輸信息贯城,最少要進行三次通信熊楼。
TCP Flags 標志位:
1. SYN:synchronous 建立連接
2. ACK:acknowledgement 確認連接
3. PSH:push 推送
4. FIN:finish 釋放連接(請求方數(shù)據(jù)已發(fā)送完畢)
5. RST: reset 重置(復位請求)
6. URG: urgent 緊急
1.SYN = 1 & ACK = 0:請求連接報文
2.SYN = 1 & ACK = 1:同意建立連接的響應報文
過程:
- 第一次握手:客戶端請求建立連接SYN = 1,Seq = x能犯,進入 SYN_SEND 狀態(tài)鲫骗,等待服務端應答
- 第二次握手:服務器允許建立連接SYN = 1,ACK = x+1踩晶,Seq = y执泰,進入 SYNC_RCVD 狀態(tài),等待客戶端確認
- 第三次握手:客戶端確認建立連接ACK = y + 1渡蜻,連接建立术吝。雙方進入 ESTABLIASHED 狀態(tài)
四次揮手 Four-way handshake
服務端和客戶端均可主動斷開連接:服務端、客戶端均需確認對方無數(shù)據(jù)再發(fā)送
- 第一次握手:無數(shù)據(jù)再發(fā)送茸苇,主動關(guān)閉連接發(fā)送 FIN 報文排苍,等待對法發(fā)送 ACK 報文,進入
FIN_WAIT_1
狀態(tài) - 第二次握手:同意關(guān)閉連接發(fā)送 ACK 報文確認可關(guān)閉税弃,并將未發(fā)送完畢的數(shù)據(jù)推送給對方
- 第三次握手:請求對方關(guān)閉連接發(fā)送 FIN 報文纪岁,等待 ACK 報文
- 第四次握手:關(guān)閉發(fā)送 ACK,進入
TIME_WAIT
狀態(tài)则果,過 2 MSL(最大分段生存時間)未收到重傳信息幔翰,直接關(guān)閉。
注意: 中間直接發(fā)送 ACK + FIN西壮,則主動方會直接跳過 FIN_WAIT_2 狀態(tài)
TCP Keep-Alive 機制(心跳包)
數(shù)據(jù)交互完畢后遗增,一方主動釋放連接。但出現(xiàn)意外時款青,TCP 連接不能及時釋放做修,導致要維護很多半打開的連接。
實現(xiàn):定時(半秒等)給對方發(fā)一個探測包,若收到 ACK 則認為連接存活饰及,若 重試一定次數(shù) 都沒收到回應則直接丟棄該 TCP 連接蔗坯。
在我的 B 站直播間數(shù)據(jù)爬蟲 抓取時,就需要每隔半分鐘給 B 站的彈幕服務器發(fā)一個心跳包燎含,否則連接會在一分鐘后斷開宾濒。
UDP 協(xié)議
特點
- 不可靠:沒有確認、超時重傳屏箍、序列號機制:UDP 數(shù)據(jù)報不保證能送達绘梦、不保證數(shù)據(jù)報的順序
- 無需建立連接:創(chuàng)建 UDP 連接前無需握手創(chuàng)建連接
- 一次發(fā)送一個報文,給定報文長度:過長 IP 層會分片
- 支持多播和廣播
應用:DNS赴魁、流媒體(速度要求 > 質(zhì)量要求)
優(yōu)點:快卸奉、比 TCP 稍安全
缺點:不可靠、不穩(wěn)定
TCP | UDP | |
---|---|---|
報文 | 面向字節(jié)流 | 面向報文 |
雙工性 | 全雙工 | 一對一颖御、一對多榄棵、多對一、多對多 |
流量控制 | 滑動窗口 | 無 |
擁塞控制 | 快重傳郎嫁、快恢復 | 無 |
傳輸速度 | 慢 | 快 |
IP 協(xié)議
地址分類
IPv4 用點分十進制表示秉继,IP 地址 = 網(wǎng)絡地址 + 主機地址(層次)
全零 0.0.0.0
:本主機、全一:255.255.255.255
當前子網(wǎng)的廣播地址
A 類:8 – 1(0) = 7 位網(wǎng)絡號
- 主機A的地址為:58.1.2.3/8,前面58位于1~127之間,所以這是一個A類地址.
- 跟主機A位于同一個網(wǎng)絡中的IP地址有 : 58.0.0.1 ~ 58.255.255.254,一共有 2^24-2個
- 不包括:58.0.0.0(網(wǎng)絡地址)跟58.255.255.255(廣播地址)
B 類:16 – 2(10) = 14 位網(wǎng)絡號
C 類:24 – 3(110) = 21 位網(wǎng)絡號
子網(wǎng)掩碼
用子網(wǎng)掩碼劃分一個 IP 的網(wǎng)絡地址和主機地址泽铛。
IP & 子網(wǎng)掩碼 = 網(wǎng)絡地址
192.168.1.1/24
是 192.168.1.1/255.255.255.0
的簡寫尚辑,前 24 位為網(wǎng)絡號
子網(wǎng)劃分:將大的整體網(wǎng)絡劃為小的子網(wǎng)絡
Socket 編程
socket:三元組(IP 地址、協(xié)議盔腔、端口號)標識網(wǎng)絡中唯一的進程杠茬,在 unix 中是文件
socket 使用了門面 Facade 模式:外部與內(nèi)部的通信必須經(jīng)過 facade
socket 隱藏了 TCP/IP 細節(jié),開放交互接口弛随。有:
1. socket() // 創(chuàng)建套接字
2. bind() // 將套接字綁定到服務器地址上
3. listen() // 等待連接請求
4. accept() // 允許連接
5. read() // 讀數(shù)據(jù)
6. write() // 寫數(shù)據(jù)
7. close() // 關(guān)閉連接
搭建簡單的 Server:
- 等待連接
- 建立連接
- 接收請求:讀取 HTTP 請求報文
- 處理請求:訪問資源(文件)
- 構(gòu)建響應:創(chuàng)建 HTTP 響應報文
- 發(fā)送響應
數(shù)據(jù)結(jié)構(gòu)
1.└── 數(shù)據(jù)結(jié)構(gòu)
2. ├── 數(shù)組
3. ├── 鏈表
4. ├── 棧
5. ├── 隊列
6. ├── 哈希表
7. ├── 二叉樹
8. ├── 堆
9. └── 字典樹 trie
參考資料:常見數(shù)據(jù)結(jié)構(gòu)及其多種實現(xiàn)的可視化
數(shù)組 Array
元素在內(nèi)存中連續(xù)存放瓢喉。每個元素占用內(nèi)存相同,可通過下標算出元素位置快速訪問
優(yōu)點:訪問快 O(1)
缺點:增加舀透、刪除元素需要移動大量元素栓票,慢 O(N)
場景:快速訪問元素,很少插入和刪除元素
數(shù)組 | 鏈表 | |
---|---|---|
內(nèi)存分配愕够、元素存儲位置 | 靜態(tài)分配內(nèi)存(棧走贪,系統(tǒng)自動分配) | 動態(tài)分配內(nèi)存(堆,申請和管理麻煩) |
棧 | 堆 | |
分配方式 | 系統(tǒng)自動分配惑芭、速度快 | 自己申請和管理坠狡、new 慢 |
大小 | 編譯時確定语御,具體值 | 是不連續(xù)的內(nèi)存區(qū)域 |
鏈表 Linked List
元素在內(nèi)存中不是連續(xù)存放贪婉。元素間通過指向指針聯(lián)系在一起,訪問元素必須從第一個元素開始遍歷查找
優(yōu)點:插入济炎、刪除元素只需改變指針,快 O(1)
缺點:訪問慢 O(N)
場景:經(jīng)常插入凯亮、刪除元素
分類
- 單向鏈表:節(jié)點僅指向下一節(jié)點边臼,最后一個節(jié)點指向 nil
- 雙向鏈表:每個節(jié)點有 2 個指針
pre
和next
,最后一個節(jié)點的next
指向 nil - 循環(huán)鏈表:單鏈表 + 最后一個節(jié)點指向第一個節(jié)點
時間復雜度
- 查找:
O(N)
- 插入触幼、移除:
O(1)
棧 Stack
元素遵循后進先出(LIFO)原則硼瓣。元素僅在表尾(棧頂)進行插入(入棧 push
)究飞、刪除(出棧 pop
)
實現(xiàn)
- 單鏈表實現(xiàn):保存頭節(jié)點的指針置谦,在頭節(jié)點前入棧、頭節(jié)點上出棧
- 數(shù)組實現(xiàn):直接操作數(shù)組最后一個元素亿傅,可能出現(xiàn)數(shù)組溢出:
- 空棧(空數(shù)組)上
pop()
- 滿棧(滿數(shù)組)上
push()
媒峡,加倍數(shù)組大小
- 空棧(空數(shù)組)上
時間復雜度
- 查找:O(N)
- 插入、刪除:O(1)
隊列
元素遵循先進先出(FIFO)原則葵擎。元素在一端插入(進隊列 enqueue)谅阿、另一端刪除(出隊列 dequeue)
出隊列元素是在隊列中存在時間最長的元素。
實現(xiàn)
- 單鏈表實現(xiàn):保存指向首尾節(jié)點的指針酬滤,從鏈表尾進隊列签餐,鏈表頭出隊列
- 數(shù)組實現(xiàn):修改
arr[0]
進隊列,修改arr[len - 1]
出隊列
時間復雜度
- 查找:O(N)
- 插入盯串、刪除:O(1)
哈希表
散列表:根據(jù) key 鍵值直接訪問數(shù)據(jù)的內(nèi)存地址
hash(data) = key
:hash() 是一類算法氯檐,處理任意長度的 data,得到定長的 key 值体捏,過程不可逆冠摄。
若 data 是數(shù)據(jù)集,則 key 也是數(shù)據(jù)集几缭,將 keys 與原始數(shù)據(jù)一一映射就得到哈希表河泳,即 M[key] = data
二叉樹
- 滿二叉樹:深度為 k 且有 2^k -1 個節(jié)點的二叉樹
- 完全二叉樹:最后一層只缺右邊節(jié)點,其它層節(jié)點數(shù)已為最大值
遍歷方式(棧實現(xiàn)較好)
- 前序遍歷:根 -> 左 -> 右:abdefgc
- 中序遍歷:左 -> 根 -> 右:debgfac
- 后序遍歷:左 -> 右 ->根:edgfbac
二叉搜索樹
性質(zhì):左子節(jié)點均小于根節(jié)點年栓、右子節(jié)點均大于根節(jié)點
復雜度
- 搜索:
O(log(n))
- 插入和刪除:
O(log(n))
算法
└── 算法
├── 排序
├── 查找
└── 字符串算法
參考資料:常見算法的過程可視化拆挥、常見算法的 Go 實現(xiàn)
排序
數(shù)據(jù)大小 n,好的復雜度 O(logN)
某抓,壞的 O(N2)
應用場景
- n 較兄酵谩:直接插入排序
- 基本有序:冒泡排序、直接插入
- n 較大:快速排序搪缨、歸并排序食拜、堆排序等 O(NlogN)
交換排序一:冒泡排序
過程
- 比較相鄰元素,前大于后就交換副编。
- 遍歷操作第 1~N 個元素负甸,此時 第 N 個元素最大。
- 遍歷操作 1~N-1,重復遍歷并比較呻待、交換打月。…
- 嵌套遍歷結(jié)束即完成排序蚕捉。
優(yōu)化
- 設置 flag奏篙,發(fā)生交換設為 true,某趟提前排序完畢則為 false迫淹,直接退出秘通。
- 記錄每輪最后發(fā)生排序的位置,下輪遍歷到此處即可敛熬。
分析
情況 | 復雜度 |
---|---|
最佳情況:已有序肺稀,全遍歷 1 次 | O(N) |
最壞情況:反序,全遍歷 N 次 | O(N^2) |
平均情況 | O(N^2) |
?
交換排序二:快速排序
過程
- 選取基準:從數(shù)組中選一個數(shù)(第一個)作為基準
- 分區(qū):遍歷數(shù)組应民,比基準更小的值放在基準前邊话原、更大的在后邊
- 遞歸:遞歸的在分區(qū)后的數(shù)組中,再選基準诲锹、分區(qū)
分析
情況 | 復雜度 |
---|---|
最佳 | O(logN) |
最壞(數(shù)組反序繁仁、數(shù)組元素全部相同) | O(N^2) |
平均 | O(logN) |
?
選擇排序一:簡單選擇排序
過程
- 遍歷第 1 次:找出最小的元素置于第 1 位
- 遍歷第 N-1 次:排序完畢
分析
情況 | 復雜度 |
---|---|
最佳、最差归园、平均 | O(N^2) |
選擇排序二:堆排序
過程
- 將無序的數(shù)組構(gòu)建成二叉樹
- 二叉樹整理為堆(完全二叉樹)
- 輸出根結(jié)點黄虱,再次整理堆輸出根結(jié)點
分析
情況 | 復雜度 |
---|---|
最佳、最差蔓倍、平均 | O(logN) |
插入排序一:直接插入排序
過程
- 第一個元素(認為已排序好)
- 從第二個元素開始悬钳,往后選擇元素向前遍歷,找到合適的位置插入
- 遍歷到最后一個位置偶翅,排序完畢
分析
情況 | 復雜度 |
---|---|
最佳(數(shù)組升序) | O(N) |
最壞(數(shù)組反序) | O(N^2) |
平均 | O(N^2) |
插入排序二:希爾排序
過程:選擇一個固定(動態(tài))的步長默勾,對步長內(nèi)的元素進行直接插入排序
分析
情況 | 復雜度 |
---|---|
最佳、最壞聚谁、平均(都要步長分割母剥、遍歷) | O(NlogN) |
歸并排序
過程
- 將長度為 N 的序數(shù)組分為 N / 2 的子數(shù)組
- 遞歸劃分子數(shù)組,子數(shù)組內(nèi)部排序
分析
情況 | 復雜度 |
---|---|
最佳 | O(N) |
最壞 | O(NlogN) |
平均 | O(NlogN) |
基數(shù)排序(非比較)
過程
- 獲取數(shù)組中最大值的位數(shù) b
- 從 1 ~ b 遍歷組合位數(shù)相同的元素
分析
情況 | 復雜度 |
---|---|
最佳形导、最壞环疼、平均 | O(N * b) |
查找
無序查找:順序查找
- 順序掃描序列,依次比較值
- 復雜度:O(N)
有序查找(無序序列需要提前排序)
二分查找(折半查找)
- 找到中間節(jié)點比較值朵耕,相等則查找到炫隶,更小則繼續(xù)對左邊折半查找,更大則向右邊
- 查找比對點:
mid = (low + high) / 2
或mid = low + (high - low) / 2
- 復雜度:比較次數(shù)阎曹,O(log2 N)
插值查找
- 動態(tài)的改進二分查找的查找點伪阶,使其更接近區(qū)間
- 查找對比點:
mid = low + (key - arr[low]) / (arr[high] - arr[low]) * (high - low)
- 復雜度:O(log2(log2N))
二叉查找樹
- 中序遍歷獲得排序好的數(shù)組
- 復雜度:一般 O(log2N)煞檩,最壞情況 單支樹 O(N)
平衡二叉查找樹(AVL 樹)
- 任何節(jié)點的 2 棵子樹,高度差最多為 1
- 平衡:根結(jié)點到任意一個葉子節(jié)點栅贴,距離都相等
操作系統(tǒng)
體系結(jié)構(gòu)
- 機器碼:最高位 0 為正斟湃、1 為 負
- 原碼:符號位 + 真值絕對值的二進制
- 反碼:正數(shù)反碼就是本身、負數(shù)除符號位外取反
- 補碼:正數(shù)補碼就是本身檐薯、負數(shù)除符號位外取反 + 1
1. [+1] = [00000001]原 = [00000001]反 = [00000001]補
2. [-1] = [10000001]原 = [11111110]反 = [11111111]補
- 1 byte = 8 bits凝赛,1 word = 2 byte(16位)= 4 byte(32位機)& 字是計算機數(shù)據(jù)處理和運算的單位
- 字節(jié)序:占用內(nèi)存超過 1 byte 的數(shù)據(jù)在內(nèi)存中的存放順序
- 小端字節(jié)序:低字節(jié)數(shù)據(jù)放在內(nèi)存低地址
- 大端字節(jié)序:低字節(jié)數(shù)據(jù)放在內(nèi)存高地址(符合閱讀習慣,網(wǎng)絡中數(shù)據(jù)傳輸?shù)膮f(xié)議坛缕,即網(wǎng)絡字節(jié)序)
B 站直播數(shù)據(jù)抓取 中墓猎,請求數(shù)據(jù)的打包格式就是大端字節(jié)序,否則連接到彈幕服務器祷膳。
0x12345678 的存儲:
Big Endian
低地址 高地址
---------------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 12 | 34 | 56 | 78 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Little Endian
低地址 高地址
---------------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 78 | 56 | 34 | 12 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
基礎(chǔ)
操作系統(tǒng)功能:文件管理陶衅、存儲管理、輸入輸出管理直晨、作業(yè)管理、進程管理
中斷
- CPU 暫停執(zhí)行當前程序膨俐,去執(zhí)行中斷程序
- 中斷的優(yōu)先級指明處理的緊急程度勇皇,如:
機器錯誤 > 時鐘 > 磁盤 > 網(wǎng)絡設備 > 終端 > 軟件中斷
系統(tǒng)調(diào)用
- 2 個級別:核心態(tài)、用戶態(tài)
- 程序執(zhí)行一般在 user model焚刺,需要使用操作系統(tǒng)服務(創(chuàng)建敛摘、讀寫文件),請求切換到 kernel model 執(zhí)行
- 核心態(tài)能存取用戶乳愉、內(nèi)核的指令和數(shù)據(jù)兄淫,用戶態(tài)只能存取用戶自己的指令和數(shù)據(jù)
中斷和系統(tǒng)調(diào)用的關(guān)系:程序申請核心態(tài)時,將產(chǎn)生一個軟件中斷蔓姚,系統(tǒng)將處理
并發(fā)多任務
多道程序:程序運行捕虽, CPU 空閑時(等待 IO),此時 CPU 去運行其他程序
分時系統(tǒng):改進后的多道程序坡脐,程序運行一段時間后會主動讓出 CPU
多任務系統(tǒng):操作系統(tǒng)從底層接管所有硬件資源泄私,程序以進程運行,程序運行超時會被強制暫停
進程
運行中的程序
4 個地址空間
- 文本域:要執(zhí)行的代碼
- 數(shù)據(jù)域:存放變量备闲、程序執(zhí)行期間動態(tài)分配的內(nèi)存
- 堆棧:存放本地變量和指令
3 種狀態(tài)
- 等待態(tài):IO
- 就緒態(tài):等待系統(tǒng)分配 CPU 運行
- 運行態(tài):占用 CPU 正在處理
4 種進程間通信
- 消息傳遞:pipe管道
- 同步:信號量晌端、讀寫鎖
- 內(nèi)存共享
- 遠程過程調(diào)用:RPC
死鎖:多個進程因循環(huán)等待資源而都無法執(zhí)行
線程
輕量級進程
- 解決問題:很多不同的進程需要共享同樣的資源(文件),切換進程的成本很高
- 是可以獨立運行的單位恬砂,切換快速咧纠、開銷小 & 可并發(fā)執(zhí)行
- 共享進程的資源:在同一進程中的線程因為有相應的地址空間,可以共享進程已打開的文件泻骤、定時器等
協(xié)程
- 微線程漆羔,coroutine:用戶級的線程
- 線程是搶占式調(diào)度乳幸、協(xié)程是協(xié)同式調(diào)度,避免無意義的搶占钧椰,但是調(diào)度由用戶決定
IO 多路復用
內(nèi)核發(fā)現(xiàn)某個進程指定的 IO 準備讀取粹断,就會通知該進程,如客戶端處理多個描述符時嫡霞,大大減小創(chuàng)建和維護的開銷瓶埋。
并發(fā)與并行
- 并發(fā):多個操作可在重疊的時間運行
- 并行:同一時刻有多條指令在執(zhí)行,多核是并行的前提诊沪。
數(shù)據(jù)庫
事務:一系列 SQL 集合
ACID 特性
- 原子性 automatic:事務是原子工作單位养筒,要么全部執(zhí)行、要么全部不執(zhí)行
- 一致性 consistency:數(shù)據(jù)庫通過事務完成狀態(tài)轉(zhuǎn)變
- 隔離性 isolation:事務提交前端姚,對數(shù)據(jù)的影響是不可見的
- 持久性 duration:事務完成后晕粪,對數(shù)據(jù)的影響是持久的
4 種隔離級別
- 臟讀:一個事務讀取了另一個事務尚未提交的修改
- 非重復讀:一個事務對同一行數(shù)據(jù)讀取兩次,得到不同結(jié)果
- 幻讀:事務在操作過程中進行了兩次查詢渐裸,第二次的結(jié)果包含了第一次未出現(xiàn)的新數(shù)據(jù)
- 丟失修改:當兩個事務更新相同的數(shù)據(jù)源巫湘,第一個事務提交,第二個撤銷昏鹃。那么第一個也要撤銷
3 種實現(xiàn)隔離的鎖
- 共享鎖 S 鎖:只讀 SELECT 操作尚氛,鎖定共享資源,阻止其他用戶寫數(shù)據(jù)
- 更新鎖 U 鎖:阻止其他用戶更新數(shù)據(jù)
- 獨占鎖 X 鎖:一次只能有一個獨占鎖占用一個資源洞渤,阻止添加其他所有鎖阅嘶。有效防止臟讀
索引
優(yōu)點
- 大大加快檢索速度
- 加快表之間的關(guān)聯(lián)
- 唯一性索引 UNIQUE:保證行數(shù)據(jù)的唯一性
缺點
- 創(chuàng)建和維護消耗時間和物理空間
場景
- 用在經(jīng)常需要連接的字段:如外鍵,加快連接速度
- 用在經(jīng)常需要排序的字段:索引已排序载迄,直接利用讯柔,加快排序時間
OOP
三個基本特征:封裝、繼承护昧、多態(tài)
封裝
- 提取對象的特征魂迄,抽象成類。
- 外部只能訪問 public 的屬性和方法捏卓。private 的屬性和方法內(nèi)部調(diào)用极祸,設置 getter、setter 開放接口允許外部訪問和修改私有數(shù)據(jù)怠晴,protected 的數(shù)據(jù)和方法子類繼承和調(diào)用
繼承:使用現(xiàn)有類的所有功能遥金。
多態(tài):子類覆蓋父類的同名方法
??
Golang 面試題
問答類
1. 在 Go 中如何使用多行字符串?
使用反引號 “ 來包含多行字串蒜田,或使用 +
來連接多行字符串(注意換行會包含\n
稿械,縮進會包含 \t
,空格沒有轉(zhuǎn)義符):
1.func main() {
2. str1 := `
3. line1
4. line2
5.`
6. str2 := "\n line1\n\t" +
7. "line2\n"
8. fmt.Println(str1 == str2) // true
9.}
2. 如何獲取命令行的參數(shù)冲粤?
有兩種方法:
使用 os
庫美莫,如:
1. func main() {
2. args := os.Args
3. if args == nil { // 校驗參數(shù)并輸出提示信息
4. return
5. }
6. fmt.Printf("%T\n", args)
7. fmt.Printf("%v\n", args)
8. }
可以看出 os.Args
接收到的參數(shù)是 string slice页眯,元素分別是運行的程序名、多個參數(shù)值:
使用 flag
庫厢呵,步驟:
- 定義各個參數(shù)的類型窝撵、名字、默認值與提示信息
- 解析
- 獲取參數(shù)值
func main() {
name := flag.String("name", "", "Your name")
var age int
flag.IntVar(&age, "age", -1, "Your age")
flag.Parse()
println("name", *name)
println("age", age)
}
注意上邊獲取參數(shù)值的兩種方式襟铭,使用時也有所不同:
func Int(name string, value string, usage string) *string // 返回地址
func IntVar(p *int, name string, value int, usage string) // 修改第一個參數(shù)值
3. 如何在不輸出的情況下格式化字符串碌奉?
使用 func Sprintf(format string, a ...interface{}) string
即可,常用在手動組合 SQL 語句上:
func main() {
fmt.Println(formatSQL(20))
}
func formatSQL(id int) string {
return fmt.Sprintf("SELECT * FROM users WHERE id=%d", id)
}
4. 如何交換兩個變量的值寒砖?
直接使用元組(tuple)賦值即可:
a, b = b, a
注意元組賦值是對應有序賦值的:
1. a, b, c = b, c, a // 交換三個變量的值
2.
3. a := 1
4. b := 2
5. a, b, a = b, a, b // a = 2, b = 1
5. 如何復制 slice赐劣、map 和 interface 的值?
slice:
func main() {
names := []string{"Tom", "Jerry"}
nums := []string{"one", "two", "three"}
pNames := names // 確認 names 被更新
// names = nums // 直接賦值
// fmt.Println(copy(names, nums)) // 使用 copy
fmt.Println(names, nums, pNames)
}
- 直接賦值, 底層數(shù)組將不會更新:
map:
最簡單的方法,遍歷所有 key:
func main() {
src := map[string]bool{"key1": false, "key2": true}
dst := make(map[string]bool)
for key, value := range src { // 遍歷所有 key
dst[key] = value
}
fmt.Println(dst)
}
interface:
Go 中沒有內(nèi)建的函數(shù)來直接拷貝 interface 的值漠嵌,也不能直接賦值咐汞。如 2 個 struct 的字段完全一致,可以使用強制類型轉(zhuǎn)換或反射來賦值献雅。
參考:關(guān)于結(jié)構(gòu)體復制問題碉考、Copying Interface Values In Go
6. 下邊兩種 slice 的聲明有何不同?哪種更好挺身?
var nums []int
nums := []int{}
第一種如果不使用 nums,就不會為其分配內(nèi)存锌仅,更好(不使用編譯也不會通過)章钾。
寫出程序運行輸出的內(nèi)容
1. 考察多個 defer 與 panic 的執(zhí)行順序
func main() {
deferCall()
}
func deferCall() {
defer func() { fmt.Println("打印前") }()
defer func() { fmt.Println("打印中") }()
defer func() { fmt.Println("打印后") }()
panic("觸發(fā)異常")
}
defer 可以類比為析構(gòu)函數(shù),多個 defer 本身的執(zhí)行是棧 LIFO 先進后出的順序热芹,代碼拋出的 panic 如果在所有 defer 中都不使用 recover 恢復贱傀,則直接退出程序。
如果手動使用 os.Exit()
退出伊脓,則 defer 不執(zhí)行府寒。
2. 考察 defer 與 return 的執(zhí)行順序
func main() {
fmt.Println(double1(5))
fmt.Println(double1(6))
fmt.Println()
fmt.Println(double2(5))
fmt.Println(double2(6))
}
// 匿名返回
// 加倍參數(shù),若結(jié)果超過 10 則還原
func double1(v1 int) int {
var v2 int
defer func() {
if v2 > 10 {
v2 = v1 // v2 不會被修改
}
}()
v2 = v1 * 2
return v2
}
// 有名返回
func double2(v1 int)(v2 int) {
// v2 與函數(shù)一起被聲明报腔,在 defer 中能被修改
defer func() {
if v2 > 10 {
v2 = v1 // v2 被修改
}
}()
v2 = v1 * 2
return
}
注意 return var
會分為三步執(zhí)行:
return 語句為 var
賦值
- 匿名返回值函數(shù):先聲明株搔,再賦值
- 有名返回值函數(shù):直接賦值
檢查是否存在 defer 語句:逆序執(zhí)行多條 defer,有名返回函數(shù)可能會再次修改 var
真正返回 var
到調(diào)用處
3. 考察 goroutine 的傳值方式
func main() {
runtime.GOMAXPROCS(1) // 強制使多個 goroutine 串行執(zhí)行
wg := sync.WaitGroup{}
wg.Add(10)
for i := 0; i < 5; i++ {
go func() {
fmt.Println("i: ", i)
wg.Done()
}()
// time.Sleep(1 * time.Second) // 此時將順序輸出 1 2 3 4 5
}
for i := 0; i < 5; i++ {
go func(i int) {
fmt.Println("i: ", i)
wg.Done()
}(i)
}
wg.Wait()
}
第一個 for 循環(huán):以極快的速度分配完 5 個 goroutine纯蛾,此時 i
的值為 5纤房,gouroutine 得到的 i
都是 5
第二個 for 循環(huán):每次都會將 i
的值拷貝一份傳給 goroutine,得到的 i
不同翻诉,輸出不同
4. 考察 defer 參數(shù)的計算時機
1. func main() {
2. a := 1
3. b := 2
4. defer add("A", a, add("B", a, b))
5. a = 0
6. defer add("C", a, add("D", a, b))
7. b = 1
8. }
9.
10.
11. func add(desc string, a, b int) int {
12. sum := a + b
13. fmt.Println(desc, a, b, sum)
14. return sum
15. }
defer 語句會計算好 func 的參數(shù)炮姨,再放入執(zhí)行棧中捌刮。
注意第 7 行:四個 defer func 的參數(shù)此時已是確定值,不再對 defer 中的 b 造成影響舒岸。
5. 考察 Go 的組合
1. type People struct{}
2.
3. func (p *People) ShowA() {
4. fmt.Println("people showA")
5. p.ShowB()
6. }
7. func (p *People) ShowB() {
8. fmt.Println("people showB")
9. }
10.
11.
12. type Teacher struct {
13. People
14. }
15.
16. func (t *Teacher) ShowB() {
17. fmt.Println("teacher showB")
18. }
19.
20. func main() {
21. t := Teacher{}
22. t.ShowB()
23. t.ShowA()
24. }
第 13 行: Teacher
通過嵌入 People
來獲取了 ShowA()
和 showB()
第 16 行:Teacher
實現(xiàn)并覆蓋了 showB()
第 24 行:調(diào)用未覆蓋的 showA()
绅作,因為它的 receiver 依舊是 People,相當于 People 調(diào)用
來源:https://wuyin.io/2018/03/16/golang-interviews/
添加小編微信:grey0801蛾派,歡迎交流俄认!