linux機制


  • socket操作函數read/write和recv/send用法基本相同,后者比前者多了一個flag參數。詳見套接字I/O函數。如果是阻塞socket,執行讀操作時,如果socket接收緩存區沒有數據會阻塞等待數據;執行寫操作時,如果socket發送緩存區沒有足夠的空間存放此次寫入的數據,則會阻塞等待緩存區釋放。讀/寫數據到緩存區成功后會立即返回,但寫入socket緩存區並不代表數據會成功發送到對端,例如接收到TCP RST報文會導致TCP斷鏈並清空緩存區數據。如果是非阻塞socket,在執行讀操作時,如果socket接收緩存區沒有數據,則直接返回EWOULDBLOCK錯誤;在執行寫操作時,如果socket發送緩沖區中有足夠空間或者是不足以拷貝所有待發送數據的空間的話,則拷貝前面N個能夠容納的數據,返回實際拷貝的字節數,socket發送緩存區沒有空間時會返回EWOULDBLOCK錯誤。可以使用fcntl函數為文件描述符設置O_NONBLOCK標記來實現非阻塞socket。
  • 阻塞/非阻塞socket是相對一條連接來說的。使用非阻塞可以防止讀寫線程阻塞,一般用於服務端。golang的read/write是阻塞的,但底層是非阻塞的,可以使用多協程實現非阻塞。

參考

淺談TCP/IP網絡編程中socket的行為

  •  Linux進程調度
  • IO復用主要是服務端通過select(),poll(),epoll()等方式,可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒,就是這個文件描述符進行讀寫操作之前),能夠通知程序進行相應的讀寫操作。但它們本質上還是同步IO。

參考:

I/O多路復用select、poll、epoll的區別使用

  • 零拷貝主要是減少用戶空間到內核空間的拷貝次數。零拷貝通常使用mmap,sendfile,FileChannel,DMA等技術實現。使用sendfile時用戶無法對文件進行修改,但使用mmap時可以修改文件。從Linux 2.4版本開始,操作系統底層提供了scatter/gather這種DMA的方式來從內核空間緩沖區中將數據直接讀取到協議引擎中,而無需將內核空間緩沖區中的數據再拷貝一份到內核空間socket相關聯的緩沖區中,此時只有外設緩存區滿時寫操作才會阻塞。

參考:

淺談 Linux下的零拷貝機制

  •  TCP
    • TCP的TIME_WAIT有兩個作用:
      • 防止前一個TCP連接的殘留數據(在序列號恰好正確的情況下)進入后續的TCP連接中
      • 防止TCP揮手過程發出去的最后一個ACK報文丟棄,此時需要重傳該ACK報文
  • fase sharing:字節對齊的原理
  • Linux 網絡隊列:IP棧的報文提交會直接到QDisc隊列,QDisc可以使用一定的策略來管控流量
    • BQL通過自動調節到driver queue的數據長度,來防止driver queue中的數據過大造成Bufferbloat而產生延遲。TSO, GSO, UFO和GRO可能會導致驅動隊列中數據的增加,因此為了提高吞吐量的延遲,可以關閉這些功能。
    • QDisc(Queuing Disciplines)位於IP棧和driver queue之間,實現了流量分類,優先級划分和速率管控等。可以使用tc命令配置。QDisc有三個關鍵概念:QDiscs,classes和filter
      • QDisc用於流量隊列。Linux實現了大量QDisc來滿足各個QDisc對應的的報文隊列和行為。該接口允許QDisc可以在沒有IP棧和NIC驅動修改的前提下實現隊列管理。默認情況下,每個網卡都會分配一個pfifo_fast類型的QDisc 。
      • 第二個是與QDisc緊密相關的class。獨立的QDisc 可能會實現class來處理其不同的流量。
      • Filters用於按照QDisc 或class來區分流量。

  • TCP rtt和rto
  • TCP擁塞避免算法,目前主流Linux的默認擁塞避免算法為cubic,可以使用ss -i命令查看。tcp有滑動窗口,擁塞窗口,滑動窗口為接收端可接收的數據大小,等於window_size*(2^tcp_window_scaling),可接收數據的緩存大小通過ACK報文通知對端;擁塞窗口可以通過ss -i的cwnd字段獲得,發送端發送的數據不能超過cwnd和接收方通告窗口的大小。
滑動窗口本質上是描述接受方的TCP數據報緩沖區大小的數據,發送方根據這個數據來計算自己最多能發送多長的數據。如果發送方收到接受方的窗口大小為0的TCP數據報,那么發送方將停止發送數據,等到接受方發送窗口大小不為0的數據報的到來。
關於滑動窗口協議,還有三個術語,分別是:

窗口合攏:當窗口從左邊向右邊靠近的時候,這種現象發生在數據被發送和確認的時候。  
窗口張開:當窗口的右邊沿向右邊移動的時候,這種現象發生在接受端處理了數據以后。  
窗口收縮:當窗口的右邊沿向左邊移動的時候,這種現象不常發生。  

下面看下tahoe算法,reno算法(快速重傳和快速恢復)和cubic算法的擁塞圖,這三個算法在慢啟動階段相同,基於超時或重復確認來確認是否切換到擁塞避免階段。不同點在擁塞避免階段。tahos算法的描為(來自TCP-IP詳解-卷1):

1) 對一個給定的連接,初始化cwnd為1個報文段, ssthresh為65535個字節。
2) TCP輸出例程的輸出不能超過cwnd和接收方通告窗口的大小。擁塞避免是發送方使用的流量控制,而通告窗口則是接收方進行的流量控制。前者是發送方感受到的網絡擁塞的估計,而后者則與接收方在該連接上的可用緩存大小有關。
3) 當擁塞發生時(超時或收到重復確認),ssthresh被設置為當前窗口大小的一半(cwnd和接收方通告窗口大小的最小值,但最少為2個報文段)。此外,如果是超時引起了擁塞,則cwnd被設置為1個報文段(這就是慢啟動)4) 當新的數據被對方確認時,就增加cwnd,但增加的方法依賴於我們是否正在進行慢啟動或擁塞避免。如果cwnd小於或等於ssthresh,則正在進行慢啟動,否則正在進行擁塞避免。慢啟動一直持續到我們回到當擁塞發生時所處位置的半時候才停止(因為我們記錄了在步驟2中給我們制造麻煩的窗口大小的一半),然后轉為執行擁塞避免。慢啟動算法初始設置cwnd為1個報文段,此后每收到一個確認就加1。這會使窗口按指數方式增長:發送1個報文段,然后是2個,接着是4個⋯⋯。擁塞避免算法要求每次收到一個確認時將cwnd增加1 /cwnd。與慢啟動的指數增加比起來,這是一種加性增長(additive increase)。我們希望在一個往返時間內最多為cwnd增加1個報文段(不管在這個RTT中收到了多少個ACK),然而慢啟動將根據這個往返時間中所收到的確認的個數增加cwnd。

下圖來自CSDN,可以看到tahoe算法在產生擁塞時會將cwnd設置為1,大大降低了傳輸效率

 reno使用快速重傳和快速恢復改進了tahoe算法:

快重傳
快重傳算法首先要求接收方每收到一個失序的報文段后就立即發出重復確認(為的是使發送方及早知道有報文段沒有到達對方)而不要等到自己發送數據時才進行捎帶確認。
接收方收到了M1和M2后都分別發出了確認。現在假定接收方沒有收到M3但接着收到了M4。顯然,接收方不能確認M4,因為M4是收到的失序報文段。根據可靠傳輸原理,接收方可以什么都不做,也可以在適當時機發送一次對M2的確認。但按照快重傳算法的規定,接收方應及時發送對M2的重復確認,這樣做可以讓發送方及早知道報文段M3沒有到達接收方。發送方接着發送了M5和M6。接收方收到這兩個報文后,也還要再次發出對M2的重復確認。這樣,發送方共收到了接收方的四個對M2的確認,其中后三個都是重復確認。快重傳算法還規定,發送方只要一連收到三個重復確認就應當立即重傳對方尚未收到的報文段M3,而不必繼續等待M3設置的重傳計時器到期。由於發送方盡早重傳未被確認的報文段,因此采用快重傳后可以使整個網絡吞吐量提高約20%快恢復
與快重傳配合使用的還有快恢復算法,其過程有以下兩個要點:

當發送方連續收到三個重復確認,就執行“乘法減小”算法,把慢開始門限ssthresh減半。這是為了預防網絡發生擁塞。請注意:接下去不執行慢開始算法。
由於發送方現在認為網絡很可能沒有發生擁塞,因此與慢開始不同之處是現在不執行慢開始算法(即擁塞窗口cwnd現在不設置為1),而是把cwnd值設置為慢開始門限ssthresh減半后的數值,然后開始執行擁塞避免算法(“加法增大”),使擁塞窗口緩慢地線性增大。

可以看到reno算法在發生擁塞避免時不會將cwnd變為1,這樣提高了傳輸效率,快速重傳和快速恢復機制也有利於更快探測到擁塞。

 reno算法在擁塞避免階段仍然是線性遞增的,而cubic源於BIC算法,在擁塞避免階段采用二分法查找最佳擁塞窗口,相比線性增加又增加了速率。下圖來自該paper

下面2張圖,第一張為reno算法下的擁塞避免,第二張為cubic算法下的擁塞避免,可以看到cubic的擁塞窗口逼近速度更快

  • 同主機進行TCP通信時,可能使用jumbo報文,導致MSS遠大於一般的1460字節。為了方式這種情況下導致創建和傳輸大量符合MTU要求的報文,Linux實現了TSO,USO和GSO,參見下面描述
In order to avoid the overhead associated with a large number of packets on the transmit path, the Linux kernel implements several optimizations: TCP segmentation offload (TSO), 
UDP fragmentation offload (UFO) and generic segmentation offload (GSO). All of these optimizations allow the IP stack to create packets which are larger than the MTU of the outgoing NIC. 
For IPv4, packets as large as the IPv4 maximum of 65,536 bytes can be created and queued to the driver queue. 
In the case of TSO and UFO, the NIC hardware takes responsibility for breaking the single large packet into packets small enough to be transmitted on the physical interface. For NICs without hardware support, 
GSO performs the same operation in software immediately before queueing to the driver queue.
  • Linux 命名空間:
    • mount命名空間:mount 命名空間通過隔離/proc/[pid]/mounts, /proc/[pid]/mountinfo, 和/proc/[pid]/mountstats文件,使得通過mount命令僅能查看到容器的掛載信息
    • Pid命名空間:通過掛載一個/proc命令使得在容器中使用ps -ef命令可以查看僅容器內部的進程
    • cgroup命名空間:容器所在的cgroup可以通過容器的進程查看/proc/$pid/cgroup,進而直到其對應的cgroup所在目錄
    • network命名空間:如果兩個進程屬於不同的命名空間,則它們的/proc/$pid/net的信息相同;如果兩個進程屬於相同的命名空間,則它們的/proc/$pid/net的信息相同。除/proc/$pid/net目錄外,網絡命名空間還隔離了/sys/class/net,/proc/sys/net,socket等。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM