一、Redis為什麼這麼快
因為Redis采取了epoll模型,IO多路複用。同步非阻塞。
二、什麼是epoll
介紹epoll之前必須要先介紹下select和poll。明白select和poll的缺點後才能知道為什麼epoll這麼快。
三、什麼是select
1、源碼
2、源碼解析
2.1、核心代碼
// select(xx,讀檔案描述符集合,寫檔案描述符集合,異常檔案描述符集合,逾時時間),我們隻需要關注讀檔案描述符集合,其他NULL不影響,因為有預設值。
select(max+1, &rset, NULL, NULL, NULL)
2.2、&rset是什麼?
rset其實就是fds(檔案描述符的集合)。而select模型存儲fds的方式是采取的bitmap,預設最大1024個。
2.3、執行流程
1.select模型每次都直接将rset(也就是fds)全部拷貝到核心态,因為核心态速度比使用者空間态快很多。
2.如果沒資料的話,select函數會阻塞,如果有資料的話會執行兩步
(1)将有資料的那個fd置位(也就是标記一下,代表這個fd有資料)
(2)select函數不在阻塞,将繼續往下執行。也就是整體周遊fds,找到有資料的那個fd讀取資料做處理。他的fd不能重用,每一次都需要重新建立新的fds且将使用者空間态的fds拷貝到核心态。(這個可以看上面while(1)的代碼)
3、缺點
- fds最大支援1024個(可以更改,但是意義不大)
- fd不可重用,每次核心态都給置位了,導緻每次都需要重新執行如下四句話建立一個新的rset(也就是fds)
- 使用者控件态拷貝rset到核心态也需要時間,雖然核心态執行比使用者态快,但是copy也需要開銷
- O(n)再次周遊問題。因為rset裡的fd被置位後,select函數并不知道哪個被置位了,需要從頭周遊到尾,逐個對比。
四、什麼是poll
1.源碼
2、源碼解析
2.1、核心代碼
// 代表傳入了5個pollfd這個結構體,逾時時間是5000,poll整體就是圍繞他的pollfd這個結構體來展開的
poll(pollfds, 5, 5000)
2.2、結構體
poll的結構體是為了fd重複利用,不需要每次都拷貝到核心态用的。
revents:置位用的,如果某個fd有資料了,就把這個revents置位為POLLIN
2.3、執行流程
- 有資料的時候流程如下
- 将pollfd這個結構體裡的revents這個字段置位為POLLIN
- 然後判斷這個結構體的revents字段是不是被置位為POLLIN了,是的話再将其結構體的這個字段恢複預設值0,然後取出資料,處理邏輯。
- 恢複為預設值0的用途是不需要在像select一樣每次都需要重新建立fds,而是直接複用。但還是避免不了每個fd第一次複制一次。好的是不用每次都建立新的了。
3、解決了select哪些問題
- 采取的連結清單存儲,而不是bitmap,解決了1024長度限制問題
- 采取結構體每次置位結構體内的revents字段,而不破壞fd本身,是以可重用,不需要每次都建立新的fd。
4、缺點
- 使用者控件态拷貝rset到核心态也需要時間,雖然核心态執行比使用者态快,但是copy也需要開銷
- O(n)再次周遊問題。因為rset裡的fd被置位後,select函數并不知道哪個被置位了,需要從頭周遊到尾,逐個對比。
五、再談epoll
0、概述
epoll的出現就是為了解決poll最後兩個缺點的。
1、源碼
2、源碼解析
2.1、核心代碼
epoll_create 相當于建立白闆
epoll_ctl相當于往白闆上寫字,寫的内容是一個fd、一個events結構體,events結構體跟pollfd的結構體很像。
epoll_ctl執行完後就得到了epfd,epfd就是循環了五個fd-events放裡面,每個fd都有events事件
epoll_wait
2.2、執行流程
- epoll将fd放到了紅黑樹裡,且不需要拷貝到核心态,因為他采取了“共享記憶體”的概念。(其實還是複制,隻是複制采取了其他技術可以使開銷極其的小)
- epoll的置位是重排,比如五個fd, 1 2 3 4 5,1 3 5這三個fd有資料了,那麼他會重排序,排成如下1 3 5 2 4。(也有的說是單獨放到新的數組裡)
- 每一次置位nfds的值都+1。且會回調epoll_wait
- 是以epoll_wait執行完會傳回有幾個fd有資料,那麼下面的for直接周遊nfds次即可。解決了前面的兩種O(n)。變成了O1
六、阻塞/非阻塞
使用者程序通過調用select/poll/epoll去處理socket請求的時候, select/poll/epoll由于是阻塞的,是以導緻使用者程序阻塞;而select/poll/epoll處理的socket用的是非阻塞
七、再次分析Redis
比如三個redis-cli,假設2個redis-cli寫入指令,
select:那麼select模型是輪詢這三個redis-cli的fd,看哪個fd有消息,有的話讀取處理消息。當他下次再寫指令的時候還需要重新建立fd,然後複制到核心态然後再周遊全部。
poll:那麼poll模型是輪詢這三個redis-cli的fd,看哪個fd有消息,有的話讀取處理消息。下次再寫入的時候還是周遊全局fd,看哪個fd有消息進行處理。省去了每次都建立新的fd且複制的過程。
epoll:epoll就不輪詢了,有消息進來後你通知我,我去處理你的消息,那些沒消息的fd我不管。而且複制到核心态的過程我采取牛逼的技術讓開銷達到最小的極緻。
八、總結
-
select、poll、epoll必須懂,redis面試90%問
-