1.一個以太網接口接收發送到它的單播地址和以太網廣播地址的幀。當一個完整的幀可用時,接口就產生一個硬中斷,而且內核調用接口層函數leintr
2.leintr檢測硬件,而且若是有一個幀到達,就調用leread把這個幀從接口轉移到一個mbuf(各層之間傳輸數據都用這個)鏈中,構造單獨的地址信息etherheaher。
etherinput檢查結構etherheaher來判斷接收到的數據的類型,根據以太網類型字段來跳轉。對於一個IP分組,schednetisr調度一個IP軟件中斷,並選擇IP輸入隊列,ipintrq。對於一個ARP分組,調度ARP軟件中斷,並選擇arpintrq。並將接收到的分組加入到隊列中等待處理。
3.當收到的數據報的協議字段指明這是一個TCP報文段時,ipintrq(經過協議轉換表中的prinput函數)會調用tcpinp t進行處理.從mbuf中取ip,tcp首部,尋找pcb,發送給插口層
3.1個關於pcb
每一個線程的task_struct都有打開文件描述符,若是是sock類型還會關聯到全局inpcb中.
listen某個fd時(new socket()=>bind(listen的端口)=>lisnten())在pcb中該fd是listen狀態
3.2in_pcblookup
搜索它的整個Internet PCB表,找到一個匹配。徹底匹配得到最高優先級,包含通配的最小通配數量的優先級高。因此當同一個端口已經創建鏈接後有了外部地址和端口,再有數據會選擇該插口。好比當140.252.1.11:1500來的數據直接就匹配到第三個的插口,其餘地址的發送到第一個插口。
4.插口層
4.1 listen 若是監聽插口收到了報文段
listen狀態下只接收SYN,即便攜帶了數據也等創建鏈接後才發送,建立新的so,在收到三次握手的最後一個報文段後,調用soisconnected喚醒插口層acceptreact
4.2 插口層accept
while (so->so_qlen == 0 && so->so_error == 0) {tsleep((caddr_t)&so->so_timeo, PSOCK | PCATCH,netcon, 0))}
當so_qlen不是0,調用falloc(p, &fp, &tmpfd)建立新的插口fd,從插口隊列中複製,返回,此時該fd也在線程的打開文件中了。調用soqremque將插口從接收隊列中刪除。
4.3 插口層read,send 從緩衝區複製mbuf。略
5.線程調用內核的accept,從調用開始會sleep直到此時能夠返回fd。
【後文若用了epoll在lsfd有事件時通知線程再調用accept會節省調用到sleep時間。】linux
DMA:硬件到內存數據拷貝脫離cpu
CPU與外設之間的數據傳送方式有程序傳送方式(cpu輪詢檢查,執行輸入指令(IN)或輸出指令(OUT))、中斷傳送方式(外設主動,請求前外設與CPU能夠並行工做,須要進行斷點和現場的保護和恢復,浪費了不少CPU的時間,適合少許數據的傳送)。CPU是經過系統總線與其餘部件鏈接並進行數據傳輸。
一般系統總線是由CPU管理的,在DMA方式時,由DMA控制器發一個信號給CPU。DMA控制器得到總線控制權,控制傳送的字節數,判斷DMA是否結束,以及發出DMA結束信號nginx
須要複製硬件數據到socket發送時,原方案4次用戶空間與內核空間的上下文切換,以及4次數據拷貝(涉及到TCP時,tcp/ip維護send buffer和recv buffer緩衝區——內核空間,須要cpu從用戶態複製到內核態,而後DMA經過網卡發送。)
如下叫領拷貝(不拷貝到用戶態)編程
2次用戶空間與內核空間的上下文切換,以及3次數據的拷貝,但用戶不能直接操做寫安全
user space不拷貝共享kernel space數據:4次用戶空間與內核空間的上下文切換,以及3次數據拷貝網絡
mmap是os到用戶內存無拷貝,用指針。首先,應用程序調用mmap(圖中1),陷入到內核中後調用do_mmap_pgoff(圖中2)。該函數從應用程序的地址空間中分配一段區域做爲映射的內存地址,並使用一個VMA(vm_area_struct)結構表明該區域,以後就返回到應用程序(圖中3)。當應用程序訪問mmap所返回的地址指針時(圖中4),因爲虛實映射還沒有創建,會觸發缺頁中斷(圖中5)。以後系統會調用缺頁中斷處理函數(圖中6),在缺頁中斷處理函數中,內核經過相應區域的VMA結構判斷出該區域屬於文件映射,因而調用具體文件系統的接口讀入相應的Page Cache項(圖中七、八、9),並填寫相應的虛實映射表。session
是否當即返回。數據結構
阻塞:空cpu,IO阻塞線程 非阻塞
是否由本線程執行多線程
同步IO 異步
accept後一個鏈接所有過程在一個進程/線程 ,結束後結束線程/進程,每次有新鏈接建立一個新的進程/線程去處理請求併發
線程池/進程池+非阻塞+IO多路複用 (非阻塞+IO多路複用 少了哪一個這種模型都沒有意義)
reactor 監聽全部類型事件,區分accept和業務處理
這個網上的圖是錯的。 accept後全部的讀寫處理都在一個線程中,無共享數據須要傳遞
基本上是accept確定要在一個線程中,由於只有一個fd。
1)單reactor單線程 accept+read/process/send
2)單reactor多線程 accept+read/send =》多process
3)多reactor多線程 accepct=>多read/process/send
4)另外一種 accepct[0號]=>子多read/send =》多process 當只有一個時退化爲單reactor多線程。線上就這個。
假設4個請求併發鏈接,2個線程
若p是瓶頸,好比p1佔用4個格子
1)單reactor多線程
r1->r2->r3->r4->s1->s2->s3->s4 p1w|w|w|w|->p3w|w|w|w| p2w|w|w|w|->p4w|w|w|w|
2)多reactor多線程
r1->p1w|w|w|w|->r3->p3w|w|w|w|->s1->s3 r2->p2w|w|w|w|->r4->p4w|w|w|w|->s2->s4
此時單reactor多線程更快。多reactor多線程編寫簡單
若r是瓶頸(好比佔3個,4個換行了=。=)
1)單reactor多線程
r1w|w|w|->r2w|w|w|->r3w|w|w|->r4w|w|w|->s1w|w|w|->s2w|w|w|->s3w|w|w|->s4w|w|w| p1-> p3 p2-> p4
2)多reactor多線程
r1w|w|w|->p1->r3w|w|w|->p3->s1w|w|w|->s3w|w|w| r2w|w|w|->p2->r4w|w|w|->p4->s2w|w|w|->s4w|w|w|
此時多reactor多線程快
最後一種模式分別
r1->r3 ->s1 ->s3 p1w|w|w|w|->p3w|w|w|w| r2->r4 ->s2 ->s4 p2w|w|w|w|->p4w|w|w|w| r1w|w|w|->r3w|w|w|->s1w|w|w|->s3w|w|w| p1-> p3 r2w|w|w|->r4w|w|w|->s2w|w|w|->s4w|w|w| p2-> p4
與reactor區別是reactor是同步讀寫,preactor是異步讀寫
早在1973年,actor模式被提出(跟圖靈機一個級別的模式,只是個模式),主要是對立於數據共享加鎖的。一個是分佈式計算中沒辦法很好的共享數據,另外一個是共享數據加鎖會阻塞
線程(單機的話)/請求超時(分佈式),還有個不重要的有時候鎖不住(好比cpu的多級cache,沒個線程裏的cache不一樣步),所以採用一種非阻塞和通訊的方式,數據發送到actor排隊
後即返回不加鎖,經過通訊傳遞改變,actor是整個數據+行爲(對象)的組合
,不只僅負責數據的鎖,actor之間要作到無共享,發給actor的本身複製一份新的,actor能夠繼續調actor,因此要都無共享
。(這麼說非阻塞IO,select等都是借鑑的這個。)
actor行爲
1.Actor將消息加入到消息隊列的尾部。
2.假如一個Actor並未被調度執行,則將其標記爲可執行。
3.一個(對外部不可見)調度器對Actor的執行進行調度。
4.Actor從消息隊列頭部選擇一個消息進行處理。
5.Actor在處理過程當中修改自身的狀態,併發送消息給其餘的Actor。
爲了實現這些行爲,Actor必須有如下特性:
● 郵箱(做爲一個消息隊列)
● 行爲(做爲Actor的內部狀態,處理消息邏輯)
● 消息(請求Actor的數據,可當作方法調用時的參數數據)
● 執行環境(好比線程池,調度器,消息分發機制等)
● 位置信息(用於後續可能會發生的行爲)
另一種:CSP,不要經過共享內存來通訊,而應該經過通訊來共享內存的思想
,Actor 和 CSP 就是兩種基於這種思想的併發編程模型.Actor 模型的重點在於參與交流的實體,而 CSP 模型的重點在於用於交流的通道.channel。channel共享帶鎖,再也不擴展了。actor的設計也要抽象好,好比好比左右兩個叉子,若是加鎖。鎖(左右)/ 爲了併發,單獨左右。actor要左右組合在一塊兒,每次判斷左叉子和右叉子都有返回成功。zmq 每一個線程綁定一個cpu,線程之間不會共享session,不須要加鎖。每一個鏈接的操做都在一個worker中.通訊傳遞數據。