第二十二章 TCP/IP層的實現

                      第二十二章    TCP/IP層的實現

 

     我比較喜歡先難後易,如果把GPU顯示管理、和網絡管理拿下後;我會從頭整理、改寫一遍APO操作系統。這樣,就會形成APO操作系統的錐形、也獲得了全局觀。內核CPU線路、和用戶CPU線路,你可以將它們看成是獨立的2個32位CPU核;內核CPU主要任務是實時處理、硬件中斷,256個實時線程包含了一些中斷程序的後半部。用戶CPU主要是動態優先級進程、線程調度,各種應用程序的運行;2個核之間是通過消息交互、句柄提交、事件驅動。

     現有的實現網絡驅動的程序都是非常複雜、囉嗦、可怕的,數據包的內存管理就可以嚇到人。再加上學院派的那些玩不死你,不擺手的TCP協議;你有那麼多精力嗎?LINUX實現網絡的程序,源代碼據說38萬行啊;難道要發揚愚公移山精神,需要幾代人的煉化?對我來說,1000行代碼量,已經感覺是一件很恐懼的事情啊;38萬行,那會直接嚇暈初哥。APO的TCP/IP層是在內核CPU線路實現的,MAC端口硬件也集成在內核CPU裏。實現TCP/IP層的只是簡單的編寫3個實時線程:RECV(),SEND(),TSINT();及事件驅動層Message()的相關消息處理,代碼量不到160行。我就想不通,那些狗屁磚家、叫獸之流的聰明仔想法?難道非要人爲將問題複雜化纔好?要知道,通常越複雜的事情、都是簡單的實現!在基礎科學領域裏,那些傢伙還要亂搞,狂妄自大;難道這就能體現你們的是聰明仔?作爲人類的一員,我只能感到悲哀!無可奈何!值得慶幸的是自己是屬於笨鳥一族。

 

      光明終會過去,落日黃昏之後,迎來的是黑暗;在黑暗的盡頭,將是黎明、蘊含光明的希望;那永恆的黑白交替運動就是宇宙規則之一!半個多月啊,看完約3000章的小說,昏天黑地;由衷的佩服那些作者們,要知道我寫一章都是那麼艱難!那麼無奈!這章將是又臭又長、沒完沒了的修改。


一、內核32位CPU

     大部分的硬件中斷都是在內核32位CPU中體現,而本文僅是討論集成在內核32位CPU中的輔助硬件MAC端口的MAC發送、接收軟中斷。相對於用戶CPU來說,內核CPU的程序要簡單得多。主程序就4行,代碼量集中在中斷程序的前本部,和中斷程序後本部的實時線程。對於內核CPU的最多256個實時線程, APO 採用了先來先服務調度模式SCHED_FIFO( First-In, First-Out,FIFO )。

SCHED_FIFO:不同的實時線程根據靜態優先級進行排隊,誰先準備好運行就先調度誰,並且正在運行的實時線程不會被終止直到以下情況發生:

(1)發生硬件中斷、用戶的中斷實時程序搶佔CPU。

(2)自己因爲資源請求而阻塞(調用sleep())。

(3)自己主動放棄CPU(調用yield())。

     當 policy 的值爲 SCHED_FIFO 時,遵守 POSIX1.b 標準的 FIFO 調度規則。它會一直運行,直到有一個線程因 I/O 阻塞,或者主動釋放 CPU ,或者是 CPU 被另一個具有更高 rt_priority 的實時中斷程序搶先。

1、內核CPU主程序

Main{      // 主程序,佔內存空間3W。

  init(); // 內核CPU初始化。

LOOP:

  Message(); // 消息處理,事件驅動層。

  RT_Thread_DD();// 實時線程調度方法。

  JMP  LOOP; // 無限循環。

}

    Message()消息處理、事件驅動層,起到承上啓下的作用;是用戶CPU和內核CPU通過異步消息通信的橋樑;後面還會詳細討論有關網絡消息的處理。

     內核CPU沒有集成多功能硬件模塊,但集成有簡化的位圖檢測功能。尋找256位圖的最高位爲1的索引時,需將位圖賦值到H4專用行寄存器;之後,用MSS1指令。如果256位均爲0,狀態寄存器MSR的S1位會置1,索引值在任務狀態寄存器MRR.7-0。

實時線程調度方法:RT_Thread_DD()       

佔用:9W,

耗時:跳到相應的實時線程:8ns,無實時線程:7ns。

RT_Thread_DD(){ // 實時線程調度方法。

   R1 = #RTTETAB; // R1指向相應的實時線程入口表RTTETAB,

   H4.E = H5.E;   // H5行寄存器爲256位的實時線程運行位圖變量,

   MSS1;          // 實時線程位圖索引

   H5.E = H4.E;  // 回傳

   BT0  MSR.S1, SJP; // 有實時線程、跳。

   RET

SJP:

   R2L = MRR.7-0; MPC = R1.R2L.W; // 跳到相應的實時線程入口。

}

256個的實時線程入口表RTTETAB:H32—H63

256個的中斷入口表:H64—H95

2、MAC硬件端口

    MAC硬件端口集成有發送、接收雙緩衝區(CPU內部RAM):2*2*48E。發送、或接收的雙緩衝區是通過乒乓模式來工作;每當發送、或接收一個數據包時,都產生一個軟中斷、只是對實時線程運行位圖的相應位置1,發送:位圖序號251、接收:位圖序號250。程序使用的MAC接收緩衝區地址:H160—H207,程序使用的MAC發送緩衝區地址:H208—H255。MAC硬件端口接收到一個IP數據包時,軟中斷置位了實時接收線程RECV();數據內容就在行寄存器H160—H207中的一些行;IP數據包大小範圍是:2E—48E(64B—1.5KB)。如果進入了實時接收線程RECV(),會據H160的參數進入各種分支程序(TCP數據包?ICMPAPO信令包?);如是信令包,那需據信令內容進行分支;如是TCP數據包,需要做IP數據包組裝,拷貝到相應的用戶流容器中。類同有,實時發送線程SEND();這只是簡單的拷貝本地內存發送數據塊緩衝區中的一個發送數據包到H208—H255中的一些行吧,最大耗時不到45ns。

   CRC、校驗和的產生、檢錯這都是硬件實現的,如果有錯、硬件自動丟棄IP數據包;還有自環、目標MAC地址判斷、軟中斷產生等,也是MAC硬件端口的事情;這裏就不多說了。

 二、實時發送線程SEND()

     APO的發送緩衝區有8個數據塊:32—39號 本地內存數據塊,發送緩衝隊列IP數據包數最大64K包。TCP數據報分割成一些IP數據包是由用戶進程提交的數據報文發送消息時,是Message()消息處理、事件驅動層的相應消息處理過程來實現的;類似的,opens()的消息也是一樣。這意味着,實時發送線程SEND()並不負責IP數據包安裝到發送緩衝隊列;只管對發送緩衝隊列的IP數據包數進行發送。對於連接建立時的信令包、或IP數據包的超時重發機制是由0.5秒定時器軟中斷實時線程TSINT()來實現。所以,TCP/IP層的發送TCP數據報文的實現有三部分:TSINT()、Message()、SEND()。

 

對於SEND(),我們需要下面的參數變量:
BU512KE  SENDBUF; // 發送隊列緩衝區32—39號本地內存數據塊。

SENDPV{

  BU16     

  BU16  PACKETSN;// 發送隊列的數據包數。

  BU32  IDLELN; // 發送隊列緩衝區的空閒行數。

  BU32  IDLEDPP; // 發送隊列緩衝區的首空閒數據包指針。

  BU32 SENDPP; // 發送隊列緩衝區的發送數據包指針。

}

     發送隊列循環緩衝區中,最後面的或許有些行沒用;如果IP數據包安裝到發送隊列時,IP數據包的長度 + 首空閒數據包指針 的值X大於緩衝區尾行,那麼先把首空閒數據包指針的內容行設爲0,再首空閒數據包指針設爲SENDBUF,之後才安裝IP數據包。如果值X大於SENDPP發送隊列緩衝區的發送數據包指針,那麼、發送隊列滿,該事件過程延緩處理;等待SEND()一些數據包後再進行。

實時發送線程SEND()。    

出口:

發送一個IP數據包,PACKETSN發送隊列的數據包數減一,IDLELN發送隊列緩衝區的空閒行數增加爲已經發送的數據包行長度,SENDPP發送隊列緩衝區的發送數據包指針爲指向下一個發送數據包。

佔用:22W。

耗時:最大 45ns。

SEND(){ // 實時發送線程。

   R1 = #SENDPV; // R1指向SENDPV。

   if R1.PACKETSN.Z- != 0 goto sen1;// 發送隊列的數據包數減一非零跳。

   MSR.SENDD = 1;// 此後無數據包發送,MAC硬件端口不再置發送軟中斷。

sen1:

   R2 = R1.SENDPP.W; // 提取發送數據包指針給R2。

   BT1 R2.E, sen2;  // 行內容非0、跳。

   R2 = #SENDBUF;   // 重設發送數據包指針爲緩衝區起始。

sen2:

   R3 = R2.TYPE.Z.10-5; // 提取數據包的TYPE參數的行長度值。

   R4 = R2 + R3;  // R4爲下一個發送數據包指針。

   if  R4<= SENDBUFMAX  goto  sen3; // 沒有越界、跳。

   R4 = #SENDBUF; // 重設下一發送數據包指針爲緩衝區起始。

sen3:

   R1.SENDPP.W = R4; // 設置下一個發送數據包指針。

   R4 = #H208; // R4指向 MAC發送緩衝區地址。

   COPY.E( R4, R2, R3L ); // 拷貝當前需發送的IP數據包到MAC。

   RET

}


三、實時接收線程RECV()

     我們並不需要指定的接收緩衝區,描述socket連接的內存v節點中,就有關於該連接的接收流容器參數。如果接收到一個與TCP數據報文內容相關的IP分段數據包,只是簡單的拷貝到接收流容器中吧。實時接收線程RECV()是要複雜一些,它除了處理TCP數據包,還有各種信令分支、及連接狀態遷移。不管怎樣,RECV()都需要在不到0.3us內完成。對於IP分組數據包的組裝,我們是需要一種更爲簡潔的方法。

     假設客戶端A和服務端B建立了一個連接,如果A請求讀B的一個100GB的文件;我們設想一下合理的通信過程。B將A的請求消息提交給B端的用戶服務進程,那麼用戶服務進程處理該消息、首先要將文件的內容從磁盤讀入到文件流容器,接着、將文件流容器提交給TCP/IP層,通信的事情就不用管了;直到TCP/IP層回給完成消息,纔讀入新的文件內容到流容器,重複該過程直到100GB的文件內容全部傳輸完成。爲了加快速度,我們也可以建立2個文件流容器、以乒乓方式進行,在提交一個文件流容器到TCP/IP層時,使用另一個文件流容器來讀入新的文件內容,用並行方式來提高速度。那麼,文件流容器的大小是多少合適呢?這取決於服務端的用戶服務進程;在建立連接時,除了要協商MSS、還需確定文件流容器的大小是多少個IP數據包。APO規定文件流容器的大小單位是256個IP數據包,最大32單位、即8K個IP數據包;文件流容器的大小行數最大64KE(2MB)。這就意味着用戶進程提交的一個TCP數據報文最大是2MB,100GB的文件就需提交50K次。如果MSS = 32E,文件流容器是64KE,用戶進程提交的一個TCP數據報文是2MB;那麼、TCP數據報文被分割成2K個IP數據包。所以,一個TCP通信的分段IP數據包的包頭;需要指明其是第幾號數據包,第幾號TCP數據報文,以及每一個TCP數據報文的IP數據包數。我們使用流標籤來標識TCP數據報文流,一個TCP數據報文流最大爲64K*2MB = 128GB。對於用戶進程來說,每次通信就是一個TCP數據報文。如果TCP數據報文的分割IP數據包數是n,那麼該報文的總字節數 = ( n – 1 )*MSS*32B + 最後的第n包的有效內容字節數。

1、通信協議TCP/IP/ICMP新規

      爲了進一步的簡化網絡編程,APO的通信協議TCP/IP/ICMP作了改進。數據包格式:MAC頭、TCP頭、ICMPAPO頭與內容或IPAICMP頭與內容或TCP數據分段報文。從另一個角度看,可分爲TCP數據分段報文、和信令報文;而信令報文又分爲路由交換機必須接收處理的ICMPAPO協議(宣告、鄰居請求、探查)信令報文,和只是轉發的IPAICMP協議信令報文。而主機對2種信令報文都需處理;當客戶端請求建立一個TCP連接時,使用ICMPAPO協議的探查信令報文,該報文到達服務端時、就包含了沿途的路由信息;服務端在ACK和MSS、servesockfd時,轉發該信令報文;這樣,客戶端就知道了往返時間、路徑信息、MSS、servesockfd等。

TYPE.15 = 0 是IP協議:

0、互聯網控制報文協議ICMPAPO,

1、TCPAPO,  IPA爲APO的IP協議,

2、IPAICMP,ICMP的差錯報文、應答報文協議,

3、UDPAPO,

4、IPV4,

5、IPV6,

6-15 其它、或用戶自定義。

IP{ // IP數據包,最小長度2E = 64B,最大長度 = 1526B

// 含頭部最大47E = 1504B,分段數據報文內容最大45E = 1440B。

 BU1E MACIP{ // 以太網和IP頭部、1E(1行) = 32B;路由器處理入口。

   BU48  MDA;   // 以太網目標地址

   BU48  MSA;   // 以太網源地址

   BU16  TYPE;  // 類型和長度,TYPE.10-0  LEN; 數據包字節長度。

   BU8   TTL;   // 跳數限制。

   BU8   TOS;   // 8位傳輸優先級、流量類型。

   BU64  SLADD; // 源鏈路地址。SLADD + MSA = 源IP地址

   BU64  DLADD; // 目標鏈路地址。DLADD + MDA = 目標IP地址

}

 TCP{ // APO的TCP協議頭、1E;支持4GB的文件流傳輸。

   BU16  dflown; // TCP數據報文流的總報文數。

   BU16  doff;   // TCP數據報文的偏移號。

   BU32  sheetoff;// 低13位0-12是IP分段數據包的偏移號。

// sheetoff.31  astfp; 1、同時多流模式,0、時分多流模式。

// sheetoff.30-26  request; 上層協議的請求方法標識。Read、write、GET、

// POST、PUT、DELETE、TRACE、CONNECT、PATCH、ABOR、ACCT等等;或自定義。

// sheetoff.25-13  dpackn; 數據報文的數據包數,25-21位是流容器單位數。

   BU8   trflag; // 傳輸控制標誌。

// trflag.7  MF; 更多分段標誌(more fragment),0、最後一個分段

// trflag.6  DF; 分段標誌:1、不分段,0、可以分段

// trflag.5  MULTICAST; 組播標誌,1、有效。

// trflag.4  SIGLL; 信令標誌:1、該報文段含有信令,0、DATA報文段傳輸。

// trflag.3  SIGC; 1、請求丟失的數據包信息。

// trflag.2-0 RES; 備用。

   BU24  flowlabel; // 24位流標籤,標識不同的TCP數據報文流。

   BU8  conflag;   // 連接控制標誌。

// conflag.7 CWR; 用來表明它接收到了設置ECE標誌的TCP包。

// conflag.6  ECE; 網絡擁擠標誌。

// conflag.5 URG; 1、緊急指針字段有效。

// conflag.4  ACK; 1、確認應答。

// conflag.3  PSR; 1、推送,不等組裝、直接提交報文段到應用層。

// conflag.2  RST; 1、復位連接。

// conflag.1  SYN; 1、請求建立連接。

// conflag.0  FIN; 1、釋放連接。

   BU24  servesockfd; // 24位服務端的文件號。  

   BU16  SPORT;   // 源端口

   BU16  DPORT;   // 目的端口

   BU16  cheksum; // 校驗和。

   BU16  urgentp; // 緊急指針。

   BU16  tranmode;// 高層協議的傳輸模式。

   BU16  staterp; // 高層協議的狀態響應碼,或錯誤碼。

   BU32  usedata; // 用戶自定義、或高層協議使用、或組播域定義。

}

 ICMP{ // APO的ICMP協議頭、1E,無連接;或是TCP分段數據報文內容。

   BU8   imcptype; // 報文類型低4位:宣告、鄰居請求、探查,或應答、

// 或差錯報文。高4位是代碼號,路由交換機只管低4位。

   BU8   segnum; // 路由交換機附帶數據項印記指針(項數、每項16字節)。

   BU16  devtype;// 源設備類型(主機,2-3-4層交換機等)、網絡中級數。 

   BU32  PTID;   // 源設備的進程號(或端口號)、線程號(或標識)。

   BU32  times;  // 報文發送時的時間戳us。

   BU16  MTU;    // 源設備的MTU。

   BU16  cheksum;// 校驗和。

   BU16B icmpdata; // 第0項的附帶數據,後面接着是更多的附帶數據。

}
  // IP數據包的其它內容。

}

     對於一些應用來說,或許在同一個連接中,同時有多個流在交織傳輸;這時,TCP/IP層是不會組裝IP數據包的,而是由應用程序來實現。當sheetoff.31 = astfp標誌爲1時,同時多流模式;TCP/IP層只是將收到的TCP報文的IP數據包,直接提交給用戶進程;丟包重發,包組裝、拆分等都是由用戶進程來操控的。APO支持同時多流傳輸模式,也支持通常的時分多流模式。在時分多流模式中,IP數據包的組裝、拆分、重複包、丟包重發等是由內核CPU的TCP/IP層來實現的。當然,也可以使用多個連接來實現同時流,而每個連接都是分時流;應用程序就無須理會TCP通信細節了。

2、信令報文

有3類信令報文:

1)、主機、路由器的宣告、鄰居請求、探查報文ICMPAPO。

2)、路由器、或主機到主機的應答、差錯報文IPAICMP。

3)、主機之間用於TCP通信的TCP信令報文。


     APO取消了廣播信令,路由器的開機宣告信令報文必須是對其每一個端口都做發送;對於主機的開機宣告信令報文,只是發送一次。路由器的鄰居請求信令報文只是對其每一個端口做一次發送,收到應答則更新路由表。對於主機的鄰居請求信令報文,其相應的路由交換機,如果不是子網主管交換機則向上轉發,同時也發送其管轄下的鄰居信息MAC表;子網主管交換機最終是轉發該鄰居請求信令報文到其每一個下級端口,相應收到來自上級的鄰居請求信令報文的路由交換機;如是有連接主機的端口,則發送其管轄下的鄰居信息MAC表,否則,往下級轉發該信令報文。對於探查報文,如果是非目的節點,則應該附加上其節點信息後再轉發。ICMP本身是無重發機制的,需要其它輔助才行,信令報文丟了也就那樣了。

    主機之間用於TCP通信的TCP信令報文,是有2次重發機制支持的;TCP信令協議鑲嵌在IP數據包的TCP頭裏,但建立TCP連接時,也需要使用ICMPAPO協議的探查報文。TCP請求建立連接SYN,和對連接的確認應答ACK,這一對信令報文是在ICMPAPO協議的分支中處理的。

     TCP的數據分段報文的丟包重傳機制是通過在連接已經建立的ESTABLISHED狀態下,SIGC爲1、傳送丟失的數據包信息來實現的。通信的A、B雙方,在A方發送了n個IP數據包後,跟着發送請求丟失的數據包信息;B方收到後回送丟失的數據包信息,A方收到後則據知再次發送,繼續這一個過程、直到該TCP數據報文完成。任何時候,信令報文的3次發送失敗、都會使連接被清除。TCP通信是較爲複雜的,我們需要分解爲一系列的簡單動作,分解爲多個層次。


3、實時接收線程RECV()  

    接收一個IP數據包,據接收的IP數據包類型TYPE.15-11數值跳轉;APO當前只支持3種類型IP數據包:TCP的IP數據包(包含TCP信令包),路由信令IP數據包ICMPAPO,節點信令IP數據包IPAICMP(應答、差錯的IP數據包)。其它保留作爲兼容或將來擴展。


RECV(){ // 實時接收線程;佔用:18W,耗時:3ns+。

  R0L = H160.TYPE.15-11; // 提取接收IP數據包類型。

  Switch(R0L){ // 據R0H的值跳轉;PPCL = (PPCL + 1).R0H。

  ICMPAPO; TCPAPO; IPAICMP; RET(保留); RET; …. 共13個RET。

     }

}

4、TCP的IP數據包接收方法TCPAPO()

    TCP的IP數據包格式:MACIP頭、TCP頭、TCP內容。如果是TCP分段報文,提取TCP數據報文的偏移號,拷貝到用戶進程的報文接收流容器的相應位置,如TCP數據報文的所有IP分段數據包都接收完畢、那就向用戶進程發TCP數據報文提交消息;如果不分段,那麼、拷貝到用戶進程的報文接收流容器,並向用戶進程發TCP數據報文提交消息。如果是含有TCP信令的IP數據包,還需做信令分支處理。

    每一個連接都有64E的信令包接收緩衝區,建立連接後,後32E就作爲接收分段報文的位圖緩衝。如果TCP報文是需要分段的,那麼、提取第0號分段報文的sheetoff.25-13  dpackn; TCP報文的數據包數,25-21位是流容器單位數(位圖行數);初始化接收分段報文的位圖緩衝。以後,每收到一個分段數據包,位圖的相應位清0。位圖緩衝反映了TCP報文的分段數據包接收的狀況;如果A方發送完TCP報文的所有分段數據包後,跟着就是請求傳送丟失的數據包信息、即接收位圖;B方收到請求後,回送位圖緩衝,A方收到後則據知再次發送。。。。

TCPAPO(){// TCP的IP數據包接收方法;佔用:45W、R30-R31,

// 最大耗時約:20ns+?。

   Getlinkd(); // R1接收分段報文的位圖緩衝指針,數據包行長度R0H,

// R2 = H160,R3指向數據報文接收流容器,R4爲數據包數+1、MSS。

// R30爲端口表項字指針,R31指向連接的內存v節點。

   BT1 H161.SIGLL, TAP4; // 是含有TCP信令的IP數據包,跳。

   BT1 H161.astfp, TAP2; // 同時多流模式,跳完整提交。

   BT1 H161.DF, TAP2;    // IP數據包不分段,跳完整提交。

   R0H = -2;   // 去頭部,只拷貝TCP分段數據報文的內容。

   R2 = H162;

   R0L = H161.sheetoff.12-0;// TCP數據報文的偏移號(數據包號)到R0L。

   R3 = +R0L*R4L;            // 指向接收流容器的相應位置。

   (R1).R0L = 0;     // 接收分段報文的位圖緩衝的相應位清0。

   if R0L != 0  goto  TAP2; // 非第0號分段報文跳。

   R0L = H161.sheetoff.25-21;// 提取流容器單位數(位圖行數)

   R0L+;                     // 位圖行數取整。

TAP1:

   (R1).R0L.E = 1; // 位圖緩衝初始化爲全1

   if R0L- != 0 goto  TAP1; // R0L-1後非零繼續

   (R1).0 = 0;     // 接收第0號分段報文的相應位清0。

TAP2:

   COPYH( R3, R2, R0H );// 拷貝數據報文到接收流容器的相應位置。

   BT1 H161.PSR, TAP3; // 如果推送,不等組裝、直接提交,跳。

   if H161.dpackn > R4H  goto  RET; // TCP數據報文沒接收完跳返回。

   (R30).W.rpstate = 1; // 置完全接收TCP報文的所有分段狀態。

TAP3: 

   SUBDP();// 向用戶相關進程提交TCP數據報文完成的消息方法、併發ACK。

   RET

TAP4:

   BT1 H161.ACK, ACKHANDLE;// 不同狀態下的ACK信令處理。

   BT1 H161.SIGC, TAP6;  // 請求丟失的數據包信息的信令,跳。

   R3 = R31.Srecvstream_p; // R3指向接收的信令數據報流容器。

   COPYH( R3, R2, R0H ); // 拷貝信令報文。

   BT1 H161.RST, TAP3; // 復位連接跳、提交消息、發ACK。

TAP5:

   (R30).W.FIN = 0;      // 其它情形、釋放連接。

   JMP  TAP3;

TAP6:  // 丟失數據包信息的請求信令,發送位圖緩衝及ACK,不提交。

   SENDBMP();

   RET

ACKHANDLE://確認ACK響應幀處理,SYN請求建立連接及響應在ICMPAPO分支。

// 帶ACK的信令包,意味着之前發送的信令,現收到確認應答ACK,有多項分支。

// 先禁用和復位發送超時定時器OVERTIME。

// SIGC收到丟失數據包信息位圖,重發丟失的數據包並最後附加SIGC信令包。

// RST復位連接的ACK,沒用返回RET。是應用進程初始化連接,發RST。

// FIN釋放連接的ACK,這時、主動關閉方纔釋放本連接。

   R31.FLAG.TIME = 1; // 禁用發送超時定時器OVERTIME。

   R31.OVERTIME.Z = 0;// 復位發送超時定時器OVERTIME。

   BT1  H161.RST,RET;

   BT1 H161.FIN, TAP5; // 就算是3次握手釋放連接吧。

   BT0 H161.SIGC, RET; // 其它情形返回。

   RPACK(); // 據丟失數據包信息位圖,重發丟失的數據包及附加SIGC信令包。

}


5、端口表

 

     16位socket端口表, 理論上有64K項、每項的內容是一個字。端口表項內容,如果是客戶端口:高8位是狀態標誌、低24位是文件號sockfd;對於服務端口,高8位是狀態標誌、低24位是0。因爲一個服務端口就對應很多連接,我們將服務端口的描述每一個客戶端連接的24位內存v節點號(socket文件號)放在IP數據包的TCP協議頭中。

sock_port_tab{ // socket端口表。

  BU32 [64K]  tabitem;// 端口表項,高8位狀態標誌、低24位sockfd或0。

// tabitem.31  linkworks; 1、連接有效;釋放連接CLOSED時,該位清0。

// tabitem.30  servelink; 1、是服務端連接LISTEN狀態,0、是客戶端。

// tabitem.29  SYN-SENT;  如是客戶端,1、在SYN-SENT狀態(sendSYN)。

// tabitem.29  SYN-RECEIVED; 如是服務端,1、在SYN-RECEIVED狀態。收到

// SYN,send SYN + ACK進入該狀態。

// tabitem.28  ESTABLISHED; 1、ESTABLISHED連接狀態。如果是服務端,

// 在SYN-RECEIVED狀態、收到ACK則進入該狀態。如果是客戶端,在SYN-SENT

// 狀態、收到SYN + ACK就發ACK進入該狀態。

// tabitem.27  FIN_WAIT;1、FIN_WAIT主動關閉等待狀態。Send FIN進入。

// 收到ACK後,linkworks= 0、釋放連接CLOSED。

// tabitem.26  TIME-WAIT;1、TIME-WAIT被動關閉等待狀態。收到 FIN進入,

// 併發送ACK。

// tabitem.25  rpstate; 1、完全接收TCP報文的所有分段狀態。

// tabitem.24  OVERTIME;1、超時定時器啓動。



     主動關閉(active close)端應用程序調用close,於是其TCP層發出FIN請求主動關閉連接,之後進入FIN_WAIT狀態;等待遠程TCP的ACK確認,收到ACK則釋放連接。如果1s內、並重發2次FIN請求,還是沒有收到ACK;也釋放連接。被動關閉(passive close)端TCP也是類似的;當被動關閉端TCP接到FIN後,就發出ACK以迴應FIN請求(它的接收也作爲文件結束符傳遞給上層應用程序),並進入TIME-WAIT:等待足夠的時間(1s內、如果再次收到FIN,會重發ACK;最多2次),以確保遠程TCP接收到連接斷開請求的確認ACK。不管怎樣,在1s後,都會自動釋放被動關閉端的連接。這些都是在0.5秒的定時器軟中斷實時線程TSINT()來實現;信令數據包都有最多3次的發送,3次失敗、連接就給清除。


Getlinkd()方法

出口:

R1接收分段報文的位圖緩衝指針,數據包行長度R0H,R2 = H160,R3指向數據報

文接收流容器,R4爲包數+1、MSS;R30爲端口表項字指針,R31指向連接v節點。

佔用:20W、R30-R31,

最大耗時:17ns+。

Getlinkd(){

   R0L= H161.DPORT.Z;   // 提取目標端口到R0L。

   R1 = #sock_port_tab;

   R30 = R1.R0L.W.; // R30爲端口表項字指針,後面是.結尾、賦值地址。

   BT1 (R30).W.linkworks, GLD1; // 連接有效、跳。

GLD0:

   MSP = +1; RET   // 連接無效,POP後返回、丟棄包結束。

GLD1:

   R1 = (R30).W.23-0; // R1爲v節點號。

   BT0 (R30).W.servelink, GLD2; // 是客戶端、跳。

   R1 = H161.servesockfd; // 從IP數據包提取24位服務端的v節點號。

GLD2:

   R31 = getvnode( ,R1 );// R31指向連接v節點內存。

   R1 = R31.MDA.W;       // 該連接的對方主機MAC地址的32位。

   if  R1 !=H160.MSA.W  goto  GLD0;// 再次覈對是否該連接,否丟包返回。

   R4H = R31.recepn+.Z;  // 接收數據包數加1並送R4H。

   R4L= R31.MSS.Z;

   R3 = R31.recvstream_p.W; // 接收的數據報流容器本地內存指針。

   R2 = #H160;

   R1 = R31.RECVSMC.W; R1 = +32; // R1接收分段報文的位圖緩衝指針。

   RET

}

 

     沒法,每天只能是龜爬式前進;老天已經收回激情,只剩下咬牙切齒的堅持。一天只能有約一小時用於這章的思考、碼字,大部分時間都是通過玩遊戲、看小說來積聚動力、念力。我們每一個人身體的每一個組成部分、或說每一個細胞中的基本粒子,都是源自宇宙的初生;它們只爲了緣分的相聚,從遙遠的古代走過來;生命是永恆的,消散的只是生命的印記,亦即靈魂。靈魂複製的夢想一直是我最大的動力,想起都激動萬分;學院派們、醒醒吧,爲加速人類的進步盡一點力量吧。


6、APO的重傳機制


     APO主機可以達到每秒百萬個IP數據包的速度,TCP報文可以分段爲最大8K個IP數據包;爲了那傳說中的網絡速度,就讓我們一次發射幾K個IP數據包吧!那管它網絡瞬間洪水滔天?丟包率達到50%?我們只追求高速;不行?修改網絡設備吧。如果一個文件流比較大,我們是想儘可能的做大TCP報文;但考慮到內存空間的限制等等因素,流容器的大小最大還是設在一個數據塊(2MB);這樣,最多分段爲2K包。我們是希望一次就將TCP報文的所有分段IP數據包發射出去,如果丟包率高於50%,那隻好改小一些TCP報文大小了。一次發射2K個IP數據包,對於1GBPS的主機MAC端口速度,也就約花2ms的時間;即使丟包率爲50%,也傳輸了1K包,這時、做一次丟包信息的信令通信;第二次選擇重傳時,或許成功率就到80%、只剩200包需要重傳,或許3次重傳就能完全成功;當然,好網絡一次就能OK。現有的TCP協議,如果每次IP數據包傳輸都來一次ACK,就算5次來一個ACK確認;也是非常愚蠢和低效率的。


    APO的TCP分段IP數據包重傳機制,是通過丟包信息的TCP信令來實現的;對於分段IP數據包的組裝、重複包的處理等是很自然的實現。通信雙方都能透過數據包接收位圖,瞭解通信狀況。A方一次發射一個TCP報文的n個分段IP數據包,會在最後附加一個請求丟包信息的TCP信令包;B方收到第0號分段IP數據包,就建立和初始化數據包接收位圖等;對於是否收到最後一個分段不重要,關鍵是能收到請求丟包信息的TCP信令包,如果超時(根據包數設定)、連接就會被釋放;如收到TCP信令包則發送數據包接收位圖的TCP信令包給A方。雙方檢查到TCP報文完全傳輸,則分別回消息給用戶進程,跟着就是下一個TCP報文了。根據IP分段數據包的偏移號、即包號,和建立連接時協商的MSS值,很容易就能定位IP分段數據包內容在接收流容器中的位置,並存放。不過,對於不分段的TCP報文,或完全接收TCP報文的所有分段IP數據包,還是要回發ACK的;這時,就不需要丟包信息的TCP信令包了。


7、APO連接描述的內存v節點

     如何區別描述連接的socket文件和其它文件類型的內存v節點呢?我們修改非描述連接的文件類型的內存v節點如下:

BU4E  file_vnode{ // 文件對應的本地內存v節點。

  BU32 vnode;    // 索引i節點號、文件名字哈希值。

  BU16 v_mode;   // 描述文件的訪問權限;文件的讀、寫、執行權限

//i_mode.15 socket; 1、socket文件,0、其它類型文件。

// i_mode.14-12  ftype; 文件類型: 0-符號連接文件,

// 1-普通文件, 2-塊設備文件,3-字符設備文件,

// 4-文件系統根目錄型文件。

// i_mode.11-0 FWQX;文件訪問權限rwx-rwx-rwx-rwx、owner–root–grp-oth

  BU16 v_flags;  // 文件標誌。

……

}

    這樣,判斷v_mode變量就可以知道是否socket文件了。 原來的i_mode.12標誌改爲v_flags.12   ACL;  文件訪問權限是否由ACL文件描述。


BU4E socket_vnode{ // socket文件的網絡v節點。

  BU2E{ // 2行的連接描述。

   BU32  PTID;   // 高16位關聯的進程號、低16位關聯的線程號。

   BU8   FLAG;    // 狀態標誌,FLAG.7 = 1爲socket文件v節點。

// FLAG.6  TIME;  1、禁用發送超時定時器OVERTIME,0、允許。

// FLAG.5-0  RES; 備用。

   BU24  BACKLOG; // 最大連接數。

   BU8   ptype;   // 協議類型。

   BU24  RECVLEN; // 允許的最大報文長度單位E。

   BU32  recvstream_len;// 數據報流容器對象的大小,單位E

   BU32  recvstream_p;  // 接收的數據報流容器本地內存空間指針。

   BU32  sendstream_p;  // 發送的數據報流容器本地內存空間指針。

   BU32  Srecvstream_p; // 接收的信令數據報流容器內存空間指針。

   BU32  Ssendstream_p; // 發送的信令數據報流容器內存空間指針。

   BU16  recepn;  // 一個TCP報文的接收IP數據包的計數。

   BU16  sendpn;  // 一個TCP報文的發送IP數據包的計數。

   BU32  crtime; // 連接創建時間標識,單位10mS。

   BU16  OVERTIME;// 發送超時定時器。

   BU16  CMPTIME; // 發送超時定時器比較值。

   BU16  MSS;    // 協商的MSS。

   BU16  MTU;    // 本機的MTU。

   BU2W。。。。。待定

   BU32  SCOUNT; // 本連接的發送數據報總計數。

   BU32  RCOUNT; // 本連接的接收數據報總計數。

}

   BU1E  MACIP; // APO的發送IP頭、1E。

   BU1E  TCP;   // APO的發送TCP協議頭、1E。

}


四、重傳定時器方法OVERTIME()

 

     重傳定時器只是在信令數據報發送時設置,接收是不需要的。爲了支持上千萬個連接,對於每一個連接的重傳定時器是一種軟件定時器。0.5秒的硬件時鐘定時器會設置狀態寄存器MSR的TIME5標誌,考慮到這並不需要實時處理,原先的0.5秒定時器軟中斷實時線程TSINT()將取消;不再是由內核CPU來處理,而是由用戶CPU的10ms中斷來進行。我們知道,內存v節點最多隻是16M個,由256個64K位圖來反映;也並非全部的內存v節點都是描述連接的。用戶CPU的10ms中斷程序一次只處理64K個內存v節點,就算這64K個全是描述連接,也就約2ms。16M個內存v節點就算有1.6千萬個是描述連接,那麼在0.5秒內也肯定能循環處理完。對於每一個內存v節點,先是判斷是否socket文件,否下一個內存v節點、是再判斷FLAG.6(TIME)= 0?如是1、禁用發送超時定時器,則下一個內存v節點;如是0、OVERTIME發送超時定時器增1,判斷超時否、超時處理;循環64K次。這樣,第一次超時或許是0.5秒 + x(x<0.5秒);這沒關係的,對於超時重發我們並不需要那麼精確;而是注重於每一個連接都要有一個信令發送超時定時機制。重傳定時器方法OVERTIME()只是用戶CPU的10ms中斷程序中的一個方法。

    誒,過完年、心也亂了,需要一定時間恢復;慢慢來吧,估計代碼量要超出預計的160行。

     內存v節點中模式空間16M個單位、單位4E( 128B, 64Z, 32W),子空間就是一個64K單位 = 4個內存數據塊(UNITLEN = 4)。OVERTIME()方法每次處理一個子空間,我們需要一個標識子空間序號的變量SUBSPACE,和一個指示子空間處理的計數變量COUNT。

BU16  SUBSPACE; // 標識子空間序號,最初爲0。

BU16  COUNT;// 子空間處理的計數,最初和循環結束時爲VNODE_IMODE1.ACTN。


OVERTIME()方法

佔用:36W、R26-R31,

最大耗時約:1.3ms。

OVERTIME(){ // 在用戶進程調到模塊中的信令重傳定時器方法。

   R1 = #VNODE_IMODE1; // 指向內存v節點中模式空間的管理變量區。

   R3 = SUBSPACE;      // R3H = COUNT, R3L爲SUBSPACE。

   R2 = R1.SSPN.R3L.W;  // R2指向子空間首地址。

   if R2 > 1  goto  OVE3;// 有效跳。

OVE1:      // 未分配、無效、下一64K個v節點循環、返回。

   R3L+; R3H-;

   if R3H = 0  goto  OVE2; // 循環結束,清標誌TIME5。

   SUBSPACE = R3; // 保存

   RET

OVE2:

   R3H = R1.ACTN.Z; R3L = 0; // 初始化COUNT、SUBSPACE。

   SUBSPACE = R3; // 保存

   PSR.TIME5 = 0; RET

OVE3:

   R4L = 64K; // 設置循環計數。

OVE4:

   BT0 R2.i_mode.15, OVE5; // 非socket文件跳下一個v節點。

   BT1 R2.i_mode.14, OVE5; // 禁用發送超時定時器OVERTIME跳。

   R4H = R2.OVERTIME.Z+;    // 超時定時器增1。

   if R4H = 1  goto  OVE5; // 0.5秒 + x(x<0.5秒)纔會第一次重發。

   if R4H <= 3  goto  RSPACK; // 跳、超時重發信令數據包。

   if R4H = R2.CMPTIME.Z  goto  RTIME; // 定時時間到處理。

   R31 = #sock_port_tab;

   R30 = R31.R0L.W.; // R30爲端口表項字指針,後面是.結尾、賦值地址。

   (R30).W.FIN = 0;  // 2次重發失敗、釋放連接。

   R2.i_mode.14 = 1; // 禁用發送超時定時器OVERTIME。

OVE5:

   R2 = +4;  // 指向下一個v節點。

   if R4L- != 0  goto  OVE4; // 循環

   JMP OVE1; // 跳下一64K個v節點循環做準備。

RSPACK:

   INTDP(); // 安裝發送信令數據包。

   JMP   OVE5;

RTIME:

…待定、不一定使用。

}


安裝一個發送數據包的方法INTDP()。    

出口:

安裝一個IP數據包到發送隊列緩衝區,PACKETSN發送隊列的數據包數加一,IDLELN發送隊列緩衝區的空閒行數減少爲發送的數據包行長度,IDLEDPP發送隊列緩衝區的首空閒數據包指針爲指向下一個發送空閒數據包位置。

佔用:16W、R26-R29

最大耗時:38ns,通常信令包長度3E、耗時才16ns、平均估計20ns。

INTDP(){ // 安裝一個發送數據包。

   R1 = #SENDPV;   // R1指向SENDPV。

   R1.PACKETSN.Z+; // 發送隊列的數據包數加一。

   R27 = R1.IDLEDPP.W; // 提取發送首空閒數據包指針給R27。

   R28 = R2.Ssendstream_p.W; // R28指向發送信令包。

   R29 = R28.TYPE.Z.10-5; // 提取數據包的TYPE參數的行長度值。

   R26 = R27 + R29;  // R26爲下一個空閒發送數據包指針。

   if  R26<= SENDBUFMAX  goto  IND1; // 沒有越界、跳。

   R27 = #SENDBUF; R26 = R27 + R29; // 重設下一空閒發送數據包指針。

IND1:

   R1.IDLEDPP.W = R26; // 保存下一個空閒發送數據包指針。

   COPY.E( R27, R28, R29L );// 拷貝當前需發送的IP數據包到緩衝隊列。

   RET

}



五、ICMPAPO信令分支處理

 

    ICMPAPO報文類型有3種:宣告、鄰居請求、探查。所有網絡上的節點都要處理這3種報文,主機可以主動發送這3種報文,但路由器、交換機只是能主動發送前2種。ICMPAPO信令報文的響應報文是IPAICMP信令報文,路由、交換機對於進入的IPAICMP信令報文只是轉發。

ICMPAPO報文格式:

ICMPAPO_IP{ // ICMPAPO報文的IP數據包

  BU1E MACIP;    // APO的發送IP頭,

  BU1E TCP;      // APO的發送TCP協議頭,

  BU1E ICMPAPO{ // APO的ICMPAPO協議頭、1E,無連接。

  BU16   imcptype;// 報文類型和項數。

// imcptype.8 search;  1、探查,0、宣告、或鄰居請求。

// imcptype.9  declare; 1、宣告,0、鄰居請求、

// imcptype.15-10 coden;  代碼號,路由、交換機只管imcptype.9-8。

// imcptype.7-0  segnum; 附帶數據項印記指針(項數、每項16字節)。

  BU16  devtype;// 源設備類型(主機,2-3-4層交換機等)、網絡中的級數。 

  BU32  PTID;   // 源設備的進程號(或端口號)、線程號(或標識)。

  BU32  times;  // 報文發送時的時間戳us。

  BU16  MTU;    // 源設備的MTU。

  BU16  cheksum;// 校驗和。

  BU16B icmpdata; // 第0項的附帶數據,後面接着是更多的附帶數據。

  }

}

 

1、主機發送ICMPAPO報文。


     宣告、鄰居請求信令報文是系統內建的鄰居進程和0號連接(0號內存v節點)來實現的,探查信令報文通常是用於應用進程的建立連接過程、在Message()消息處理、事件驅動層來實現。定時重傳機制是較爲獨立的一個模塊,通常的ICMP信令報文是沒用重傳機制的,但我們可以很容易在ICMPAPO探查信令報文增加定時重傳機制。


2、主機接收ICMPAPO報文。


    路由、交換機沒必要向主機發送宣告、鄰居請求信令報文,所以不用管其接收;主要的是用於建立連接的探查信令報文接收。


ICMPAPO探查信令報文接收方法:

出口:

佔用:17W、R26-R31

最大耗時:?ns,

ICMPAPO:

   BT1 H162.search, ICM1; // 探查跳。

   RET

ICM1:

   Getlinkd(); // R1接收信令報文的位圖緩衝指針,數據包行長度R0H,

// R2 = H160,R3指向數據報文接收流容器,R4爲數據包數+1、MSS。

// R30爲端口表項字指針,R31指向連接的內存v節點。

   BT1 H161.SYN, ICM3;    // 連接信令,跳。

ICM2:                     // 非連接信令的探查報文提交。

   R3 = R31.Srecvstream_p;// R3指向接收的信令數據報流容器。

   COPYH( R3, R2, R0H );  // 拷貝信令報文。

   JMP  TAP3;

ICM3:

   BT1 H161.ACK, ICM4;  // 連接信令ACK,跳。

   H161.ACK = 1;  // 對方請求連接,設置ACK,轉發該IP包。

   REV;           // 反轉源、目標地址指令。

   R28 = H160;

   JMP INTDP1;      // 安裝一個發送數據包方法。

ICM4:

   R31.FLAG.TIME = 1; // 禁用發送超時定時器OVERTIME。

   R31.OVERTIME.Z = 0;// 復位發送超時定時器OVERTIME。

   R0L= H162.MTU;

   R31.MSS.Z = R0L;   // 設置MSS。

   JMP  ICM2;

 

 

六、IPAICMP信令分支處理

 

    IPAICMP信令報文類型:宣告0的應答、鄰居請求1的應答; 差錯報文類型:2目標不可到達,超時3,源端抑制4,參數問題5。主機不發送IPAICMP信令報文,只是接收。IPAICMP信令報文格式與ICMPAPO的類同,只是參數imcptype不一樣。路由、交換機只是轉發目的地非自己的IPAICMP信令報文。對於TCP數據分段報文變爲IPAICMP報文只是將報文內容的第一行覆蓋IPAICMP信令報文頭,並設置報文類型等。


  BU16  imcptype;// 報文類型和項數。

// imcptype.11-8   type; 報文類型0-5。

// imcptype.15-12  coden; 代碼號。

// imcptype.7-0  segnum; 附帶數據項印記指針(項數、每項16字節)。


IPAICMP信令報文接收方法:

出口:

佔用:14W+、R26-R31

最大耗時:?ns,

IPAICMP:

   R0L= H162.TYPE.imcptype.11-8;// 提取接收IP數據包imcptype類型。

   Switch(R0L){ // 據R0L的值跳轉;PPCL = (PPCL + 1).R0H。

   SEARCH; DECLARE; NOTREACH; OVERTIME;SREST; PPROB; RET; RET;

   }
SEARCH:

   RET   // 宣告的應答不管

DECLARE: // 由用戶端交換機或路由器發出的鄰居請求應答

   JMP  ICM2; // 提交

NOTREACH: //目標不可到達、釋放連接、提交。

OVERTIME: //超時、釋放連接、提交。

PPROB:// 參數問題、釋放連接、提交。

   (R30).W.FIN = 0;      // 釋放連接。

   JMP  ICM2; // 提交。

SREST: // 源端抑制處理。

….

 

 

七、Message()消息處理、事件驅動層

 

     請求分配和釋放內存v節點(連接)都是在用戶層實現的,opens( PORT )一個客戶端連接(指定或不指定端口),實際上就是請求分配內存v節點(連接);建立一個服務端連接opens( PORT, BACKLOG, RECVLEN ) 也是類似,但請求分配的內存v節點(連接)是作爲將要到來的客戶端連接;當一個客戶端請求連接信令包到來後,使用了該內存v節點、同時將該請求連接包提交消息給用戶層,用戶層處理該消息時再請求分配一個新的的內存v節點(連接)。。。

 

    釋放連接信令包也同樣需要提交給用戶層,以便用戶層釋放內存v節點(連接)。所以,請求連接、釋放連接信令包都需要提交消息給用戶層,用戶層必須處理這2種消息。用戶層發送TCP數據報文sendto(sockfd, flags, msg, len ),是提交消息給內核的Message()消息處理、事件驅動層,由此層分段和安裝IP數據包。這層並不複雜,也容易編寫。


     跌跌撞撞的到此,剩下的代碼不多了,我也沒力氣編寫、檢查和修改了;寫了一個多月啊,我的心已不在,只好留待以後完善了。來自體內遠古細胞的呼喚、來自外河系中孤獨流浪多年的思感粒子的糾纏、來自心靈神國的吶喊,造成主線程經常性的中斷;即使有漫天的小說,即使有軍旗、飛行棋等簡單遊戲的阻擋,我也不得不暫停下來了;就讓我的靈魂重新沉入微觀世界,追尋神的足跡吧。


以後待續。。。

 




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章