從觸屏到 CPU
首先是「輸入 URL」,大部分人的第一反應(yīng)會是鍵盤,不過為了與時俱進(jìn),這里將介紹觸摸屏設(shè)備的交互。
觸摸屏一種傳感器,目前大多是基于電容(Capacitive)來實(shí)現(xiàn)的,以前都是直接覆蓋在顯示屏上的,不過最近出現(xiàn)了 3 種嵌入到顯示屏中的技術(shù),第一種是 iPhone 5 的 In-cell,它能減小了 0.5 毫米的厚度,第二種是三星使用的 On-cell 技術(shù),第三種是國內(nèi)廠商喜歡用的 OGS 全貼合技術(shù),具體細(xì)節(jié)可以閱讀這篇文章。
當(dāng)手指在這個傳感器上觸摸時,有些電子會傳遞到手上,從而導(dǎo)致該區(qū)域的電壓變化,觸摸屏控制器芯片根據(jù)這個變化就能計(jì)算出所觸摸的位置,然后通過總線接口將信號傳到 CPU 的引腳上。
以 Nexus 5 為例,它所使用的觸屏控制器是 Synaptics S3350B,總線接口為 I2C,以下是 Synaptics 觸摸屏和處理器連接的示例:
左邊是處理器,右邊是觸摸屏控制器,中間的 SDA 和 SCL 連線就是 I2C 總線接口。CPU 內(nèi)部的處理
移動設(shè)備中的 CPU 并不是一個單獨(dú)的芯片,而是和 GPU 等芯片集成在一起,被稱為 SoC(片上系統(tǒng))。
在時鐘的控制下,這些電流會經(jīng)過 MOSFET 晶體管,晶體管中包含 N 型半導(dǎo)體和 P 型半導(dǎo)體,通過電壓就能控制線路開閉,然后這些 MOSFET 構(gòu)成了 CMOS,接著再由 CMOS 實(shí)現(xiàn)「與」「或」「非」等邏輯電路門,最后由邏輯電路門上就能實(shí)現(xiàn)加法、位移等計(jì)算,整體如下圖所示(來自《計(jì)算機(jī)體系結(jié)構(gòu)》):
除了計(jì)算,在 CPU 中還需要存儲單元來加載和存儲數(shù)據(jù),這個存儲單元一般通過觸發(fā)器(Flip-flop)來實(shí)現(xiàn),稱為寄存器。
以上這些概念都比較抽象,推薦閱讀「How to Build an 8-Bit Computer」這篇文章,作者基于晶體管、二極管、電容等原件制作了一個 8 位的計(jì)算機(jī),支持簡單匯編指令和結(jié)果輸出,雖然現(xiàn)代 CPU 的實(shí)現(xiàn)要比這個復(fù)雜得多,但基本原理還是一樣的。
另外其實(shí)我也是剛開始學(xué)習(xí) CPU 芯片的實(shí)現(xiàn),所以就不在這誤人子弟了,感興趣的讀者請閱讀本節(jié)后面推薦的書籍。
從 CPU 到操作系統(tǒng)內(nèi)核
前面說到觸屏控制器將電氣信號發(fā)送到 CPU 對應(yīng)的引腳上,接著就會觸發(fā) CPU 的中斷機(jī)制,以 Linux 為例,每個外部設(shè)備都有一標(biāo)識符,稱為中斷請求(IRQ)號,可以通過 /proc/interrupts 文件來查看系統(tǒng)中所有設(shè)備的中斷請求號,以下是 Nexus 7 (2013) 的部分結(jié)果:
shell@flo:/ $ cat /proc/interrupts CPU0 17: 0 GIC dg_timer 294: 1973609 msmgpio elan-ktf3k 314: 679 msmgpio KEY_POWER
因?yàn)?Nexus 7 使用了 ELAN 的觸屏控制器,所以結(jié)果中的 elan-ktf3k 就是觸屏的中斷請求信息,其中 294 是中斷號,1973609 是觸發(fā)的次數(shù)(手指單擊時會產(chǎn)生兩次中斷,但滑動時會產(chǎn)生上百次中斷)。
為了簡化這里不考慮優(yōu)先級問題,以 ARMv7 架構(gòu)的處理器為例,當(dāng)中斷發(fā)生時,CPU 會停下當(dāng)前運(yùn)行的程序,保存當(dāng)前執(zhí)行狀態(tài)(如 PC 值),進(jìn)入 IRQ 狀態(tài)),然后跳轉(zhuǎn)到對應(yīng)的中斷處理程序執(zhí)行,這個程序一般由第三方內(nèi)核驅(qū)動來實(shí)現(xiàn).
這個驅(qū)動程序?qū)⒆x取 I2C 總線中傳來的位置數(shù)據(jù),然后通過內(nèi)核的 input_report_abs 等方法記錄觸屏按下坐標(biāo)等信息,最后由內(nèi)核中的 input 子模塊將這些信息都寫進(jìn) /dev/input/event0
這個設(shè)備文件中.
從操作系統(tǒng) GUI 到瀏覽器
前面提到 Linux 內(nèi)核已經(jīng)完成了對硬件的抽象,其它程序只需要通過監(jiān)聽 /dev/input/event0 文件的變化就能知道用戶進(jìn)行了哪些觸摸操作,不過如果每個程序都這么做實(shí)在太麻煩了,所以在圖像操作系統(tǒng)中都會包含 GUI 框架來方便應(yīng)用程序開發(fā),比如 Linux 下著名的 X。
但 Android 并沒有使用 X,而是自己實(shí)現(xiàn)了一套 GUI 框架,其中有個 EventHub 的服務(wù)會通過 epoll 方式監(jiān)聽 /dev/input/ 目錄下的文件,然后將這些信息傳遞到 Android 的窗口管理服務(wù)(WindowManagerService)中,它會根據(jù)位置信息來查找相應(yīng)的 app,然后調(diào)用其中的監(jiān)聽函數(shù)(如 onTouch 等)。
就這樣,我們解答了第一個問題,不過由于時間有限,這里省略了很多細(xì)節(jié),想進(jìn)一步學(xué)習(xí)的讀者推薦閱讀以下書籍。擴(kuò)展學(xué)習(xí)
《計(jì)算機(jī)體系結(jié)構(gòu)》《計(jì)算機(jī)體系結(jié)構(gòu):量化研究方法》《計(jì)算機(jī)組成與設(shè)計(jì):硬件/軟件接口》《編碼》《CPU自制入門》《操作系統(tǒng)概念》《ARMv7-AR 體系結(jié)構(gòu)參考手冊》《Linux內(nèi)核設(shè)計(jì)與實(shí)現(xiàn)》《精通Linux設(shè)備驅(qū)動程序開發(fā)》
瀏覽器如何向網(wǎng)卡發(fā)送數(shù)據(jù)?
從瀏覽器到瀏覽器內(nèi)核
前面提到操作系統(tǒng) GUI 將輸入事件傳遞到了瀏覽器中,在這過程中,瀏覽器可能會做一些預(yù)處理,比如 Chrome 會根據(jù)歷史統(tǒng)計(jì)來預(yù)估所輸入字符對應(yīng)的網(wǎng)站,比如輸入了「ba」,根據(jù)之前的歷史發(fā)現(xiàn) 90% 的概率會訪問「www.baidu.com 」,因此就會在輸入回車前就馬上開始建立 TCP 鏈接甚至渲染了,這里面還有很多其它策略,感興趣的讀者推薦閱讀 High Performance Networking in Chrome。
接著是輸入 URL 后的「回車」,這時瀏覽器會對 URL 進(jìn)行檢查,首先判斷協(xié)議,如果是 http 就按照 Web 來處理,另外還會對這個 URL 進(jìn)行安全檢查,然后直接調(diào)用瀏覽器內(nèi)核中的對應(yīng)方法,比如 WebView 中的 loadUrl 方法。
在瀏覽器內(nèi)核中會先查看緩存,然后設(shè)置 UA 等 HTTP 信息,接著調(diào)用不同平臺下網(wǎng)絡(luò)請求的方法。
需要注意瀏覽器和瀏覽器內(nèi)核是不同的概念,瀏覽器指的是 Chrome、Firefox,而瀏覽器內(nèi)核則是Blink、Gecko,瀏覽器內(nèi)核只負(fù)責(zé)渲染,GUI 及網(wǎng)絡(luò)連接等跨平臺工作則是瀏覽器實(shí)現(xiàn)的
HTTP 請求的發(fā)送
因?yàn)榫W(wǎng)絡(luò)的底層實(shí)現(xiàn)是和內(nèi)核相關(guān)的,所以這一部分需要針對不同平臺進(jìn)行處理,從應(yīng)用層角度看主要做兩件事情:通過 DNS 查詢 IP、通過 Socket 發(fā)送數(shù)據(jù),接下來就分別介紹這兩方面的內(nèi)容。
DNS 查詢
應(yīng)用程序可以直接調(diào)用 Libc 提供的 getaddrinfo() 方法來實(shí)現(xiàn) DNS 查詢。DNS 查詢其實(shí)是基于 UDP 來實(shí)現(xiàn)的,這里我們通過一個具體例子來了解它的查找過程,以下是使用 dig +trace fex.baidu.com
命令得到的結(jié)果(省略了一些):
; <<>> DiG 9.8.3-P1 <<>> +trace fex.baidu.com;; global options: +cmd. 11157 IN NS g.root-servers.net.. 11157 IN NS i.root-servers.net.. 11157 IN NS j.root-servers.net.. 11157 IN NS a.root-servers.net.. 11157 IN NS l.root-servers.net.;; Received 228 bytes from 8.8.8.8#53(8.8.8.8) in 220 mscom. 172800 IN NS a.gtld-servers.net.com. 172800 IN NS c.gtld-servers.net.com. 172800 IN NS m.gtld-servers.net.com. 172800 IN NS h.gtld-servers.net.com. 172800 IN NS e.gtld-servers.net.;; Received 503 bytes from 192.36.148.17#53(192.36.148.17) in 185 msbaidu.com. 172800 IN NS dns.baidu.com.baidu.com. 172800 IN NS ns2.baidu.com.baidu.com. 172800 IN NS ns3.baidu.com.baidu.com. 172800 IN NS ns4.baidu.com.baidu.com. 172800 IN NS ns7.baidu.com.;; Received 201 bytes from 192.48.79.30#53(192.48.79.30) in 1237 msfex.baidu.com. 7200 IN CNAME fexteam.duapp.com.fexteam.duapp.com. 300 IN CNAME duapp.n.shifen.com.n.shifen.com. 86400 IN NS ns1.n.shifen.com.n.shifen.com. 86400 IN NS ns4.n.shifen.com.n.shifen.com. 86400 IN NS ns2.n.shifen.com.n.shifen.com. 86400 IN NS ns5.n.shifen.com.n.shifen.com. 86400 IN NS ns3.n.shifen.com.;; Received 258 bytes from 61.135.165.235#53(61.135.165.235) in 2 ms
可以看到這是一個逐步縮小范圍的查找過程,首先由本機(jī)所設(shè)置的 DNS 服務(wù)器(8.8.8.8)向 DNS 根節(jié)點(diǎn)查詢負(fù)責(zé) .com 區(qū)域的域務(wù)器,然后通過其中一個負(fù)責(zé) .com 的服務(wù)器查詢負(fù)責(zé) baidu.com 的服務(wù)器,最后由其中一個 baidu.com 的域名服務(wù)器查詢 fex.baidu.com 域名的地址。
可能你在查詢某些域名的時會發(fā)現(xiàn)和上面不一樣,最底將看到有個奇怪的服務(wù)器搶先返回結(jié)果。。。
這里為了方便描述,忽略了很多不同的情況,比如 127.0.0.1 其實(shí)走的是 loopback,和網(wǎng)卡設(shè)備沒關(guān)系;比如 Chrome 會在瀏覽器啟動的時預(yù)先查詢 10 個你有可能訪問的域名;還有 Hosts 文件、緩存時間 TTL(Time to live)的影響等。
通過 Socket 發(fā)送數(shù)據(jù)
有了 IP 地址,就可以通過 Socket API 來發(fā)送數(shù)據(jù)了,這時可以選擇 TCP 或 UDP 協(xié)議,具體使用方法這里就不介紹了,推薦閱讀 Beej's Guide to Network Programming。
HTTP 常用的是 TCP 協(xié)議,由于 TCP 協(xié)議的具體細(xì)節(jié)到處都能看到,所以本文就不介紹了,這里談一下 TCP 的 Head-of-line blocking 問題:假設(shè)客戶端的發(fā)送了 3 個 TCP 片段(segments),編號分別是 1、2、3,如果編號為 1 的包傳輸時丟了,即便編號 2 和 3 已經(jīng)到達(dá)也只能等待,因?yàn)?TCP 協(xié)議需要保證順序,這個問題在 HTTP pipelining 下更嚴(yán)重,因?yàn)?HTTP pipelining 可以讓多個 HTTP 請求通過一個 TCP 發(fā)送,比如發(fā)送兩張圖片,可能第二張圖片的數(shù)據(jù)已經(jīng)全收到了,但還得等第一張圖片的數(shù)據(jù)傳到。
為了解決 TCP 協(xié)議的性能問題,Chrome 團(tuán)隊(duì)去年提出了 QUIC 協(xié)議,它是基于 UDP 實(shí)現(xiàn)的可靠傳輸,比起 TCP,它能減少很多來回(round trip)時間,還有前向糾錯碼(Forward Error Correction)等功能。目前 Google Plus、 Gmail、Google Search、blogspot、Youtube 等幾乎大部分 Google 產(chǎn)品都在使用 QUIC,可以通過 chrome://net-internals/#spdy 頁面來發(fā)現(xiàn)。
雖然目前除了 Google 還沒人用 QUIC,但我覺得挺有前景的,因?yàn)閮?yōu)化 TCP 需要升級系統(tǒng)內(nèi)核(比如 Fast Open)。
瀏覽器對同一個域名有連接數(shù)限制,大部分是 6,我以前認(rèn)為將這個連接數(shù)改大后會提升性能,但實(shí)際上并不是這樣的,Chrome團(tuán)隊(duì)有做過實(shí)驗(yàn),發(fā)現(xiàn)從 6 改成 10 后性能反而下降了,造成這個現(xiàn)象的因素有很多,如建立連接的開銷、擁塞控制等問題,而像SPDY、HTTP 2.0 協(xié)議盡管只使用一個 TCP 連接來傳輸數(shù)據(jù),但性能反而更好,而且還能實(shí)現(xiàn)請求優(yōu)先級。
另外,因?yàn)?HTTP 請求是純文本格式的,所以在 TCP 的數(shù)據(jù)段中可以直接分析 HTTP 的文本,如果發(fā)現(xiàn)。。。
Socket 在內(nèi)核中的實(shí)現(xiàn)
前面說到瀏覽器的跨平臺庫通過調(diào)用 Socket API 來發(fā)送數(shù)據(jù),那么 Socket API 是如何實(shí)現(xiàn)的呢?
以 Linux 為例,它的實(shí)現(xiàn)在這里 socket.c,目前我還不太了解,推薦讀者看看 Linux kernel map,它標(biāo)注出了關(guān)鍵路徑的函數(shù),方便學(xué)習(xí)從協(xié)議棧到網(wǎng)卡驅(qū)動的實(shí)現(xiàn)。
底層網(wǎng)絡(luò)協(xié)議的具體例子
接下來如果繼續(xù)介紹 IP 協(xié)議和 MAC 協(xié)議可能很多讀者會暈,所以本節(jié)將使用 Wireshark 來通過具體例子講解,以下是我請求百度首頁時抓取到的網(wǎng)絡(luò)數(shù)據(jù):
最底下是實(shí)際的二進(jìn)制數(shù)據(jù),中間是解析出來的各個字段值,可以看到其中最底部為 HTTP 協(xié)議(Hypertext Transfer Protocol),在 HTTP 之前有 54 字節(jié)(0x36),這就是底層網(wǎng)絡(luò)協(xié)議所帶來的開銷,我們接下來對這些協(xié)議進(jìn)行分析。
在 HTTP 之上是 TCP 協(xié)議(Transmission Control Protocol),它的具體內(nèi)容如下圖所示:
通過底部的二進(jìn)制數(shù)據(jù),可以看到 TCP 協(xié)議是加在 HTTP 文本前面的,它有 20 個字節(jié),其中定義了本地端口(Source port)和目標(biāo)端口(Destination port)、順序序號(Sequence Number)、窗口長度等信息,以下是 TCP 協(xié)議各個部分?jǐn)?shù)據(jù)的完整介紹:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Source Port | Destination Port |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Sequence Number |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Acknowledgment Number |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Data | |U|A|E|R|S|F| || Offset| Reserved |R|C|O|S|Y|I| Window || | |G|K|L|T|N|N| |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Checksum | Urgent Pointer |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Options | Padding |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| data |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
具體各個字段的作用這里就不介紹了,感興趣的讀者可以閱讀 RFC 793,并結(jié)合抓包分析來理解。
因?yàn)?IP 協(xié)議中可以查看到目標(biāo) IP 地址,所以如果發(fā)現(xiàn)某些特定的 IP 地址,某些路由器就會。。。
但是,光靠 IP 地址是無法進(jìn)行通信的,因?yàn)?IP 地址并不和某臺設(shè)備綁定,比如你的筆記本的 IP 在家中是 192.168.1.1,但到公司就變成 172.22.22.22 了,所以在底層通信時需要使用一個固定的地址,這就是 MAC(media access control) 地址,每個網(wǎng)卡出廠時的 MAC 地址都是固定且唯一的。
最頂上的 Frame 是代表 Wireshark 的抓包序號,并不是網(wǎng)絡(luò)協(xié)議
就這樣,我們解答了第二個問題,不過其實(shí)這里面還有很多很多細(xì)節(jié)沒介紹,建議大家通過下面的書籍進(jìn)一步學(xué)習(xí)。
擴(kuò)展學(xué)習(xí)
《計(jì)算機(jī)網(wǎng)絡(luò):自頂向下方法與Internet特色》《計(jì)算機(jī)網(wǎng)絡(luò)》《Web性能權(quán)威指南》
數(shù)據(jù)如何從本機(jī)網(wǎng)卡發(fā)送到服務(wù)器?
從內(nèi)核到網(wǎng)絡(luò)適配器(Network Interface Card)
前面說到調(diào)用 Socket API 后內(nèi)核會對數(shù)據(jù)進(jìn)行底層協(xié)議棧的封裝,接下來啟動 DMA 控制器,它將從內(nèi)存中讀取數(shù)據(jù)寫入網(wǎng)卡。
以 Nexus 5 為例,它使用的是博通 BCM4339 芯片通信,接口采用了 SD 卡一樣的 SDIO,但這個芯片的細(xì)節(jié)并沒有公開資料,所以這里就不討論了。
連接 Wi-Fi 路由
Wi-Fi 網(wǎng)卡需要通過 Wi-Fi 路由來與外部通信,原理是基于無線電,通過電流變化來產(chǎn)生無線電,這個過程也叫「調(diào)制」,而反過來無線電可以引起電磁場變化,從而產(chǎn)生電流變化,利用這個原理就能將無線電中的信息解讀出來就叫「解調(diào)」,其中單位時間內(nèi)變化的次數(shù)就稱為頻率,目前在 Wi-Fi 中所采用的頻率分為 2.4 GHz 和 5 GHz 兩種。
在同一個 Wi-Fi 路由下,因?yàn)椴捎玫念l率相同,同時使用時會發(fā)生沖突,為了解決這個問題,Wi-Fi 采用了被稱為 CSMA/CA 的方法,簡單來說就是在傳輸前先確認(rèn)信道是否已被使用,沒有才發(fā)送數(shù)據(jù)。
而同樣基于無線電原理的 2G/3G/LTE 也會遇到類似的問題,但它并沒有采用 Wi-Fi那樣的獨(dú)占方案,而是通過頻分(FDMA)、時分(TDMA)和碼分(CDMA)來進(jìn)行復(fù)用,具體細(xì)節(jié)這里就不展開了。
以小米路由為例,它使用的芯片是 BCM 4709,這個芯片由 ARM Cortex-A9 處理器及流量(Flow)硬件加速組成,使用硬件芯片可以避免經(jīng)過操作系統(tǒng)中斷、上下文切換等操作,從而提升了性能。
路由器中的操作系統(tǒng)可以基于 OpenWrt 或 DD-WRT 來開發(fā)的,具體細(xì)節(jié)我不太了解,所以就不展開了。
因?yàn)閮?nèi)網(wǎng)設(shè)備的 IP 都是類似 192.168.1.x 這樣的內(nèi)網(wǎng)地址,外網(wǎng)無法直接向這個地址發(fā)送數(shù)據(jù),所以網(wǎng)絡(luò)數(shù)據(jù)在經(jīng)過路由時,路由會修改相關(guān)地址和端口,這個操作稱為 NAT 映射。
最后家庭路由一般會通過雙絞線連接到運(yùn)營商網(wǎng)絡(luò)的。
運(yùn)營商網(wǎng)絡(luò)內(nèi)的路由
數(shù)據(jù)過雙絞線發(fā)送到運(yùn)營商網(wǎng)絡(luò)后,還會經(jīng)過很多個中間路由轉(zhuǎn)發(fā),讀者可以通過 traceroute 命令或者在線可視化工具來查看這些路由的 ip 和位置。
當(dāng)數(shù)據(jù)傳遞到這些路由器后,路由器會取出包中目的地址的前綴,通過內(nèi)部的轉(zhuǎn)發(fā)表查找對應(yīng)的輸出鏈路,而這個轉(zhuǎn)發(fā)表是如何得到的呢?這就是路由器中最重要的選路算法了,可選的有很多,我對這方面并不太了解,看起來維基百科上的詞條列得很全。
主干網(wǎng)間的傳輸
對于長線的數(shù)據(jù)傳輸,通常使用光纖作為介質(zhì),光纖是基于光的全反射來實(shí)現(xiàn)的,使用光纖需要專門的發(fā)射器通過電致發(fā)光(比如 LED)將電信號轉(zhuǎn)成光,比起前面介紹的無線電和雙絞線,光纖信號的抗干擾性要強(qiáng)得多,而且能耗也小很多。
既然是基于光來傳輸數(shù)據(jù),數(shù)據(jù)傳輸速度也就取決于光的速度,在真空中的光速接近于 30 萬千米/秒,由于光纖包層(cladding)中的折射率(refractive index)為 1.52,所以實(shí)際光速是 20 萬千米/秒左右,從首都機(jī)場飛往廣州白云機(jī)場的距離是 1967 千米,按照這個距離來算需要花費(fèi) 10 毫秒才能抵達(dá)。這意味著如果你在北京,服務(wù)器在廣州,等你發(fā)出數(shù)據(jù)到服務(wù)器返回?cái)?shù)據(jù)至少得等 20 毫秒,實(shí)際情況預(yù)計(jì)是 2- 3 倍,因?yàn)檫@其中還有各個節(jié)點(diǎn)路由處理的耗時,比如我測試了一個廣州的 IP 發(fā)現(xiàn)平均延遲為 60 毫秒。
這個延遲是現(xiàn)有科技無法解決的(除非找到超過光速的方法),只能通過 CDN 來讓傳輸距離變短,或盡量減少串行的來回請求(比如 TCP 建立連接所需的 3 次握手)。
IDC 內(nèi)網(wǎng)
數(shù)據(jù)通過光纖最終會來到服務(wù)器所在的 IDC 機(jī)房,進(jìn)入 IDC 內(nèi)網(wǎng),這時可以先通過分光器將流量鏡像一份出來方便進(jìn)行安全檢查等分析,還能用來進(jìn)行。。。
這里的帶寬成本很高,是按照峰值來結(jié)算的,以每月每 Gbps(注意這里指的是 bit,而不是Byte)為單位,北京這邊價(jià)格在十萬人民幣以上,一般網(wǎng)站使用 1G 到 10G 不等。
上圖左邊是正面,右邊是側(cè)面,可以看到頂部為交換機(jī)所留的位置。
以前這些交換機(jī)的內(nèi)部實(shí)現(xiàn)是封閉的,相關(guān)廠商(如思科、Juniper 等)會使用特定的處理器和操作系統(tǒng),外界難以進(jìn)行靈活控制,甚至有時候需要手工配置,但這幾年隨著 OpenFlow 技術(shù)的流行,也出現(xiàn)了開放交換機(jī)硬件(Open Switch Hardware),比如 Intel 的網(wǎng)絡(luò)平臺,推薦感興趣的讀者建議看看它的視頻,比文字描述清晰多了。
需要注意的是,一般網(wǎng)絡(luò)書中提到的交換機(jī)都只具備二層(MAC 協(xié)議)的功能,但在 IDC 中的交換器基本上都具備三層(IP協(xié)議)的功能,所以不需要有專門的路由了。
最后,因?yàn)?CPU 處理的是電氣信號,所以光纖中的光線需要先使用相關(guān)設(shè)備通過光電效應(yīng)將光信號轉(zhuǎn)成電信號,然后進(jìn)入服務(wù)器網(wǎng)卡。
服務(wù)器 CPU
前面說到數(shù)據(jù)已經(jīng)到達(dá)服務(wù)器網(wǎng)卡了,接著網(wǎng)卡會將數(shù)據(jù)拷貝到內(nèi)存中(DMA),然后通過中斷來通知 CPU,目前服務(wù)器端的 CPU 基本上都是 Intel Xeon,不過這幾年出現(xiàn)了一些新的架構(gòu),比如在存儲領(lǐng)域,百度使用 ARM 架構(gòu)來提升存儲密度,因?yàn)?ARM 的功耗比 Xeon 低得多。而在高性能領(lǐng)域,Google 最近在嘗試基于 POWER 架構(gòu)的 CPU 來開發(fā)的服務(wù)器,最新的 POWER8 處理器可以并行執(zhí)行 96 個線程,所以對高并發(fā)的應(yīng)用應(yīng)該很有幫助。
擴(kuò)展學(xué)習(xí)
The Datacenter as a ComputerOpen Computer《軟件定義網(wǎng)絡(luò)》《大話無線通信》
服務(wù)器接收到數(shù)據(jù)后會進(jìn)行哪些處理?
為了避免重復(fù),這里將不再介紹操作系統(tǒng),而是直接進(jìn)入后端服務(wù)進(jìn)程,由于這方面有太多技術(shù)選型,所以我只挑幾個常見的公共部分來介紹。
負(fù)載均衡
請求在進(jìn)入到真正的應(yīng)用服務(wù)器前,可能還會先經(jīng)過負(fù)責(zé)負(fù)載均衡的機(jī)器,它的作用是將請求合理地分配到多個服務(wù)器上,同時具備具備防攻擊等功能。
負(fù)載均衡具體實(shí)現(xiàn)有很多種,有直接基于硬件的 F5,有操作系統(tǒng)傳輸層(TCP)上的 LVS,也有在應(yīng)用層(HTTP)實(shí)現(xiàn)的反向代理(也叫七層代理),接下來將介紹 LVS 及反向代理。
負(fù)載均衡的策略也有很多,如果后面的多個服務(wù)器性能均衡,最簡單的方法就是挨個循環(huán)一遍(Round-Robin),其它策略就不一一介紹了,可以參考 LVS 中的算法。
LVS
LVS 的作用是從對外看來只有一個 IP,而實(shí)際上這個 IP 后面對應(yīng)是多臺機(jī)器,因此也被成為 Virtual IP。
前面提到的 NAT 也是一種 LVS 中的工作模式,除此之外還有 DR 和 TUNNEL,具體細(xì)節(jié)這里就不展開了,它們的缺點(diǎn)是無法跨網(wǎng)段,所以百度自己開發(fā)了 BVS 系統(tǒng)。
反向代理
方向代理是工作在 HTTP 上的,具體實(shí)現(xiàn)可以基于 HAProxy 或 Nginx,因?yàn)榉聪虼砟芾斫?HTTP 協(xié)議,所以能做非常多的事情,比如:
進(jìn)行很多統(tǒng)一處理,比如防攻擊策略、放抓取、SSL、gzip、自動性能優(yōu)化等應(yīng)用層的分流策略都能在這里做,比如對 /xx 路徑的請求分到 a 服務(wù)器,對 /yy 路徑的請求分到 b 服務(wù)器,或者按照 cookie 進(jìn)行小流量測試等緩存,并在后端服務(wù)掛掉的時候顯示友好的 404 頁面監(jiān)控后端服務(wù)是否異常??
Nginx 的代碼寫得非常優(yōu)秀,從中能學(xué)到很多,對高性能服務(wù)端開發(fā)感興趣的讀者一定要看看。
Web Server 中的處理
請求經(jīng)過前面的負(fù)載均衡后,將進(jìn)入到對應(yīng)服務(wù)器上的 Web Server,比如 Apache、Tomcat、Node.JS 等。
以 Apache 為例,在接收到請求后會交給一個獨(dú)立的進(jìn)程來處理,我們可以通過編寫 Apache 擴(kuò)展來處理,但這樣開發(fā)起來太麻煩了,所以一般會調(diào)用 PHP 等腳本語言來進(jìn)行處理,比如在 CGI 下就是將 HTTP 中的參數(shù)放到環(huán)境變量中,然后啟動 PHP 進(jìn)程來執(zhí)行,或者使用 FastCGI 來預(yù)先啟動進(jìn)程。
(等后續(xù)有空再單獨(dú)介紹 Node.JS 中的處理)
進(jìn)入后端語言
前面說到 Web Server 會調(diào)用后端語言進(jìn)程來處理 HTTP 請求(這個說法不完全正確,有很多其它可能),那么接下來就是后端語言的處理了,目前大部分后端語言都是基于虛擬機(jī)的,如 PHP、Java、JavaScript、Python 等,但這個領(lǐng)域的話題非常大,難以講清楚,對 PHP 感興趣的讀者可以閱讀我之前寫的 HHVM 介紹文章,其中提到了很多虛擬機(jī)的基礎(chǔ)知識。
Web 框架(Framework)
如果你的 PHP 只是用來做簡單的個人主頁「Personal Home Page」,倒沒必要使用 Web 框架,但如果隨著代碼的增加會變得越來越難以管理,所以一般網(wǎng)站都會會基于某個 Web 框架來開發(fā),因此在后端語言執(zhí)行時首先進(jìn)入 Web 框架的代碼,然后由框架再去調(diào)用應(yīng)用的實(shí)現(xiàn)代碼。
可選的 Web 框架非常多,這里就不一一介紹了。
讀取數(shù)據(jù)
這部分不展開了,從簡單的讀寫文件到數(shù)據(jù)中間層,這里面可選的方案實(shí)在太多。
擴(kuò)展學(xué)習(xí)
《深入理解Nginx》《Python源碼剖析》《深入理解Java虛擬機(jī)》《數(shù)據(jù)庫系統(tǒng)實(shí)現(xiàn)》
服務(wù)器返回?cái)?shù)據(jù)后瀏覽器如何處理?
前面說到服務(wù)端處理完請求后,結(jié)果將通過網(wǎng)絡(luò)發(fā)回客戶端的瀏覽器,從本節(jié)開始將介紹瀏覽器接收到數(shù)據(jù)后的處理,值得一提的是這方面之前有一篇不錯的文章 How Browsers Work,所以很多內(nèi)容我不想再重復(fù)介紹,因此將重點(diǎn)放在那篇文章所忽略的部分。
從 01 到字符
HTTP 請求返回的 HTML 傳遞到瀏覽器后,如果有 gzip 會先解壓,然后接下來最重要的問題是要知道它的編碼是什么,比如同樣一個「中」字,在 UTF-8 編碼下它的內(nèi)容其實(shí)是「11100100 10111000 10101101」也就是「E4 B8 AD」,而在 GBK 下則是「11010110 11010000」,也就是「D6 D0」,如何才能知道文件的編碼?可以有很多判斷方法:
用戶設(shè)置,在瀏覽器中可以指定頁面編碼HTTP 協(xié)議中<meta> 中的 charset 屬性值對于 JS 和 CSS對于 iframe
如果在這些地方都沒指明,瀏覽器就很難處理,在它看來就是一堆「0」和「1」,比如「中文」,它在 UTF-8 下有 6 個字節(jié),如果按照 GBK 可以當(dāng)成「涓枃」這 3 個漢字來解釋,瀏覽器怎么知道到底是「中文」還是「涓枃」呢?
不過正常人一眼就能認(rèn)出「涓枃」是錯的,因?yàn)檫@ 3 個字太不常見了,所以有人就想到通過判斷常見字的方法來檢測編碼,典型的比如 Mozilla 的 UniversalCharsetDetection,不過這東東誤判率也很高,所以還是指明編碼的好。
這樣后續(xù)對文本的操作就是基于「字符」(Character)的了,一個漢字就是一個字符,不用再關(guān)心它究竟是 2 個字節(jié)還是 3 個字節(jié)。
JavaScript 的執(zhí)行
(后續(xù)再單獨(dú)介紹,推薦大家看 R 大去年整理的這個帖子,里面有非常多相關(guān)資料,另外我兩年前曾講過 JavaScript 引擎中的性能優(yōu)化,雖然有些內(nèi)容不太正確了,但也可以看看)
從字符到圖片
二維渲染中最復(fù)雜的要數(shù)文字顯示了,雖然想想似乎很簡單,不就是將某個文字對應(yīng)的字形(glyph)找出來么?在中文和英文中這樣做是沒問題的,因?yàn)橐粋€字符就對應(yīng)一個字形(glyph),在字體文件中找到字形,然后畫上去就可以了,但在阿拉伯語中是不行的,因?yàn)樗杏羞B體形式。
(以后續(xù)再單獨(dú)介紹,這里非常復(fù)雜)
跨平臺 2D 繪制庫
在不同操作系統(tǒng)中都提供了自己的圖形繪制 API,比如 Mac OS X 下的 Quartz,Windows 下的 GDI 以及 Linux 下的 Xlib,但它們相互不兼容,所以為了方便支持跨平臺繪圖,在 Chrome 中使用了 Skia 庫。
(以后再單獨(dú)介紹,Skia 內(nèi)部實(shí)現(xiàn)調(diào)用層級太多,直接講代碼可能不適合初學(xué)者)
GPU 合成
(以后續(xù)再單獨(dú)介紹,雖然簡單來說就是靠貼圖,但還得介紹 OpenGL 以及 GPU 芯片,內(nèi)容太長)
擴(kuò)展學(xué)習(xí)
這節(jié)內(nèi)容是我最熟悉,結(jié)果反而因?yàn)檫@樣才想花更多時間寫好,所以等到以后再發(fā)出來好了,大家先可以先看看以下幾個站點(diǎn):
ChromiumMozilla HacksSurfin' Safari
瀏覽器如何將頁面展現(xiàn)出來?
前面提到瀏覽器已經(jīng)將頁面渲染成一張圖片了,接下來的問題就是如何將這張圖片展示在屏幕上。Framebuffer
以 Linux 為例,在應(yīng)用中控制屏幕最直接的方法是將圖像的 bitmap 寫入 /dev/fb0 文件中,這個文件實(shí)際上一個內(nèi)存區(qū)域的映射,這段內(nèi)存區(qū)域稱為 Framebuffer。
需要注意的是在硬件加速下,如 OpenGL 是不經(jīng)過 Framebuffer 的。
從內(nèi)存到 LCD
在手機(jī)的 SoC 中通常都會有一個 LCD 控制器,當(dāng) Framebuffer 準(zhǔn)備好后,CPU 會通過 AMBA 內(nèi)部總線通知 LCD 控制器,然后這個控制器讀取 Framebuffer 中的數(shù)據(jù),進(jìn)行格式轉(zhuǎn)換、伽馬校正等操作,最終通過 DSI、HDMI 等接口發(fā)往 LCD 顯示器。
以 OMAP5432 為例,下圖是它所支持的一種并行數(shù)據(jù)傳輸:
LCD 顯示
最后簡單介紹一下 LCD 的顯示原理。
首先,要想讓人眼能看見,就必須有光線進(jìn)入,要么通過反射、要么有光源,比如 Kindle 所使用的 E-ink 屏幕本身是不發(fā)光的,所以必須在有光線的地方才能閱讀,它的優(yōu)點(diǎn)是省電,但限制太大,所以幾乎所有 LCD 都會自帶光源。
目前 LCD 中通常使用 LED 作為光源,LED 接上電源后,在電壓的作用下,內(nèi)部的正負(fù)電子結(jié)合會釋放光子,從而產(chǎn)生光,這種物理現(xiàn)象叫電致發(fā)光(Electroluminescence),這在前面介紹光纖時也介紹過。
以下是 iPod Touch 2 拆開后的樣子:(來自 Wikipedia):
在上圖中可以看到 6 盞 LED,這就是整個屏幕的光源,這些光源將通過反射的反射輸出到屏幕中。
有了光源還得有色彩,在 LED 中通常做法是使用彩色濾光片(Color filter)來將 LED 光源轉(zhuǎn)成不同顏色。
另外直接使用三種顏色的 LED 也是可行的,它能避免了濾光導(dǎo)致的光子浪費(fèi),降低耗電,很適用于智能手表這樣的小屏幕,Apple 收購的LuxVue 公司就采用的是這種方式,感興趣的話可以去研究它的專利
LCD 屏幕上的每個物理像素點(diǎn)實(shí)際上是由紅、綠、藍(lán) 3 種色彩的點(diǎn)組成,每個顏色點(diǎn)能單獨(dú)控制,下面是用顯微鏡放大后的情況(來自 Wikipedia):
從上圖可以看到每 3 種顏色的濾光片都全亮的時候就是白色,都滅就是黑色,如果你仔細(xì)看還能看到有些點(diǎn)并不是完全黑,這是字體上的反鋸齒效果。
通過這 3 種顏色亮度的不同組合就能產(chǎn)生出各種色彩,如果每個顏色點(diǎn)能產(chǎn)生 256 種亮度,就能生成 256 *256 *256 = 16777216 種色彩。
并不是所有顯示器的亮度都能達(dá)到 256,在選擇顯示器時有個參數(shù)是 8-Bit 或 6-Bit 面板,其中 8-Bit 的面板能在物理上達(dá)到256 種亮度,而 6-Bit 的則只有 64 種,它需要靠刷新率控制(Frame rate control)技術(shù)來達(dá)到 256 的效果。
如何控制這些顏色點(diǎn)的亮度?這就要靠液晶體了,液晶體的特性是當(dāng)有電流通過時會發(fā)生旋轉(zhuǎn),從而將部分光線擋住,所以只要通過電壓控制液晶體的轉(zhuǎn)動就能控制這個顏色點(diǎn)的亮度,目前手機(jī)屏幕中通常使用 TFT 控制器來對其進(jìn)行控制,在 TFT 中最著名的要數(shù) IPS 面板。
這些過濾后的光線大部分會直接進(jìn)入眼睛,有些光還會在其它表面上經(jīng)過漫(diffuse)反射或鏡面(specular)反射后再進(jìn)入眼睛,加上環(huán)境光的影響,要真正算出有多少光到眼睛是一個積分問題,感興趣的讀者可以研究基于物理的渲染。
當(dāng)光線進(jìn)入眼睛后,接下來就是生物學(xué)的領(lǐng)域了,所以我們到此結(jié)束。
擴(kuò)展學(xué)習(xí)
《Computer Graphics, 3rd Edition : Principles and Practices》《交互式計(jì)算機(jī)圖形學(xué)》
本文所忽略的內(nèi)容
為了編寫方便,前面的介紹中將很多底層細(xì)節(jié)實(shí)現(xiàn)忽略了,比如:
內(nèi)存相關(guān) 堆,這里的分配策略有很多,比如 malloc 的實(shí)現(xiàn) 棧,函數(shù)調(diào)用,已經(jīng)有很多優(yōu)秀的文章或書籍介紹了 內(nèi)存映射,動態(tài)庫加載等 隊(duì)列幾乎無處不在,但這些細(xì)節(jié)和原理沒太大關(guān)系各種緩存 CPU 的緩存、操作系統(tǒng)的緩存、HTTP 緩存、后端緩存等等各種監(jiān)控 很多日志會保存下來以便后續(xù)分析
FAQ
從微博反饋來看,有些問題被經(jīng)常問到,我就在這里統(tǒng)一回答吧,如果有其它問題請?jiān)谠u論中問。
Q:學(xué)那么多有什么用?根本用不著
A:計(jì)算機(jī)是人類最強(qiáng)大的工具,你不想了解它是如何運(yùn)作的么?
Q:什么都了解一點(diǎn),還不如精通一項(xiàng)吧?
A:非常認(rèn)同,初期肯定需要先在某個領(lǐng)域精通,然后再去了解周邊領(lǐng)域的知識,這樣還能讓你對之前那個領(lǐng)域有更深刻的理解。
Q:曬出來培養(yǎng)一堆面霸跟自己過不去?
A:本文其實(shí)寫得很淺,每個部分都能再深入展開。
Q:這題要把人累死啊,說幾天都說不完的
A:哈哈哈,大神你暴露了,題目只是手段,目的是將你這樣的大牛挖掘出來。