以前,人們瀏覽網頁,經常是通過搜索,或者是一些網址導航來點進去。但現在,經常上網的人,都應該能記得住幾個網址了,他們也學會了在瀏覽器的地址欄里直接輸入網站的url來直接訪問網站。但往往沒有人會去研究為什么在瀏覽器里輸入url就能訪問網站呢?這是一個神奇而又復雜的過程,想知道url與網站背后的關系?通過閱讀深圳市星翼創想網絡科技有限公司(專業的網站建設公司)選摘的這篇文章,你就可以了解一些基礎的計算機只是了。萬一跟人談及這個,你的形象會瞬間變得高大上哦!
第一個問題:從輸入 URL 到瀏覽器接收的過程中發生了什么事情?
從觸屏到 CPU
首先是「輸入 URL」,大部分人的第一反應會是鍵盤,不過為了與時俱進,這里將介紹觸摸屏設備的交互。
觸摸屏一種傳感器,目前大多是基于電容(Capacitive)來實現的,以前都是直接覆蓋在顯示屏上的,不過最近出現了 3 種嵌入到顯示屏中的技術,第一種是 iPhone 5 的 In-cell,它能減小了 0.5 毫米的厚度,第二種是三星使用的 On-cell 技術,第三種是國內廠商喜歡用的 OGS 全貼合技術。
當手指在這個傳感器上觸摸時,有些電子會傳遞到手上,從而導致該區域的電壓變化,觸摸屏控制器芯片根據這個變化就能計算出所觸摸的位置,然后通過總線接口將信號傳到 CPU 的引腳上。
以 Nexus 5 為例,它所使用的觸屏控制器是 Synaptics S3350B,總線接口為 I2C,以下是 Synaptics 觸摸屏和處理器連接的示例:
左邊是處理器,右邊是觸摸屏控制器,中間的 SDA 和 SCL 連線就是 I2C 總線接口。
CPU 內部的處理
移動設備中的 CPU 并不是一個單獨的芯片,而是和 GPU 等芯片集成在一起,被稱為 SoC(片上系統)。
前面提到了觸屏和 CPU 的連接,這個連接和大部分計算機內部的連接一樣,都是通過電氣信號來進行通信的,也就是電壓高低的變化,如下面的時序圖:
在時鐘的控制下,這些電流會經過 MOSFET 晶體管,晶體管中包含 N 型半導體和 P 型半導體,通過電壓就能控制線路開閉,然后這些 MOSFET 構成了 CMOS,接著再由 CMOS 實現「與」「或」「非」等邏輯電路門,最后由邏輯電路門上就能實現加法、位移等計算,整體如下圖所示(來自《計算機體系結構》):
除了計算,在 CPU 中還需要存儲單元來加載和存儲數據,這個存儲單元一般通過觸發器(Flip-flop)來實現,稱為寄存器。
以上這些概念都比較抽象,推薦閱讀「How to Build an 8-Bit Computer」這篇文章,作者基于晶體管、二極管、電容等原件制作了一個 8 位的計算機,支持簡單匯編指令和結果輸出,雖然現代 CPU 的實現要比這個復雜得多,但基本原理還是一樣的。
另外其實我也是剛開始學習 CPU 芯片的實現,所以就不在這誤人子弟了,感興趣的讀者請閱讀本節后面推薦的書籍。
從 CPU 到操作系統內核
前面說到觸屏控制器將電氣信號發送到 CPU 對應的引腳上,接著就會觸發 CPU 的中斷機制,以 Linux 為例,每個外部設備都有一標識符,稱為中斷請求(IRQ)號,可以通過 /proc/interrupts 文件來查看系統中所有設備的中斷請求號,以下是 Nexus 7 (2013) 的部分結果:
shell@flo:/$cat/proc/interruptsCPU017: 0 GIC dg_timer294: 1973609 msmgpio elan-ktf3k314: 679 msmgpio KEY_POWER
因為 Nexus 7 使用了 ELAN 的觸屏控制器,所以結果中的 elan-ktf3k 就是觸屏的中斷請求信息,其中 294 是中斷號,1973609 是觸發的次數(手指單擊時會產生兩次中斷,但滑動時會產生上百次中斷)。
為了簡化這里不考慮優先級問題,以 ARMv7 架構的處理器為例,當中斷發生時,CPU 會停下當前運行的程序,保存當前執行狀態(如 PC 值),進入 IRQ 狀態),然后跳轉到對應的中斷處理程序執行,這個程序一般由第三方內核驅動來實現,比如前面提到的 Nexus 7 的驅動源碼在這里 touchscreen/ektf3k.c。
這個驅動程序將讀取 I2C 總線中傳來的位置數據,然后通過內核的 input_report_abs 等方法記錄觸屏按下坐標等信息,最后由內核中的input 子模塊將這些信息都寫進 /dev/input/event0 這個設備文件中,比如下面展示了一次觸摸事件所產生的信息:
130|shell@flo:/$getevent-lt/dev/input/event0[ 414624.658986]EV_ABS ABS_MT_TRACKING_ID 0000835c[ 414624.659017]EV_ABS ABS_MT_TOUCH_MAJOR 0000000b[ 414624.659047]EV_ABS ABS_MT_PRESSURE 0000001d[ 414624.659047]EV_ABS ABS_MT_POSITION_X 000003f0[ 414624.659078]EV_ABS ABS_MT_POSITION_Y 00000588[ 414624.659078]EV_SYN SYN_REPORT 00000000[ 414624.699239]EV_ABS ABS_MT_TRACKING_ID ffffffff[ 414624.699270]EV_SYN SYN_REPORT 00000000
從操作系統 GUI 到瀏覽器
前面提到 Linux 內核已經完成了對硬件的抽象,其它程序只需要通過監聽 /dev/input/event0 文件的變化就能知道用戶進行了哪些觸摸操作,不過如果每個程序都這么做實在太麻煩了,所以在圖像操作系統中都會包含 GUI 框架來方便應用程序開發,比如 Linux 下著名的 X。
但 Android 并沒有使用 X,而是自己實現了一套 GUI 框架,其中有個 EventHub 的服務會通過 epoll 方式監聽 /dev/input/ 目錄下的文件,然后將這些信息傳遞到 Android 的窗口管理服務(WindowManagerService)中,它會根據位置信息來查找相應的 app,然后調用其中的監聽函數(如 onTouch 等)。
就這樣,我們解答了第一個問題,不過由于時間有限,這里省略了很多細節。
第二個問題:瀏覽器如何向網卡發送數據?
從瀏覽器到瀏覽器內核
前面提到操作系統 GUI 將輸入事件傳遞到了瀏覽器中,在這過程中,瀏覽器可能會做一些預處理,比如 Chrome 會根據歷史統計來預估所輸入字符對應的網站,比如輸入了「ba」,根據之前的歷史發現 90% 的概率會訪問「www.baidu.com 」,因此就會在輸入回車前就馬上開始建立 TCP 鏈接甚至渲染了,這里面還有很多其它策略,感興趣的讀者推薦閱讀 High Performance Networking in Chrome。
接著是輸入 URL 后的「回車」,這時瀏覽器會對 URL 進行檢查,首先判斷協議,如果是 http 就按照 Web 來處理,另外還會對這個 URL 進行安全檢查,然后直接調用瀏覽器內核中的對應方法,比如 WebView 中的 loadUrl 方法。
在瀏覽器內核中會先查看緩存,然后設置 UA 等 HTTP 信息,接著調用不同平臺下網絡請求的方法。
需要注意瀏覽器和瀏覽器內核是不同的概念,瀏覽器指的是 Chrome、Firefox,而瀏覽器內核則是 Blink、Gecko,瀏覽器內核只負責渲染,GUI 及網絡連接等跨平臺工作則是瀏覽器實現的
HTTP 請求的發送
因為網絡的底層實現是和內核相關的,所以這一部分需要針對不同平臺進行處理,從應用層角度看主要做兩件事情:通過 DNS 查詢 IP、通過 Socket 發送數據,接下來就分別介紹這兩方面的內容。
DNS 查詢
應用程序可以直接調用 Libc 提供的 getaddrinfo() 方法來實現 DNS 查詢。
DNS 查詢其實是基于 UDP 來實現的,這里我們通過一個具體例子來了解它的查找過程,以下是使用 dig +trace fex.baidu.com 命令得到的結果(省略了一些):
;<<>>DiG9.8.3-P1<<>>+trace fex.baidu.com;;globaloptions:+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.;;Received228bytes from8.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.;;Received503bytes from192.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.;;Received201bytes from192.48.79.30#53(192.48.79.30) in 1237 msfex.baidu.com. 7200 IN CNAME fexteam.duapp.com.fexteam.duapp.com. 300IN 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.;;Received258bytes from61.135.165.235#53(61.135.165.235) in 2 ms
可以看到這是一個逐步縮小范圍的查找過程,首先由本機所設置的 DNS 服務器(8.8.8.8)向 DNS 根節點查詢負責 .com 區域的域務器,然后通過其中一個負責 .com 的服務器查詢負責 baidu.com 的服務器,最后由其中一個 baidu.com 的域名服務器查詢 fex.baidu.com 域名的地址。
可能你在查詢某些域名的時會發現和上面不一樣,最底將看到有個奇怪的服務器搶先返回結果。。。
這里為了方便描述,忽略了很多不同的情況,比如 127.0.0.1 其實走的是 loopback,和網卡設備沒關系;比如 Chrome 會在瀏覽器啟動的時預先查詢 10 個你有可能訪問的域名;還有 Hosts 文件、緩存時間 TTL(Time to live)的影響等。
通過 Socket 發送數據
有了 IP 地址,就可以通過 Socket API 來發送數據了,這時可以選擇 TCP 或 UDP 協議,具體使用方法這里就不介紹了,推薦閱讀 Beej’s Guide to Network Programming。
HTTP 常用的是 TCP 協議,由于 TCP 協議的具體細節到處都能看到,所以本文就不介紹了,這里談一下 TCP 的 Head-of-line blocking 問題:假設客戶端的發送了 3 個 TCP 片段(segments),編號分別是 1、2、3,如果編號為 1 的包傳輸時丟了,即便編號 2 和 3 已經到達也只能等待,因為 TCP 協議需要保證順序,這個問題在 HTTP pipelining 下更嚴重,因為 HTTP pipelining 可以讓多個 HTTP 請求通過一個 TCP 發送,比如發送兩張圖片,可能第二張圖片的數據已經全收到了,但還得等第一張圖片的數據傳到。
為了解決 TCP 協議的性能問題,Chrome 團隊去年提出了 QUIC 協議,它是基于 UDP 實現的可靠傳輸,比起 TCP,它能減少很多來回(round trip)時間,還有前向糾錯碼(Forward Error Correction)等功能。目前 Google Plus、 Gmail、Google Search、blogspot、Youtube 等幾乎大部分 Google 產品都在使用 QUIC,可以通過 chrome://net-internals/#spdy 頁面來發現。
雖然目前除了 Google 還沒人用 QUIC,但我覺得挺有前景的,因為優化 TCP 需要升級系統內核(比如 Fast Open)。
瀏覽器對同一個域名有連接數限制,大部分是 6,我以前認為將這個連接數改大后會提升性能,但實際上并不是這樣的,Chrome 團隊有做過實驗,發現從 6 改成 10 后性能反而下降了,造成這個現象的因素有很多,如建立連接的開銷、擁塞控制等問題,而像 SPDY、HTTP 2.0 協議盡管只使用一個 TCP 連接來傳輸數據,但性能反而更好,而且還能實現請求優先級。
另外,因為 HTTP 請求是純文本格式的,所以在 TCP 的數據段中可以直接分析 HTTP 的文本,如果發現。。。
Socket 在內核中的實現
前面說到瀏覽器的跨平臺庫通過調用 Socket API 來發送數據,那么 Socket API 是如何實現的呢?
以 Linux 為例,它的實現在這里 socket.c,目前我還不太了解,推薦讀者看看 Linux kernel map,它標注出了關鍵路徑的函數,方便學習從協議棧到網卡驅動的實現。
底層網絡協議的具體例子
接下來如果繼續介紹 IP 協議和 MAC 協議可能很多讀者會暈,所以本節將使用 Wireshark 來通過具體例子講解,以下是我請求百度首頁時抓取到的網絡數據:
最底下是實際的二進制數據,中間是解析出來的各個字段值,可以看到其中最底部為 HTTP 協議(Hypertext Transfer Protocol),在 HTTP 之前有 54 字節(0×36),這就是底層網絡協議所帶來的開銷,我們接下來對這些協議進行分析。
在 HTTP 之上是 TCP 協議(Transmission Control Protocol),它的具體內容如下圖所示:
文章轉載請保留網址:http://waterplane.cn/news/industry/1508.html