socket的傳送與接收緩衝區

2021-07-29 06:44:26 字數 4354 閱讀 2564

應用程式可通過呼叫send(write, sendmsg等)利用tcp socket向網路傳送應用資料,而tcp/ip協議棧再通過網路裝置介面把已經組織成struct sk_buff的應用資料(tcp資料報)真正傳送到網路上,由於應用程式呼叫send的速度跟網路介質傳送資料的速度存在差異,所以,一部分應用資料被組織成tcp資料報之後,會快取在tcp socket的傳送快取佇列中,等待網路空閒時再傳送出去。同時,tcp協議要求對端在收到tcp資料報後,要對其序號進行ack,只有當收到乙個tcp 資料報的ack之後,才可以把這個tcp資料報(以乙個struct sk_buff的形式存在)從socket的傳送緩衝佇列中清除。

tcp socket的傳送緩衝區實際上是乙個結構體struct sk_buff的佇列,我們可以把它稱為傳送緩衝佇列,由結構體struct sock的成員sk_write_queue表示。sk_write_queue是乙個結構體struct sk_buff_head型別,這是乙個struct sk_buff的雙向鍊錶,其定義如下:

struct sk_buff_head ;

核心**中,先在這個佇列中建立足夠存放資料的struct sk_buff,然後向佇列存入應用資料。

結構體struct sock的成員sk_wmem_queued表示傳送緩衝佇列中已分配的位元組數,一般來說,分配乙個struct sk_buff是用於存放乙個tcp資料報,其分配位元組數應該是mss+協議首部長度。在我的實驗環境中,mss值是1448,協議首部取最大長度 max_tcp_header,在我的實驗環境中為224。經資料對齊處理後,最後struct sk_buff的truesize為1956。也就是佇列中每分配乙個struct sk_buff,成員sk_wmem_queue的值就增加1956。

struct sock的成員sk_forward_alloc是表示預分配長度。當我們第一次要為傳送緩衝佇列分配乙個struct sk_buff時,我們並不是直接分配需要的記憶體大小,而是會以記憶體頁為單位進行的預分配。

tcp協議分配struct sk_buff的函式是sk_stream_alloc_pskb。它首先根據傳入的引數指定的大小在記憶體中分配乙個struct sk_buff,如果成功,sk_forward_alloc取該大小值,並向上取整到頁(4096位元組)的整數倍。並累加到struct sock的成員sk_prot,也即表示tcp協議的結構體mytcp_prot的成員memory_allocated中,該成員是乙個指標,指向變數 tcp_memory_allocated,它表示的是當前整個tcp協議當前為緩衝區所分配的記憶體(包括讀緩衝佇列)

當把這個新分配成功的struct sk_buff放入到緩衝佇列sk_write_queue後,從sk_forward_alloc中減去該sk_buff的truesize值。第二次分配struct sk_buff時,只要再從sk_forward_alloc中減去新的sk_buff的truesize即可,如果sk_forward_alloc已經小於當前的truesize,則將其再加上乙個頁的整數倍值,並累加入tcp_memory_allocated。

也就是說,通過sk_forward_alloc使全域性變數tcp_memory_allocated儲存當前tcp協議總的緩衝區分配記憶體的大小,並且該大小是頁邊界對齊的。

(2)

前面講到struct sock的成員sk_forward_alloc表示預分配記憶體大小,用於向全域性變數mytcp_memory_allocated累加當前已分配的整個 tcp協議的緩衝區大小。之所以要累加這個值,是為了對tcp協議總的可用緩衝區大小作限制。表示tcp協議的結構體mytcp_prot還有幾個成員與緩衝區相關。

mysysctl_tcp_mem是乙個陣列,由mytcp_prot的成員sysctl_mem指向,陣列共有三個元素,mysysctl_tcp_mem[0]表示對緩衝區總的可用大小的最低限制,當前總共分配的緩衝區大小低於這個值,則沒有問題,分配成功。 mysysctl_tcp_mem[2]表示對緩衝區可用大小的最高硬性限制,一旦總分配的緩衝區大小超出這個值,我們只好把tcp

socket 的傳送緩衝區的預設大小sk_sndbuf減小為已分配緩衝佇列大小的一半,但不能小於sock_min_sndbuf(2k),但保證這一次的分配成功。mysysctl_tcp_mem[1]介於前面兩個值的中間,這是乙個警告值,一旦超出這個值,進入警告狀態,這個狀態下,根據呼叫引數來決定此次分配是否成功。

這三個值的大小是根據所在系統的記憶體大小,在初始化時決定的,在我的實驗環境中,記憶體大小為256m,這三個值分配是:96k,128k,192k。它們可以通過/proc檔案系統,在/proc/sys/net/ipv4/tcp_mem中進行修改。當然,除非特別需要,一般無需改動這些預設值。

mysysctl_tcp_wmem也是乙個同樣結構的陣列,表示傳送緩衝區的大小限制,由mytcp_prot的成員sysctl_wmem指向,其預設值分別是4k,16k,128k。可以通過/proc檔案系統,在/proc/sys/net/ipv4/tcp_wmem中進行修改。struct sock的成員sk_sndbuf的值是真正的傳送緩衝佇列的預設大小,其初始值取中間乙個16k。在tcp資料報的傳送過程中,一旦 sk_wmem_queued超過sk_sndbuf的值,則傳送停止,等待傳送緩衝區可用。因為有可能一批已傳送出去的資料還沒有收到ack,同時,緩衝佇列中的資料也可全部發出去,已達到清空緩衝佇列的目的,所以,只要在網路不是很差的情況下(差到沒有辦法收到ack),這個等待在一段時間後會成功的。

全域性變數mytcp_memory_pressure是乙個標誌,在tcp緩衝大小進入警告狀態時,它置1,否則置0。

(3)

mytcp_sockets_allocated是到目前為止,整個tcp協議中建立的socket的個數,由mytcp_prot的成員 sockets_allocated指向。可以在/proc/net/sockstat檔案中檢視,這只是乙個供統計檢視用的資料,沒有任何實際的限制作用。

mytcp_orphan_count表示整個tcp協議中待銷毀的socket的個數(已無用的socket),由mytcp_prot的成員orphan_count指向,也可以在/proc/net/sockstat檔案中檢視。

mysysctl_tcp_rmem是跟mysysctl_tcp_wmem相同結構的陣列,表示接收緩衝區的大小限制,由mytcp_prot的成員 sysctl_rmem指向,其預設值分別是4096bytes,87380bytes,174760bytes。它們可以通過/proc檔案系統,在 /proc/sys/net/ipv4/tcp_rmem中進行修改。struct sock的成員sk_rcvbuf表示接收緩衝佇列的大小,其初始值取mysysctl_tcp_rmem[1],成員sk_receive_queue 是接收緩衝佇列,結構跟sk_write_queue相同。

tcp socket的傳送緩衝佇列跟接收緩衝佇列的大小既可以通過/proc檔案系統進行修改,也可以通過tcp選項操作進行修改。套接字級別上的選項 so_rcvbuf可用於獲取和修改接收緩衝佇列的大小(即strcut sock->sk_rcvbuf的值),比如下列的**可用於獲取當前系統的接收緩衝佇列大小:

int rcvbuf_len;

int len = sizeof(rcvbuf_len);

if( getsockopt( fd, sol_socket, so_rcvbuf, (void *)&rcvbuf_len, &len ) < 0 )

printf("the recevice buf len: %d\n", rcvbuf_len );

而套接字級別上的選項so_sndbuf則用於獲取和修改傳送緩衝佇列的大小(即struct sock->sk_sndbuf的值),**同上,只需改so_rcvbuf為so_sndbuf即可。

獲取傳送和接收緩衝區的大小相對簡單一些,而設定的操作在核心中動作會稍微複雜一些,另外,在介面上也會有所差異,即由setsockopt傳入的表示緩衝區大小的引數是實際大小的1/2,即,如果想要設傳送緩衝區的大小為20k,則需要這樣呼叫setsockopt:

int rcvbuf_len = 10 * 1024;  //實際緩衝區大小的一半。

int len = sizeof(rcvbuf_len);

if( setsockopt( fd, sol_socket, so_sndbuf, (void *)&rcvbuf_len, len ) < 0 )

在核心中,首先核心要判斷新設定的值是否超過上限,若超過,則取上限為新值,傳送和接收緩衝區大小的上限值分別為sysctl_wmem_max和 sysctl_rmem_max的2倍。這兩個全域性變數的值是相等的,都為(sizeof(struct sk_buff) + 256) * 256,大概為64k負載資料,由於struct sk_buff的影響,實際傳送和接收緩衝區的大小最大都可設到210k左右。它們的下限是2k,即緩衝區大小不能低於2k。

另外,so_sndbuf和so_rcvbuf有乙個特殊的版本:so_sndbufforce和so_rcvbufforce,它們不受傳送和接收緩衝區大小上限的限制,可設定不小於2k的任意緩衝區大小。(完)

socket的傳送與接收緩衝區

應用程式可通過呼叫send write,sendmsg等 利用tcp socket向網路傳送應用資料,而tcp ip協議棧再通過網路裝置介面把已經組織成struct sk buff的應用資料 tcp資料報 真正傳送到網路上,由於應用程式呼叫send的速度跟網路介質傳送資料的速度存在差異,所以,一部分...

socket的傳送與接收緩衝區

應用程式可通過呼叫send write,sendmsg等 利用tcp socket向網路傳送應用資料,而tcp ip協議棧再通過網路裝置介面把已經組織成struct sk buff的應用資料 tcp資料報 真正傳送到網路上,由於應用程式呼叫send的速度跟網路介質傳送資料的速度存在差異,所以,一部分...

tcp socket的傳送與接收緩衝區

tcp socket的傳送緩衝區實際上是乙個結構體struct sk buff的佇列,我們可以把它稱為傳送緩衝佇列,由 結構體struct sock的成員sk write queue struct sk buf head 表示 sk write queue是乙個結構體struct sk buff h...