計算機組成原理(七)——輸入輸出系統(tǒng)
一、輸入輸出接口概述






二、輸入輸出方式












三、中斷請求與響應(yīng)






















四、DMA方式














?



五、習(xí)題




*快速面經(jīng)(go面經(jīng))
go中的協(xié)程
1.1 協(xié)程的定義
在go語言中,協(xié)程被認(rèn)為是輕量級的線程, 和線程不同的是,操作系統(tǒng)內(nèi)核
感知不到協(xié)程的存在, 協(xié)程的管理依賴于Go語言運行時自身提供的調(diào)度器
同時Go語言中的協(xié)程是從屬于某一個線程的。
1.2協(xié)程的調(diào)度方式
協(xié)程是用戶態(tài)的。協(xié)程的管理依賴Go語言運行時的調(diào)度器。同時,Go語言中的協(xié)程是從屬于某一個線程的,協(xié)程與線程的對應(yīng)關(guān)系為M:N,即多對多 .如圖所示, Go語言調(diào)度器可以將多個協(xié)程調(diào)度到一個線程中,一個協(xié)程也可能切換到多個線程中執(zhí)行。
1.3協(xié)程之間的切換
1.協(xié)程的速度要快于線程
2.其原因在于協(xié)程切換不用經(jīng)過操作系統(tǒng)用戶態(tài)與內(nèi)核態(tài)的切換
3.并且Go語言中的協(xié)程切換只需要保留極少的狀態(tài)和寄存器變量值
4.而線程切換會保留額外的寄存器變量值(例如浮點數(shù)寄存器)
1.4調(diào)度策略
線程之間的調(diào)度大部分是搶占式的,協(xié)程的調(diào)度是一般是協(xié)作式的調(diào)度(當(dāng)一個協(xié)程處理完自己的任務(wù)后,可以主動將執(zhí)行權(quán)限讓渡給其他協(xié)程)
1.5 ??臻g大小
線程一般是固定的棧大小,go采用了動態(tài)擴張收縮的策略。
go協(xié)程的調(diào)度
GMP調(diào)度——G(Goroutine): 即Go協(xié)程,每個go關(guān)鍵字都會創(chuàng)建一個協(xié)程。M (Machine): 工作線程,內(nèi)核線程。P (Processor): 處理器 【go定義的一個概念,不是指CPU】,包含運行Go代碼的主要資源,也有調(diào)度goroutine的能力,一般是CPU的內(nèi)核數(shù)。每個P中包含一個local run queue的goroute隊列,調(diào)度器中包含一個全局的goroute隊列。Java中的線程對應(yīng)一個內(nèi)核線程,go中是多對多的關(guān)系,增大了并發(fā)度。
go中的Context
其主要作用是在 goroutine 中進行上下文的傳遞,在傳遞信息中又包含了 goroutine 的運行控制、上下文信息傳遞等功能。
context 與 select-case 聯(lián)合,還可以實現(xiàn)上下文的截止時間、信號控制、信息傳遞等跨 goroutine 的操作,是 Go 語言協(xié)程的重要組成部分。

Go 的垃圾回收
Go語言的GC演進:Go1.0僅支持串行 → Go1.1可以并行 → Go1.3 精確STW →Go1.5 三色并發(fā)+屏障→ Go1.8 混合寫屏障。
Go中GC詳細(xì)描述
標(biāo)記清除法
標(biāo)記階段—>清除階段
三色標(biāo)記法
新建對象是白色,開始gc的時候,從root開始遍歷所有的對象,將遍歷到的對象放入灰色集合中,在對灰色集合進行遍歷,將灰色對象引用的對象從白盒放入灰盒,并把此對蝦干從灰盒放到黑盒。重復(fù)上述步驟,直到灰盒中沒有對象,回收白盒。
屏障機制
“強-弱” 三色不變式
1.強三色不變式:不存在黑色對象引用到白色對象的指針。
2.弱三色不變式:所有被黑色對象引用的白色對象都處于灰色保護狀態(tài) 【即白色對象存在灰色引用】。
總結(jié)
Go1.3版本之前,使用的是普通標(biāo)記清除法,整體過程需要啟動STW,GC性能極差。
Go1.3版本,使用的是簡單優(yōu)化的標(biāo)記清除法,通過將STW的時間提前,GC性能略有提升。
Go1.5版本, 使用改進版的標(biāo)記清除算法——三色標(biāo)記法,堆空間啟動寫屏障,棧空間不啟動,全部掃描之后,需要重新掃描一次棧(這時需要STW),GC性能提升較大。
Go1.8版本,三色標(biāo)記法,混合寫屏障機制, ??臻g不啟動,堆空間啟動。整個過程幾乎不需要STW,GC性能提升很大。
select 和 epoll的區(qū)別
相同點:epoll和select都是多路復(fù)用下的一種機制,多路復(fù)用I/O就是通過一種機制,可以監(jiān)視多個文件描述符,一旦某個文件描述符就緒,就通知程序該文件描述符可以進行讀寫操作。
select():用于確定一個或多個套接口的狀態(tài)。
select的幾大缺點(不可忽視):
1、每次調(diào)用select()的時候,都必須要將fd從用戶態(tài)轉(zhuǎn)換成內(nèi)核態(tài),這個開銷在fd很多的時候非常大。
2、調(diào)用select()的時候,在操作系統(tǒng)內(nèi)核API都會遍歷整個fd集,這會大大影響系統(tǒng)效率。
3、select()可打開的文件描述符的上限太少了,默認(rèn)是1024個。
epoll不用每次遍歷fd,只有初始化的時候遍歷+基于事件回調(diào)的方式獲取fd,epoll可打開的文件沒有上線。
Redis Multi實現(xiàn)原理
redis中的multi是為了實現(xiàn)redis獨特的事務(wù)而存在的,底層是有一個隊列,將Multi后面的命令先進先出的順序加入隊列,在執(zhí)行exce命令,將隊列中的命令取出執(zhí)行。配合redis完成事務(wù)的命令還有watch(會監(jiān)控一個數(shù)據(jù)是否被修改,當(dāng)被修改之后,當(dāng)前事務(wù)直接失效)、discard(取消當(dāng)前事務(wù)的執(zhí)行)。
Redis?AOF和RDB的實現(xiàn)原理
aof和rdb都是redis進行持久化的一種方式,redis持久化分為主動持久化和被動持久化,rdb可以認(rèn)為是redis database,是一種基于快照的redis持久化,rdb持久化的時候,會開啟一個子線程進行持久化,耗時較大。aof的全名叫做文件的尾部添加模式,只對修改的部分進行添加,實效性比較高,但是經(jīng)常修改數(shù)據(jù)會使得aof文件過大,所以在一段時間過后應(yīng)該重建aof。
Redis常用的數(shù)據(jù)結(jié)構(gòu)及原理
redis是一種key-value類型的數(shù)據(jù)庫,key是string類型,value有五種數(shù)據(jù)類型,string、hash、list、set、zset。redis數(shù)據(jù)結(jié)構(gòu)的設(shè)計是非常巧妙,在于內(nèi)斂了數(shù)據(jù)結(jié)構(gòu)的實現(xiàn),用戶不必糾結(jié)于使用什么底層數(shù)據(jù)結(jié)構(gòu),redis會自動切換為最適合的數(shù)據(jù)結(jié)構(gòu)進行存儲數(shù)據(jù)。string的底層有int、embstr還有raw,根據(jù)val的類型、大小從而選擇不同的數(shù)據(jù)結(jié)構(gòu)。hash,底層實現(xiàn)有hashtable和ziplist,list底層有l(wèi)inkedlist和ziplist,在新版本的redis中出現(xiàn)了兩者結(jié)合的quicklist,set的底層有hashtable和intset,zset底層實現(xiàn)有skiplist和ziplist。skiplist是一種高效的類似于二分查找的數(shù)據(jù)結(jié)構(gòu),其適用于經(jīng)常修改的場景。
Go Slice底層原理
go語言的slice的結(jié)構(gòu)有點類似于redis中的sds(簡單動態(tài)字符串(simple dynamic string,SDS)),slice的結(jié)構(gòu)體存儲著len大小、容量大小和底層的數(shù)組的指針,相比于go中的數(shù)組,slice的大小范圍是可變的,slice在進行append操作的時候,當(dāng)?shù)讓拥臄?shù)組的容量不夠的時候需要進行擴容,擴容的規(guī)則是,當(dāng)擴容的容量小于256的時候,一般是兩倍進行擴容,大于256的時候,一般1.25倍進行擴容,直至滿足要求。
arp表的作用?arp的分組格式?對于主機不存在的apr請求會發(fā)生什么?
arp是將ip地址轉(zhuǎn)換成mac地址,arp分組格式包含目的服務(wù)器的mac地址,物理層協(xié)議等,若對不存在的主機發(fā)送arp請求,沒有回應(yīng)。
Mutex中sema鎖是什么?
sema充當(dāng)信號量:用來喚醒goroutine
Mutex的正常模式和饑餓模式?
Mutex分為正常模式和饑餓模式,正常模式是為了減少協(xié)程之間切換的開銷,饑餓模式是為了防止協(xié)程太長時間沒有獲得鎖。正常模式是當(dāng)新進來的協(xié)程在搶奪鎖的時候,會進行自旋進行判斷,當(dāng)鎖占有者釋放鎖的時候,直接將鎖的占用交給自旋者,省去了喚醒和切換的開銷。當(dāng)?shù)却犃械膮f(xié)程等待時間超過一個最大值的時候,mutex切換成饑餓模式,新進入的協(xié)程就不會自旋,然后是通過先進先出的方式急性獲取鎖,當(dāng)隊列里面為空或者隊列里面獲取鎖等待的時間小于某一個值的時候,饑餓模式又會切換成正常模式。
Redis分布式鎖如何實現(xiàn)的
分布式鎖一般需要從幾個方面去考慮,正確性和效率,分布式鎖一般需要一個全局的key,然后持有鎖的用完必須進行釋放鎖,如果忘記釋放,必須要有過期時間。因為redis命令執(zhí)行的時候是以單線程進行執(zhí)行的,符合天然的原子性條件之一,沒有沖突,可以考慮用setnx命令去設(shè)置鎖,值存在的時候才有效,并且在高版本的時候還可以設(shè)置過期時間,解鎖的時候需要加鎖的值進行匹配,防止被其他人進行解鎖。除了用sentx命令之外,還可以用lua腳本多個命令批量執(zhí)行。初次之外,在一些業(yè)務(wù)場景下面,常常用數(shù)據(jù)庫的唯一鍵來做冪等處理。
分布式鎖還有哪些實現(xiàn)方案
用的多的有數(shù)據(jù)庫的唯一索引、redis的單線程實行的原理和zomkeeper等。
線程池是用來處理啥的、使用的業(yè)務(wù)場景、解決什么業(yè)務(wù)問題
線程池存在是為加快響應(yīng)處理業(yè)務(wù),減少了線程創(chuàng)建的等待時間,線程池主要是對工作的線程進行統(tǒng)一管理,從內(nèi)存、調(diào)度和GC的角度去考慮,線程池在業(yè)務(wù)上應(yīng)用很廣,比如MySQL連接池,TomCat的連接池等。
讓你實現(xiàn)一個RPC框架,應(yīng)該要考慮哪些點
協(xié)議、壓縮方式、端口號、序列化。
阻塞IO和非阻塞IO有什么區(qū)別
文件從磁盤或者網(wǎng)卡傳輸?shù)接脩艟彌_區(qū)的時候,會先將數(shù)據(jù)拷貝到內(nèi)核區(qū),在從內(nèi)核去拷貝到用戶區(qū),阻塞時只從數(shù)據(jù)從外部拷貝到內(nèi)核區(qū)的時候,用戶進程時阻塞還是輪詢,輪詢時非常消耗cpu資源,阻塞的話,應(yīng)用進程進行休眠。