面經:服務器相關

阻塞IO

當你去讀一個阻塞的文件描述符時,如果在該文件描述符上沒有數據可讀,那么它會一直阻塞(通俗一點就是一直卡在調用函數那里),直到有數據可讀。當你去寫一個阻塞的文件描述符時,如果在該文件描述符上沒有空間(通常是緩沖區)可寫,那么它會一直阻塞,直到有空間可寫。以上的讀和寫我們統一指在某個文件描述符進行的操作,不單單指真正的讀數據,寫數據,還包括接收連接accept(),發起連接connect()等操作…

非阻塞IO

當你去讀寫一個非阻塞的文件描述符時,不管可不可以讀寫,它都會立即返回,返回成功說明讀寫操作完成了,返回失敗會設置相應errno狀態碼,根據這個errno可以進一步執行其他處理。它不會像阻塞IO那樣,卡在那里不動!!!

Level_triggered(水平觸發)

當被監控的文件描述符上有可讀寫事件發生時,epoll_wait()會通知處理程序去讀寫。如果這次沒有把數據一次性全部讀寫完(如讀寫緩沖區太小),那么下次調用 epoll_wait()時,它還會通知你在上沒讀寫完的文件描述符上繼續讀寫,當然如果你一直不去讀寫,它會一直通知你!!!如果系統中有大量你不需要讀寫的就緒文件描述符,而它們每次都會返回,這樣會大大降低處理程序檢索自己關心的就緒文件描述符的效率!!!

LT的缺點

LT模式下,可寫狀態的fd會一直觸發事件,該怎么處理這個問題

方法1:每次要寫數據時,將fd綁定EPOLLOUT事件,寫完后將fd同EPOLLOUT從epoll中移除。

方法2:方法一中每次寫數據都要操作epoll。如果數據量很少,socket很容易將數據發送出去。可以考慮改成:數據量很少時直接send,數據量很多時在采用方法1
為什么ET模式下一定要設置非阻塞?

因為ET模式下是無限循環讀,直到出現錯誤為EAGAIN或者EWOULDBLOCK,這兩個錯誤表示socket為空,不用再讀了,然后就停止循環了,如果是阻塞,循環讀在socket為空的時候就會阻塞到那里,主線程的read()函數一旦阻塞住,當再有其他監聽事件過來就沒辦法讀了,給其他事情造成了影響,所以必須要設置為非阻塞。
最大TCP連接數是多少?

理論上是等于 客戶端IP數?客戶端的端口數,即2的32次方?2的16次方即2的48次方,但是實際上受到文件描述符數量限制和內存空間限制。

Edge_triggered(邊緣觸發)

當被監控的文件描述符上有可讀寫事件發生時,epoll_wait()會通知處理程序去讀寫。如果這次沒有把數據全部讀寫完(如讀寫緩沖區太小),那么下次調用epoll_wait()時,它不會通知你,也就是它只會通知你一次,直到該文件描述符上出現第二次可讀寫事件才會通知你!!!這種模式比水平觸發效率高,系統不會充斥大量你不關心的就緒文件描述符!!!
所以ET所以循環處理,保證能將數據讀取完畢,即同時要保證非阻塞IO,不然最后會被阻塞

Reactor

主線程往epoll內核上注冊socket讀事件,主線程調用epoll_wait等待socket上有數據可讀,當socket上有數據可讀的時候,主線程把socket可讀事件放入請求隊列。睡眠在請求隊列上的某個工作線程被喚醒,處理客戶請求,然后往epoll內核上注冊socket寫請求事件。主線程調用epoll_wait等待寫請求事件,當有事件可寫的時候,主線程把socket可寫事件放入請求隊列。睡眠在請求隊列上的工作線程被喚醒,處理客戶請求。

Proactor

主線程調用aio_read函數向內核注冊socket上的讀完成事件,并告訴內核用戶讀緩沖區的位置,以及讀完成后如何通知應用程序,主線程繼續處理其他邏輯,當socket上的數據被讀入用戶緩沖區后,通過信號告知應用程序數據已經可以使用。應用程序預先定義好的信號處理函數選擇一個工作線程來處理客戶請求。工作線程處理完客戶請求之后調用aio_write函數向內核注冊socket寫完成事件,并告訴內核寫緩沖區的位置,以及寫完成時如何通知應用程序。主線程處理其他邏輯。當用戶緩存區的數據被寫入socket之后內核向應用程序發送一個信號,以通知應用程序數據已經發送完畢。應用程序預先定義的數據處理函數就會完成工作。

reactor模式和Proactor模式對比

reactor模式:同步阻塞I/O模式,注冊對應讀寫事件處理器,等待事件發生進而調用事件處理器處理事件。 proactor模式:異步I/O模式。

Reactor和Proactor模式的主要區別就是真正的讀取和寫入操作是有誰來完成的,Reactor中需要應用程序自己讀取或者寫入數據,Proactor模式中,應用程序不需要進行實際讀寫過程。

Reactor:非阻塞同步網絡模型,可以理解為:來了事件我通知你,你來處理

Proactor:異步網絡模型,可以理解為:來了事件我來處理,處理完了我通知你。

理論上:Proactor比Reactor效率要高一些。

webserver相關

并發模型

面向對象的Reactor模型,和面向過程的程序不同,面向對象的程序耦合度更低,可以將每一個行為分離開來,形成一個個事物,如果某一個事物阻塞了或者出現了異常可以及時切換到其他事物中,不會阻塞住整個系統,muduo網絡庫,Redis和Nginx都使用了Reactor模式。通過多線程或者多進程技術,提高了系統的效率。本系統采用多線程提高并發度,因為多線程粒度更小切換的消耗更低,性能更好。使用線程池是為避免線程頻繁創建和銷毀帶來的開銷,在程序的開始創建固定數量的線程,線程數量和計算機內存的數量保持一致可以有較好的CPU利用率,通常IO多的程序開更多的線程,CPU操作多的程序開更少的線程。使用Epoll作為IO多路復用的實現方式,為了調試方便在mac使用了Kqueue作為實現方式。
一個主Reactor主要用來處理accept的連接并負責分配client的連接請求給副Reactor,它是一種面向對象的IO復用模式。在建立連接后用輪詢的方式分配給工作線程,因為涉及到多線程的任務分配會有競爭問題,可以使用eventfd或者條件變量實現異步喚醒工作線程,線程會從Epoll_wait中醒來,獲取從主線程中獲取活躍連接進行處理,主線程會刪除這個連接,這也類似于生產者消費者模型,主線程處理新的連接,等第二次來數據的連接時喚醒消費者線程處理,本系統這里的互斥鎖由某個特定線程中loop創建,不會出現驚群的情況只會被該線程和主線程中使用。

定時任務功能實現

服務器程序通常管理著眾多定時事件,因此有效地組織這些定時事件,使之能在預期的時間點被觸發且不影響服務器的主要邏輯,對于服務器的性能有著至關重要的影響。為此,我們要將每個定時事件分別封裝成定時器,并使用某種容器類數據結構,比如鏈表、排序鏈表和時間輪,將所有定時器串聯起來,以實現對定時事件的統一管理。本項目是為了方便釋放那些超時的非活動連接,關閉被占用的文件描述符,才使用定時器。
每個副Reactor持有一個定時器,用于處理超時請求和長時間不活躍的連接。定時器可以使用時間輪,紅黑樹,雙向鏈表和小根堆實現,我使用了使用了C++標準庫中的priority_queue優先隊列,它底層是堆,通過惰性刪除的方法提高效率,因為在時間片到期的時候并不會馬上刪除超時節點,而是每次連接通信結束的時候循環的檢查,會將超時的節點全部刪除,由于小根堆的特點越早超時的節點越在堆的上方,檢查時間隊列的間隔和頻率減少了,效率就會提高。

Epoll邊緣模式

Epoll的觸發模式在這里我選擇了ET模式,muduo使用的是LT,這兩者IO處理上有很大的不同。ET模式要比LE復雜許多,它對用戶提出了更高的要求,即每次讀,必須讀到不能再讀直到出現EAGAIN,每次寫,寫到不能再寫知道出現EAGAIN。而LT則簡單的多,可以選擇也這樣做,也可以為編程方便,比如每次只read一次,muduo就是這樣做的,這樣可以減少系統調用次數。

線程池模塊

線程池是使用了已經創建好的線程進行循環處理任務,避免了大量線程的頻繁創建與銷毀的成本。
實現思想:利用生產者消費者隊列,創建多個線程并全部初始化去運行,通過條件變量判斷隊列中是否有任務,沒有任務就等待,當有任務時就可以通過條件變量來喚醒阻塞中的線程去處理這個任務。
代碼實現:類主要有兩個類,一個類是任務類,一個類是線程池類,其中任務類有兩個成員變量,一個是數據和處理數據方式的一個函數指針,成員函數有兩個,一個是用于接收任務和數據的處理方式的函數,一個是run函數,其執行這個處理數據的函數。線程池類中的成員變量主要有線程池的最大數量、一個緩沖隊列、一個互斥鎖,用來保護對隊列的操作、一個條件變量,用于實現線程池中線程的同步。threadpool_create是線程的的入口函數,每個線程都在一個死循環里等待任務,當有任務到來,就可以獲取隊列中的任務對象,然后去執行任務對象中的回調函數來處理數據。

核心模塊

Channel類:Channel和一個 EventLoop綁定是一種事物,在Channel類管理一個fd文件描述符,存儲這個事件的數據類型event以及對應的函數,當事件活躍的時候會調用到之前保存在類中的函數。因此,程序中所有帶有讀寫時間的對象都會和一個Channel關聯,包括loop中的eventfd,listenfd,HttpData等。
EventLoop:One loop per thread意味著每個線程只能有一個EventLoop對象,EventLoop即是時間循環,每次從poller里拿活躍事件,并給到Channel里分發處理。EventLoop中的loop函數會在最底層Thread中被真正調用,開始無限的循環,直到某一輪的檢查到退出狀態后從底層一層一層的退出。

建立連接

建立連接的時候服務器端首先使用socket()創建套接字,其次使用bind()將如IPv4綁定到套接字,最后調用listen()監聽連接,使用Epoll IO復用的ET邊緣模式監聽listenfd的讀請求,數據的通信已經由操作系統幫我們完成了,這里的通信是指3次握手的過程,這個過程不需要應用程序參與,當應用程序感知到連接時,此時該連接已經完成了3次握手的過程,accept()會在收到最后ACK文件后放回。另一個原因是一般情況下,連接是客戶端主動發起,服務器端被動連接,也不會出現同時建立的情況。檢查這個連接的fd描述符,如果是第一次建立連接則會講這個描述符加入Epoll的紅黑數中。第二次同一個連接活躍的時候就可以講這個fd加入Epoll的就緒隊列中,使用ET模式會比LT電平模式麻煩,與LT不同的是,LT模式當有活躍事物的時候會不停的觸發提醒直到處理完這個事物,而ET模式的活躍事物只會提醒一次,因此開發的時候要無限循環讀直到就緒連接為空。
假設server只監聽一個端口,一個連接就是一個四元組原ip,原port,對端ip,對端port,那么理論上可以建立2^48個連接,可是,fd可沒有這么多這是由于操作系統限制和用戶進程限制實際上主要首先于內存,可以通過Linux中的終端命令ulimit臨時修改fd數量,通過vim /etc/security/limits.conf文件中修改永久的fd數量。為了避免連接滿了無法處理新的連接,新的連接會阻塞在connect()上,防止空等,在第一次客戶端發起半連接包后就不在處理的DOS攻擊,避免就緒隊列滿了后會導致新連接無法建立。本系統采取的方案是參考muduo網絡庫,準備一個空的文件描述符,限制最大連接數,不要在連接滿了才進行處理,達到一定數量的連接后,accept()阻塞返回后直接close(),這樣對端不會收到RST,客戶端可以知道服務器還存活著而不是宕機。

優雅關閉

通常server和client都可以主動發Fin來關閉連接,這是TCP的特點全雙工連接。對于client(非Keep-Alive),發送完請求后就可以半關閉寫端,這個時候就是告訴客戶端服務器端不在寫數據但是可以繼續讀數據,這也是符合TCP協議的邏輯,然后收到server發來的應答后讀空read(),最后關閉連接。也可以不使用shutdown()半關閉寫端,等讀完直接close()。對于Keep-Alive長連接的情況,需要觀察客戶端的行為,服務器端應該保證不主動斷開主動權掌握在客戶端手里。

共享內存

使用mmap加速內核與用戶空間的消息傳遞。無論是select,poll還是epoll都需要內核把FD消息通知給用戶空間,如何避免不必要的內存拷貝就很重要,在這點上,epoll是通過內核于用戶空間mmap同一塊內存實現的。(mmap 內存共享 少一份拷貝吧)

epoll開發相關

1、單個epoll并不能解決所有問題,特別是你的每個操作都比較費時的時候,因為epoll是串行處理的。 所以你有還是必要建立線程池來發揮更大的效能。
2、如果fd被注冊到兩個epoll中時,如果有時間發生則兩個epoll都會觸發事件。
3、如果注冊到epoll中的fd被關閉,則其會自動被清除出epoll監聽列表。
4、如果多個事件同時觸發epoll,則多個事件會被聯合在一起返回。
5、epoll_wait會一直監聽epollhup事件發生,所以其不需要添加到events中。
6、為了避免大數據量io時,et模式下只處理一個fd,其他fd被餓死的情況發生。linux建議可以在fd聯系到的結構中增加ready位,然后epoll_wait觸發事件之后僅將其置位為ready模式,然后在下邊輪詢ready fd列表
ET模式僅當狀態發生變化的時候才獲得通知,這里所謂的狀態的變化并不包括緩沖區中還有未處理的數據,也就是說,如果要采用ET模式,需要一直read/write直到出錯為止,很多人反映為什么采用ET模式只接收了一部分數據就再也得不到通知了,大多因為這樣;而LT模式是只要有數據沒有處理就會一直通知下去的.

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/447124.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/447124.shtml
英文地址,請注明出處:http://en.pswp.cn/news/447124.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何用區塊鏈保障數據安全和承載數據確權

區塊鏈可以確保數據安全,體現在那些方面呢? 主要是兩個維度,一是數據的不可篡改性;另外一個就是數據的隱私安全性。區塊鏈技術本身并不解決任何的安全問題,因此需要搭配安全技術一起使用,比如非對稱加密、…

面經:單例模式

侯捷單例 和劍指不同  餓漢式 餓漢式的特點是一開始就加載了,如果說懶漢式是“時間換空間”,那么餓漢式就是“空間換時間”,因為一開始就創建了實例,所以每次用到的之后直接返回就好了。餓漢式有兩種常見的寫法&…

屬性加密技術及基于屬性的ABE算法的訪問控制技術介紹

屬性加密技術 基于身份的加密體制簡介 基于身份的加密體制可以看作一種特殊的公鑰加密,它有如下特點:系統中用戶的公鑰可以由任意的字符串組成。這些字符串可以是用戶在現實中的身份信息,如:身份證號碼、用戶姓名、電話號碼、Email地址等,因…

面經:http協議

總結HTTPS傳輸過程 客戶端先從服務器獲取到證書,證書中包含公鑰 客戶端將證書進行校驗 客戶端生成一個對稱密鑰,用證書中的公鑰進行加密,發送給服務器 服務器得到這個請求后用私鑰進行解密,得到該密鑰 客戶端以后發出后續的請求&…

基于屬性加密的ABE算法的應用場景思考展望

ABE算法先前使用在云計算場景中,和區塊鏈存在交叉應用場景,具體問題體現在 數據的異地存儲、云服務器提供商的不可信、管理員能否對自身數據擁有足夠的控制能力以及如何保證數據的安全有效共享都是亟需解決的問題。 研究背景: 云計算越來越…

面經:設計模式

什么是接口隔離原則(Interface Segregation Principle) 定義:客戶端不應該依賴它不需要的接口;一個類對另一個類的依賴應該建立在最小的接口上。概括的說就是:建立單一接口,不要建立臃腫龐大的接口。&…

區塊鏈、密碼和銀行之間的衍生關系

銀行場景中密碼服務 設置密碼 用戶在注冊的時候,如果使用弱密碼,系統會檢測出來。我的猜測是將弱密碼的hash運算和用戶輸入的密碼hash比對,如果一致,禁止用戶注冊。 1、不要設置簡單密碼,您設置的密碼必須符合中信銀…

面經:多線程 線程池

使用線程池 當進程被初始化后,主線程就被創建了。對于絕大多數的應用程序來說,通常僅要求有一個主線程,但也可以在進程內創建多個順序執行流,這些順序執行流就是線程,每一個線程都是獨立的。 線程是進程的組成部分&am…

AIgorand區塊鏈中VRF隨機函數的應用

VRF(Verifiable Random Function) 可驗證隨機函數可以看作是一個隨機預言機,即可以通過任意的一個輸入,獲得一個隨機數輸出:輸出的結果(Output)是一個隨機數,其數值會均勻分布在值域…

AIgorand的相關學習參考鏈接

相關具體的開發者與SDK鏈接如下: GoSDKJavaScript SDK 網頁鏈接 測試網申請鏈接Github存儲庫鏈接開發者網址AIgorand官網Telegram電報群綜合白皮書MediumNaver Blog領英Linkedin區塊鏈瀏覽器INC公示錢包地址基金會公示錢包地址Telegram電報群官方 Github地址 相關…

操作系統 內核棧

視頻哈工大李治軍老師:https://www.bilibili.com/video/BV1d4411v7u7?p12 參考文檔:https://blog.csdn.net/SakuraA6/article/details/108810916 學長在我大一推薦我看,p12和p13的內容真的有那么難嗎,現在已經是我看的第三遍了還…

區塊鏈技術指南 序章理解感悟

序二 誤區一: 區塊鏈是一種顛覆性的新技術。區塊鏈不是一個新的技術,而是一個新的技術的組合。其關鍵的技術,包括P2P動態組網、基于密碼學的共享賬本、共識機制(拜占庭將軍問題,分布式場景下的一致性問題&#xff09…

面經:紅黑樹 B樹 B+樹 哈希表

1.對于插入,刪除,查找 以及 輸出有序序列 這幾個操作,紅黑樹也可以完成,時間復雜度 與 用跳表實現是相同的。 但是,對于按照區間查找數據這個操作(比如 [20,300]),紅黑樹的效率沒有跳表高&#…

回溯法和dfs的區別

值得注意,回溯法以深度優先搜索的方式搜索解空間,并且在搜索過程中用剪枝函數避免無效搜索。那為何 回溯算法 深度優先搜索 剪枝函數這一說法沒有錯? 因為樹是特殊的圖。簡單來說,樹是廣義的圖。再簡單來說,樹是圖。…

C++學習筆記 簡單部分

C 數據類型 使用變量來存儲各種信息,變量保留的是它所存儲的值的內存位置。這意味著,當創建一個變量時,就會在內存中保留一些空間。這段內存空間可以用于存儲各種數據類型(比如字符型、寬字符型、整型、浮點型、雙浮點型、布爾型…

Redis kqeue相關源碼

mask 或delmask :添加或者刪除的事件類型,AE_NONE表示沒有任何事件;AE_READABLE表示可讀事件;AE_WRITABLE表示可寫事件; 如aeCreateFileEvent(loop,e->fd,AE_READABLE,redisAeReadEvent,e); static int aeApiAddEv…

C++學習筆記章節中 面向對象詳解

C 類&對象 C類定義 本質上是一個數據類型的藍圖,定義了類的對象包含的信息,以及可以在這個類對象上執行哪些操作。類的定義是以class開頭,后面接類的名稱。類的主體是包含在一個花括號中,類的定義之后,必須跟著一…

Mac 破解軟件打不開沒有權限

Mac 破解軟件打不開沒有權限 sudo codesign -fs - /Applications/CleanMyMac\ X.app文件損壞 xxx sudo xattr -r -d /Applications/MarginNote\ 3.app sudo xattr -r -d com.apple.quarantine xxxx sudo codesign --force --deep --sign - /Applications/MarginNote\ 3\…

條件變量之虛假喚醒

當線程從等待已發出信號的條件變量中醒來,卻發現它等待的條件不滿足時,就會發生虛假喚醒。之所以稱為虛假,是因為該線程似乎無緣無故地被喚醒了。但是虛假喚醒不會無緣無故發生:它們通常是因為在發出條件變量信號和等待線程最終運…

拷貝構造函數和拷貝賦節省代碼最好用一個私有的函數

令 copy assignment操作符調用copy構造函數是不合理的,因為這就像試圖構造一個已經存在的對象。這件事如此荒涔,乃至于根本沒有相關語法。是有一些看似如你所愿的語法,但其實不是;也的確有些語法背后真正做了它,但它們…