最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

探析如何使用SystemTap觀測TCP?Backlog

 更新時間:2023年04月23日 17:01:53   作者:解月月的bug  
這篇文章主要為大家介紹了使用SystemTap觀測TCP?Backlog過程解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

什么是TCP Backlog

本文所使用的Linux內(nèi)核版本信息

5.15.0-56-generic #62-Ubuntu SMP Tue Nov 22 19:54:14 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

backlog的中文含義是 積壓 的意思,在Linux網(wǎng)絡(luò)中,意味著網(wǎng)絡(luò)數(shù)據(jù)包的積壓,在Linux表現(xiàn)為半連接隊(duì)列和全連接隊(duì)列存儲這些積壓的數(shù)據(jù)包。backlog參數(shù)的大小,則會影響半連接隊(duì)列和全連接隊(duì)列緩存數(shù)據(jù)包的多少。

其中,半連接隊(duì)列和全連接隊(duì)列的含義如圖所示(此處引用張師傅博客中的圖)

  • 半連接隊(duì)列(Incomplete connection queue),又稱 SYN 隊(duì)列
  • 全連接隊(duì)列(Completed connection queue),又稱 Accept 隊(duì)列

從服務(wù)端角度看待TCP三次握手的過程,有以下幾步:

  • 調(diào)用 listen 函數(shù)時,TCP 的狀態(tài)被從 CLOSE 狀態(tài)變?yōu)?LISTEN,此時內(nèi)核就創(chuàng)建了半連接隊(duì)列和全連接隊(duì)列。backlog參數(shù)就是在listen的時候指定的。
int listen(int sockfd, int backlog);
  • 在TCP進(jìn)行三次握手的時候,收到SYN報文會先將數(shù)據(jù)包放到半連接隊(duì)列,然后發(fā)出SYN+ACK
  • 接著當(dāng)收到對端的SYN+ACK的時候,再將這個連接請求的數(shù)據(jù)包移動到全連接隊(duì)列,等待應(yīng)用程序通過accept() 函數(shù)讀取。

我們可以通過listen函數(shù)傳入backlog參數(shù)值,且backlog參數(shù)值會影響到半連接隊(duì)列和全連接隊(duì)列的大小,但是我們該怎么觀測到最終操作系統(tǒng)使用的backlog的大小呢?又怎么觀測到半連接隊(duì)列、全連接隊(duì)列中的緩存的包數(shù)量呢?backlog參數(shù)和半連接隊(duì)列、全連接隊(duì)列的大小之間又有什么關(guān)系呢?

實(shí)驗(yàn)環(huán)境搭建

先在本地電腦上啟動了兩個虛擬機(jī),Linux虛擬機(jī)1(命名為L1,ip: 10.211.55.6)和Linux虛擬機(jī)2(命名為L2,ip: 10.211.55.8),以 L1 作為服務(wù)器,L2作為客戶端。

觀測Linux最終采用的backlog大小

為確定backlog值通過listen函數(shù)設(shè)置進(jìn)去之后,操作系統(tǒng)最終采用的數(shù)值,可以通過systemtap工具來確定。安裝好systemtap工具之后,編寫探測腳本如下:

probe kernel.function("tcp_v4_conn_request") {  
    tcphdr = __get_skb_tcphdr($skb);  
    dport = __tcp_skb_dport(tcphdr);  
    if (dport == 9090)  
    {  
        printf("reach here\n");  
        printf("socket struct: %s \n", $sk$);  
        syn_qlen = @cast($sk, "struct inet_connection_sock")->icsk_accept_queue->qlen;  
        max_backlog=$sk->sk_max_ack_backlog;  
        printf("qlen: %d, max_backlog: %d  \n", syn_len, max_backlog);  
    }  
}

這個腳本做的事情,就是對linux中 tcp_v4_conn_request 這個內(nèi)核函數(shù)做了探針,只要調(diào)用到這個內(nèi)核函數(shù),且端口號為9090,就會執(zhí)行一系列的打印操作。其中,會將socket對象打印出來,也會將socket對象中的 sk_max_ack_backlog 變量打印出來,這個變量正是linux最終采用的backlog值。

將這個腳本放到機(jī)器L1中的任一用戶目錄下,腳本命名為 tcp_backlog.stp,然后用命令執(zhí)行:

sudo stap -v tcp_backlog.stp

如果運(yùn)行成功,則會看到在終端上顯示正在運(yùn)行的提示:

此時,為避免編程語言的干擾,用C語言準(zhǔn)備一段服務(wù)器的啟動代碼,backlog值可以通過修改常量來更改,這里使用backlog值為20

// main.c
#include <sys/socket.h>  
#include <stdio.h>  
#include <netinet/in.h>  
#include <unistd.h>  
#include <string.h>  
#include <stdlib.h>  
#include <sys/shm.h>  
#define MYPORT  9090  
#define BACKLOG 20  
#define BUFFER_SIZE 1024  
int main()  
{  
    ///定義sockfd  
    int server_sockfd = socket(AF_INET,SOCK_STREAM, 0);  
    ///定義sockaddr_in  
    struct sockaddr_in server_sockaddr;  
    server_sockaddr.sin_family = AF_INET;  
    server_sockaddr.sin_port = htons(MYPORT);  
    server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
    ///bind,成功返回0,出錯返回-1  
    if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1)  
    {  
        perror("bind");  
        exit(1);  
    }  
    ///listen,成功返回0,出錯返回-1  
    if(listen(server_sockfd, BACKLOG) == -1)  
    {  
        perror("listen");  
        exit(1);  
    }  
    ///客戶端套接字  
    char buffer[BUFFER_SIZE];  
    char message[100] = "已成功接收!";  
    struct sockaddr_in client_addr;  
    socklen_t length = sizeof(client_addr);  
    ///成功返回非負(fù)描述字,出錯返回-1  
    int conn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length);  
    if(conn<0)  
    {  
        perror("connect");  
        exit(1);  
    }  
    while(1)  
    {  
        memset(buffer,0,sizeof(buffer));  
        int size = read(conn, buffer, 1024);  
        if(strcmp(buffer,"exit\n")==0)  
            break;  
        strncat(buffer, message, 100);  
        fputs(buffer, stdout);  
        write(conn,buffer,strlen(buffer)+1);  
    }  
    close(conn);  
    close(server_sockfd);  
    return 0;  
}

在L1上通過命令編譯sk.c 并啟動:

gcc main.c -o sk.o && ./sk.o 

啟動后,在L2上通過nc命令連接L1的9090端口:

nc 10.211.55.6 9090

接著觀察 tcp_backlog.stp 探針腳本的輸出:

可見此時使用的backlog值為20,通過這個方法,我們可以觀測到linux最終采用的 backlog值的大小是多少了。

<>系統(tǒng)變量對backlog大小的影響

backlog雖然可以通過listen設(shè)置進(jìn)去,但是按照張師傅的博客所說,最終的大小會受到操作系統(tǒng)的配置影響??赏ㄟ^sysctl命令查看這兩個系統(tǒng)變量:

sysctl net.ipv4.tcp_max_syn_backlog
# net.ipv4.tcp_max_syn_backlog = 128
sysctl net.core.somaxconn
# net.core.somaxconn = 4096

按照上述觀測的方法,函數(shù)傳入的backlog值分別在 小于128,大于128但小于4096,大于4096這三個區(qū)間取一個值。設(shè)置backlog大小為 20、200、6000,分別觀測操作系統(tǒng)最終采用的backlog值如下:

listen backlog值為200時,操作系統(tǒng)采用的backlog值為200

listen backlog值為6000時,操作系統(tǒng)采用的backlog值為4096,和系統(tǒng)變量 net.core.somaxconn 保持一樣。

將上述測試數(shù)據(jù)總結(jié)如下:

listen backlog值操作系統(tǒng)實(shí)際采用的backlog值
2020
200200
60004096

在張師傅的博客中提到, Linux內(nèi)核版本在3.10.0的時候,會受到 net.ipv4.tcp_max_syn_backlognet.core.somaxconn 的影響,且受這兩個變量影響的邏輯還比較復(fù)雜。但是在 5.15.0版本中,已經(jīng)做了簡化,代碼如下:

// net/socket.c
int __sys_listen(int fd, int backlog)
{
  struct socket *sock;
  int err, fput_needed;
  int somaxconn;
  sock = sockfd_lookup_light(fd, &err, &fput_needed);
  if (sock) {
    # sysctl_somaxconn對應(yīng)系統(tǒng)變量net.core.somaxconn的值
    somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;
    if ((unsigned int)backlog > somaxconn)
      backlog = somaxconn;
    err = security_socket_listen(sock, backlog);
    if (!err)
      err = sock->ops->listen(sock, backlog);
    fput_light(sock->file, fput_needed);
  }
  return err;
}

再簡化一下核心邏輯,核心邏輯的偽代碼如下:

backlog = listen_backlog;
somaxconn = valuOf(`net.core.somaxconn`);
if(backlog > somaxconn) {
	backlog = somaxconn;
}

按張師傅的博客所說,在內(nèi)核版本為3.10.0中, backlog 值會在這個時候依次傳遞給 __sys_listen() -> inet_listen()->inet_csk_listen_start()->reqsk_queue_alloc(),最終在 reqsk_queue_alloc函數(shù)中根據(jù)這兩個系統(tǒng)變量經(jīng)歷一系列復(fù)雜的計(jì)算,最終得到操作系統(tǒng)使用的backlog值。但是這些操作,在5.x版本的內(nèi)核都去掉了,reqsk_queue_alloc函數(shù)中不再對backlog做過任何處理:

// net/ipv4/inet_connection_sock.c
// 在這個函數(shù)中,雖然傳入了backlog,但是在后續(xù)的處理中完全沒有用上,由此證明backlog的賦值,在 __sys_listen 函數(shù)中已經(jīng)完成
int inet_csk_listen_start(struct sock *sk, int backlog)
{
  struct inet_connection_sock *icsk = inet_csk(sk);
  struct inet_sock *inet = inet_sk(sk);
  int err = -EADDRINUSE;
  reqsk_queue_alloc(&icsk->icsk_accept_queue);
  sk->sk_ack_backlog = 0;
  inet_csk_delack_init(sk);
  /* There is race window here: we announce ourselves listening,
   * but this transition is still not validated by get_port().
   * It is OK, because this socket enters to hash table only
   * after validation is complete.
   */
  inet_sk_state_store(sk, TCP_LISTEN);
  if (!sk->sk_prot->get_port(sk, inet->inet_num)) {
    inet->inet_sport = htons(inet->inet_num);
    sk_dst_reset(sk);
    err = sk->sk_prot->hash(sk);
    if (likely(!err))
      return 0;
  }
  inet_sk_set_state(sk, TCP_CLOSE);
  return err;
}
// net/core/request_sock.c
void reqsk_queue_alloc(struct request_sock_queue *queue)
{
  spin_lock_init(&queue->rskq_lock);
  spin_lock_init(&queue->fastopenq.lock);
  queue->fastopenq.rskq_rst_head = NULL;
  queue->fastopenq.rskq_rst_tail = NULL;
  queue->fastopenq.qlen = 0;
  queue->rskq_accept_head = NULL;
}

觀測半連接隊(duì)列大小

在三次握手的過程中,服務(wù)端收到握手請求包之后,會先把它放到半連接隊(duì)列中,然后回復(fù)SYN+ACK。接著接收到客戶端返回的ACK報文時,再把這個數(shù)據(jù)包從半連接隊(duì)列移動到全連接隊(duì)列中。在正常情況下,SYN報文在半連接隊(duì)列逗留的時間會很快,觀測半連接隊(duì)列大小要做點(diǎn)處理。

按照張師傅博客提供的方法,可以在客戶端設(shè)置防火墻,把服務(wù)端返回的ACK包都扔掉,這樣在服務(wù)端就不會收到ACK報文了。

// 在L2機(jī)器上設(shè)置這條防火墻規(guī)則
sudo iptables --append INPUT --match tcp --protocol tcp --src 10.211.55.6 --sport 9090 --tcp-flags SYN SYN --jump DROP
// 查看防火墻規(guī)則是否設(shè)置成功
sudo iptables -L

接著用上述的服務(wù)端代碼啟動服務(wù)后,在L2上通過nc命令連接上:

nc 10.211.55.6 9090

接著可以通過以下命令觀察到,當(dāng)前有多少個連接處于SYN_RECV狀態(tài):

sudo netstat -lnpa | grep :9090  | awk '{print $6}' | sort | uniq -c | sort -rn

處于SYN_RECV狀態(tài)的連接,意味著接收到了客戶端的SYN報文但未接收到ACK報文。此時連接就處于SYN_RECV狀態(tài)。通過這個點(diǎn)可以觀測到半連接隊(duì)列此時的大小是多少。你也可以在L2上通過程序發(fā)起多次連接,看看SYN_RECV狀態(tài)的連接數(shù)是否有變化,此處就不再敘述了。

觀測全連接隊(duì)列大小

當(dāng)請求收到ACK之后,就會從半連接隊(duì)列挪到全連接隊(duì)列,此時連接已經(jīng)完全建立,連接狀態(tài)就會從LISTEN變成ESTABLISHED狀態(tài),等待應(yīng)用程序調(diào)用accept函數(shù)從全連接隊(duì)列中取走數(shù)據(jù)。所以,要觀察全連接隊(duì)列的大小,只要觀察ESTABLISHED狀態(tài)的連接數(shù)即可。同樣可以采用netstat命令:

netstat -lnpa | grep :9090 | awk '{print $6}' | sort | uniq -c | sort -rn

也可以使用ss命令來進(jìn)行觀測。使用命令如下:

ss -lnt | grep :9090
  • 處于 LISTEN 狀態(tài)的 socket,Recv-Q 表示 accept 隊(duì)列排隊(duì)的連接個數(shù),Send-Q 表示全連接隊(duì)列(也就是 accept 隊(duì)列)的總大小
  • 對于非 LISTEN 狀態(tài)的 socket,Recv-Q 表示 receive queue 的字節(jié)大小,Send-Q 表示 send queue 的字節(jié)大小

總結(jié)

SystemTap是一個很有力的工具,用好這個工具,可以實(shí)實(shí)在在地觀測到Linux內(nèi)部的狀態(tài),讓自己對操作系統(tǒng)有個更深刻的認(rèn)識。

以上就是使用SystemTap觀測TCP Backlog過程解析的詳細(xì)內(nèi)容,更多關(guān)于SystemTap觀測TCP Backlog的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • DevEco Studio 2.0開發(fā)鴻蒙HarmonyOS應(yīng)用初體驗(yàn)全面測評(推薦)

    DevEco Studio 2.0開發(fā)鴻蒙HarmonyOS應(yīng)用初體驗(yàn)全面測評(推薦)

    這篇文章主要介紹了DevEco Studio 2.0開發(fā)鴻蒙HarmonyOS應(yīng)用初體驗(yàn)全面測評,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • 鴻蒙HarmonyOS剪切板的實(shí)現(xiàn)

    鴻蒙HarmonyOS剪切板的實(shí)現(xiàn)

    這篇文章主要介紹了鴻蒙HarmonyOS剪切板的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • 在HarmonyOS工程中添加Module的方法

    在HarmonyOS工程中添加Module的方法

    Module是HarmonyOS應(yīng)用的基本功能單元,包含了源代碼、資源文件、第三方庫及應(yīng)用清單文件,每一個Module都可以獨(dú)立進(jìn)行編譯和運(yùn)行。這篇文章主要介紹了在HarmonyOS工程中添加Module的方法,需要的朋友可以參考下
    2020-09-09
  • windows磁盤I/O的性能評估方法詳解

    windows磁盤I/O的性能評估方法詳解

    這篇文章主要介紹了windows磁盤I/O的性能評估方法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-09-09
  • 如何在Windows中監(jiān)視IO性能

    如何在Windows中監(jiān)視IO性能

    這篇文章主要介紹了如何在Windows中監(jiān)視IO性能,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-09-09
  • 淺析鴻蒙基礎(chǔ)之Permanent 持久性內(nèi)存對象(HarmonyOS鴻蒙開發(fā)基礎(chǔ)知識)

    淺析鴻蒙基礎(chǔ)之Permanent 持久性內(nèi)存對象(HarmonyOS鴻蒙開發(fā)基礎(chǔ)知識)

    這篇文章主要介紹了淺析鴻蒙基礎(chǔ)之Permanent 持久性內(nèi)存對象(HarmonyOS鴻蒙開發(fā)基礎(chǔ)知識),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • 硬盤如何分區(qū)的方法

    硬盤如何分區(qū)的方法

    硬盤如何分區(qū)的方法...
    2007-02-02
  • windows10打開switchHost 提示無修改權(quán)限問題解決

    windows10打開switchHost 提示無修改權(quán)限問題解決

    這篇文章主要介紹了windows10打開switchHost,提示無修改權(quán)限問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-09-09
  • Jetson tk2開發(fā)部署實(shí)現(xiàn)過程圖解

    Jetson tk2開發(fā)部署實(shí)現(xiàn)過程圖解

    這篇文章主要介紹了Jetson tk2開發(fā)部署實(shí)現(xiàn)過程圖解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-11-11
  • HarmonyOS鴻蒙基本控件的實(shí)現(xiàn)

    HarmonyOS鴻蒙基本控件的實(shí)現(xiàn)

    這篇文章主要介紹了HarmonyOS應(yīng) 基本控件的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09

最新評論

石门县| 界首市| 岚皋县| 宜丰县| 陆河县| 大方县| 徐汇区| 靖州| 沙坪坝区| 乐陵市| 澄迈县| 昆明市| 榆社县| 都昌县| 民权县| 大余县| 阳城县| 大余县| 叶城县| 青岛市| 商都县| 金华市| 阿克陶县| 潜江市| 姚安县| 修武县| 即墨市| 安达市| 疏勒县| 巴东县| 屏山县| 永兴县| 威宁| 鹤庆县| 龙口市| 元朗区| 来安县| 周宁县| 澄城县| 凤城市| 阳西县|