每日一句
人的痛苦會(huì)把自己折磨到多深呢勉耀?
每日一句
You cannot swim for new horizons until you have courage to lose sight of the shore.
除非有勇氣離開(kāi)岸邊指煎,否則你永遠(yuǎn)游不到彼岸。
概念
IO 是主存和外部設(shè)備(硬盤便斥、終端和網(wǎng)絡(luò)等)拷貝數(shù)據(jù)的過(guò)程至壤。IO是操作系統(tǒng)的底層功能實(shí)現(xiàn),底層通過(guò)I/O指令進(jìn)行完成枢纠。
以下是5種類Unix下可用的I/O模型
阻塞式I/O:Blocking IO
非阻塞式I/O:nonblocking IO
I/O 復(fù)用(Select像街,poll epoll):IO multiplexing
信號(hào)驅(qū)動(dòng)式I/O(SIGIO):signal driven IO
異步 I/O(posix 的 aio 系列函數(shù)):asynchromous IO
Blocking IO
在 Linux 中,默認(rèn)情況下所有的 socket 都是 Blocking晋渺,一個(gè)典型的讀操作流程大概是這樣:
通常涉及等待數(shù)據(jù)從網(wǎng)絡(luò)到達(dá)镰绎。當(dāng)所有等待數(shù)據(jù)到達(dá)時(shí),它被復(fù)制到內(nèi)核中的某個(gè)緩沖區(qū)
把數(shù)據(jù)從內(nèi)核緩沖區(qū)復(fù)制到應(yīng)用程序緩沖區(qū)
當(dāng)用戶進(jìn)程調(diào)用了 recvfrom 這個(gè)系統(tǒng)調(diào)用木西, kernel 就開(kāi)始了 IO 的第一個(gè)階段:準(zhǔn)備數(shù)據(jù)跟狱。對(duì)于 network IO 來(lái)說(shuō),很多時(shí)候數(shù)據(jù)在一開(kāi)始還沒(méi)有到達(dá)(比如户魏,還沒(méi)有收到一個(gè)完整的 UDP 包)驶臊。這個(gè)時(shí)候 kernel 就要等待足夠的數(shù)據(jù)到來(lái)。而在用戶進(jìn)程這邊叼丑,整個(gè)進(jìn)程會(huì)被阻塞关翎。當(dāng) kernel 一直等到數(shù)據(jù)準(zhǔn)備好了,它就會(huì)將數(shù)據(jù)從kernel 中拷貝到用戶內(nèi)存鸠信,然后kernel返回結(jié)果纵寝,用戶進(jìn)程才解除 block 的狀態(tài),重新運(yùn)行起來(lái)星立。
所以爽茴,Blocking IO 的特點(diǎn)就是在IO執(zhí)行的兩個(gè)階段都被 block了
非阻塞式I/O
Linux 下,可以用過(guò)設(shè)置 socket 使其變?yōu)?non-blocking绰垂。當(dāng)對(duì)一個(gè) non-blocking socket 執(zhí)行操作時(shí)室奏,流程如下:
從圖中可以看出,當(dāng)用戶進(jìn)程發(fā)出read操作時(shí)劲装,如果kernel中的數(shù)據(jù)還沒(méi)有準(zhǔn)備好胧沫,那么它并不會(huì)block用戶進(jìn)程,而是立刻返回一個(gè)error占业。 從用戶進(jìn)程角度講 绒怨,它發(fā)起一個(gè)read操作后,并不需要等待谦疾,而是馬上就得到了一個(gè)結(jié)果南蹂。用戶進(jìn)程判斷結(jié)果是一個(gè)error時(shí),它就知道數(shù)據(jù)還沒(méi)有準(zhǔn)備好念恍,于是它可以再次 發(fā)送read操作六剥。一旦kernel中的數(shù)據(jù)準(zhǔn)備好了晚顷,并且又再次收到了用戶進(jìn)程的system call,那么它馬上就將數(shù)據(jù)拷貝到了用戶內(nèi)存仗考,然后返回音同。
所以,用戶進(jìn)程第一個(gè)階段不是阻塞的,需要不斷的主動(dòng)詢問(wèn)kernel數(shù)據(jù)好了沒(méi)有秃嗜;第二個(gè)階段依然總是阻塞的权均。
IO 多路復(fù)用(NIO)
select、epoll 的好處就在于單個(gè) process 就可以同時(shí)處理多個(gè)網(wǎng)絡(luò)鏈接的 IO锅锨。
IO 復(fù)用和同步阻塞本質(zhì)一樣叽赊,不過(guò)利用了新的 Select 系統(tǒng)調(diào)用,由內(nèi)核來(lái)負(fù)責(zé)本來(lái)是請(qǐng)求進(jìn)程該做的輪訓(xùn)操作必搞,看似不非阻塞IO還多了哥系統(tǒng)調(diào)用開(kāi)銷必指,不過(guò)因?yàn)橹С侄嗦稩O才算提高了效率
也就是一個(gè)可以監(jiān)聽(tīng)多個(gè)。
它的基本原理就是 select恕洲、epoll 這個(gè) function會(huì)不斷的輪詢所負(fù)責(zé)的所有 socket塔橡,當(dāng)某個(gè) socket 有數(shù)據(jù)到達(dá)了,就通知用戶進(jìn)程霜第。它的流程如下圖:
當(dāng)用戶線程調(diào)用select葛家,那么整個(gè)進(jìn)程會(huì)被阻塞,而同時(shí)泌类,kernel會(huì)監(jiān)視所有select負(fù)責(zé)的 socket =癞谒,當(dāng)任何一個(gè) socket 中的數(shù)據(jù)準(zhǔn)備好了,select 就會(huì)返回刃榨,這個(gè)時(shí)候用戶進(jìn)程會(huì)調(diào)用 read 操作弹砚,將數(shù)據(jù) kernel 拷貝到用戶進(jìn)程。
首先開(kāi)啟套接字的信號(hào)驅(qū)動(dòng)式IO功能枢希,并且通過(guò)sigaction(信號(hào)處理程序) 系統(tǒng)調(diào)用安裝一個(gè)信號(hào)處理函數(shù) 桌吃,該函數(shù)調(diào)用將立即返回,當(dāng)前進(jìn)程沒(méi)有被阻塞 晴玖,繼續(xù)工作读存;當(dāng)數(shù)據(jù)報(bào)準(zhǔn)備好的時(shí)候,內(nèi)核為該進(jìn)程產(chǎn)生SIGIO 的信號(hào)呕屎,隨后既可以在信號(hào)處理函數(shù)中調(diào)用recvfrom 讀取數(shù)據(jù)報(bào),并且通知主循環(huán)數(shù)據(jù)已經(jīng)準(zhǔn)備好等待處理敬察;也可以直接通知主循環(huán)讓它讀取數(shù)據(jù)報(bào)秀睛;(其實(shí)就是一個(gè)待讀取的通知和待處理的通知),基本不會(huì)用到莲祸。
異步IO(AIO)
多線程和多進(jìn)程的模型雖然解決了并發(fā)的問(wèn)題,但是系統(tǒng)不能無(wú)限的增加線程,由于系統(tǒng)的切換線程的開(kāi)銷恒大简软,所以畦韭,一旦線程數(shù)量過(guò)多察郁,CPU的時(shí)間就花在線程的切換上,正真運(yùn)行代碼的時(shí)間就會(huì)減少酝掩,結(jié)果導(dǎo)致性能嚴(yán)重下降
由于我們要解決的問(wèn)題是CPU高速執(zhí)行能力和IO設(shè)備的龜速嚴(yán)重不匹配,多線程和多進(jìn)程只是解決這一個(gè)問(wèn)題的一種方法。
另一種解決IO問(wèn)題的方法是異步IO,當(dāng)代碼需要執(zhí)行一個(gè)耗時(shí)的IO操作時(shí),他只發(fā)出IO指令脊凰,并不等待IO結(jié)果然后就去執(zhí)行其他代碼,一段時(shí)間后,當(dāng)IO返回結(jié)果是,在通知CPU進(jìn)行處理我們調(diào)用aio_read函數(shù),給內(nèi)核傳遞描述符贷盲,緩沖區(qū)指針,緩沖區(qū)大小佳魔,和文件偏移量断国,并且告訴內(nèi)核當(dāng)整個(gè)操作完成時(shí)如何通知我們,該函數(shù)調(diào)用后薄疚,立即返回碧信,不會(huì)被阻塞
另一方面:從kernel的角度,當(dāng)他收到一個(gè)aio_read之后街夭,首先它立即返回砰碴,所以不會(huì)對(duì)用戶進(jìn)程產(chǎn)生block,然后kernel會(huì)等待數(shù)據(jù)準(zhǔn)備完成板丽,然后將數(shù)據(jù)拷貝到用戶內(nèi)存(copy由內(nèi)核完成)衣式,當(dāng)著一切完成后,kernel會(huì)給用戶進(jìn)程發(fā)送一個(gè)singal或者執(zhí)行下一個(gè)基于線程回調(diào)函數(shù)來(lái)完成此次IO處理過(guò)程檐什,告訴他read操作完成
美文佳句
歲月應(yīng)是靜好碴卧。近來(lái),心神總是不寧乃正,想必是被塵世間的誘惑所困住册。
我想,要是遠(yuǎn)方的祥夫先生來(lái)到我這“梅知堂”瓮具,兩人清茶一杯荧飞,盤腿而坐,或看他畫梅名党,紙上的梅花仿佛兀自開(kāi)在飛雪里叹阔,如是,那該是一幅怎樣的墨梅圖传睹?
昨晚耳幢,夢(mèng)里看到那個(gè)戴著小黑圓眼鏡的祥夫背著黃色的大包正往江南趕路。是又到了梅花開(kāi)的日子了嗎欧啤?
面試題
SpringMVC工作原理睛藻?
1. 客戶端發(fā)送請(qǐng)求到前端控制器 DispatcherServlet
2. DispatcherServlet 收到請(qǐng)求后,調(diào)用 HandlerMapping 處理器映射器邢隧,請(qǐng)求獲取 handler
3. 處理器映射器根據(jù) url 找到具體的處理器店印,生成處理器對(duì)象以及處理器攔截器(如果有則生成)一并返回給 DispatcherServlet
4. DispatcherServlet 調(diào)用 HandlerAdapter 處理器適配器
5. HandlerAdapter 經(jīng)過(guò)適配器調(diào)用 具體處理器(Handler,也叫后端控制器)
6. Handler 執(zhí)行完成返回 ModelAndView
7. HandlerAdaper 將 Handler 執(zhí)行結(jié)果 ModelAndView 返回給 DispatcherServlet
8. DispatcherServlet 將 ModelAndView 傳給 ViewResolver 視圖解析器 進(jìn)行解析
9. ViewResolver 解析后返回具體 View
10. DispatcherServlet 對(duì) view 進(jìn)行 渲染視圖(即將模型數(shù)據(jù)填充至視圖中)
11. DispatcherServlet 響應(yīng)用戶
ArrayList 倒慧、LinkedList和Vector的區(qū)別按摘?
ArrayList | LinkedList | Vector | |
線程是否安全 | 不保證 | 不保證 | 保證線程安全,但是底層大量使用了synchronized關(guān)鍵字纫谅,效率不是很高 |
底層數(shù)據(jù)結(jié)構(gòu) | 數(shù)組炫贤,查詢效率高,多用于查詢較多的場(chǎng)合 | LinkedList 底層是雙向鏈表系宜,插入和刪除非常方便照激,適用于插入較多的場(chǎng)合 | 數(shù)組,查詢效率高盹牧,多用于查詢較多的場(chǎng)合 |
默認(rèn)大小與擴(kuò)容 | JDK 1.7 之前默認(rèn)大小是 10 JDK 1.7 之后是0 每次按照1.5倍擴(kuò)容 | 底層是鏈表結(jié)構(gòu)俩垃,是不連續(xù)的存儲(chǔ)空間,沒(méi)有默認(rèn)大小的說(shuō)法 | Vector擴(kuò)容是2倍 |
@RestController 和 @Controller 有什么區(qū)別汰寓?
@RestController
注解口柳,在 @Controller
基礎(chǔ)上,增加了 @ResponseBody
注解有滑,更加適合目前前后端分離的架構(gòu)下跃闹,提供 Restful API ,返回例如 JSON 數(shù)據(jù)格式。當(dāng)然望艺,返回什么樣的數(shù)據(jù)格式苛秕,根據(jù)客戶端的 "ACCEPT"
請(qǐng)求頭來(lái)決定。
你好找默,我是yltrcc艇劫,日常分享技術(shù)點(diǎn)滴,歡迎關(guān)注我:ylcoder