Linux核心網路協議棧8 socket監聽

2021-08-26 01:29:50 字數 3271 閱讀 1848

幾個問題

了解以下幾個問題的同學可以直接忽略下文:

1、listen 庫函式主要做了什麼?

2、 什麼是最大併發連線請求數?

3、什麼是等待連線佇列?

socket 監聽相對還是比較簡單的,先看下應用程式**:

listen( server_sockfd, 5) ;
其中,第乙個引數 server_sockfd為服務端 socket所對應的檔案描述符,第二個引數5 代表監聽socket 能處理的最大併發連線請求數,在2.6.26 核心中,該值為 256 ;

listen 庫函式呼叫的主要工作可以分為以下幾步:

1 、根據 socket檔案描述符找到核心中對應的 socket結構體變數;這個過程在《socket位址繫結 》 一文中描述過,這裡不再重述;

2 、設定 socket的狀態並初始化等待連線佇列;

3 、將 socket放入listen 雜湊表中;

listen呼叫**跟蹤

下面是 listen庫函式對應的核心處理函式:

asmlinkage long sys_listen(int fd, int backlog)

return err;

}

根據《建立socket 》 一文的介紹,例子中,這裡sock->ops-> listen(sock, backlog) 實際上呼叫的是 net/ipv4/af_inet.c:inet_listen() 函式:

int inet_listen(struct socket *sock, int backlog)

// 4 設定sock的最大併發連線請求數

sk->sk_max_ack_backlog = backlog;

err = 0;

out:

release_sock(sk);

return err;

}

上面的**中,有點值得注意的是,當 sock 狀態已經是 tcp_listen 時,也可以繼續呼叫 listen() 庫函式,其作用是設定 sock 的最大併發連線請求數;

下面看看 inet_csk_listen_start() 函式:

int inet_csk_listen_start(struct sock *sk, const int nr_table_entries)

sk->sk_state = tcp_close;

__reqsk_queue_destroy(&icsk->icsk_accept_queue);

return -eaddrinuse;

}

這裡 nr_table_entries 是引數 backlog 經過最大值調整後的值;

相關資料結構

先看下接下來的**中提到了幾個資料結構,一起來看一下:

1 、 request_sock

struct request_sock ;
socket 在偵聽的時候,那些來自其它主機的 tcp socket 的連線請求一旦被接受(完成三次握手協議),便會建立乙個 request_sock ,建立與請求 socket 之間的乙個 tcp 連線。該 request_sock 會被放在乙個先進先出的佇列中,等待 accept 系統呼叫的處理;

2 、 listen_sock

struct listen_sock ;
新建立的 request_sock 就存放在 syn_table 中;這是乙個雜湊陣列,總共有 nr_table_entries 項;

成員 max_qlen_log 以 2 的對數的形式表示 request_sock 佇列的最大值;

qlen 是佇列的當前長度;

hash_rnd 是乙個隨機數,計算雜湊值用;

3 、 request_sock_queue

struct request_sock_queue ;
結構體 struct request_sock_queue 中的 rskq_accept_head 和 rskq_accept_tail 分別指向 request_sock 佇列的佇列頭和佇列尾;

等待連線佇列初始化

先看下 reqsk_queue_alloc() 的源**:

int reqsk_queue_alloc(struct request_sock_queue *queue,

unsigned int nr_table_entries)

整個過程中,先計算 request_sock 的大小並申請空間,然後初始化 request_sock_queue 的相應成員的值;

tcp_listensocket管理

在《埠管理》一文中提到管理 socket 的雜湊表結構 inet_hashinfo ,其中的成員 listening_hash[inet_lhtable_size] 用於存放處於 tcp_listen 狀態的 sock ;

當 socket 通過 listen() 呼叫完成等待連線佇列的初始化後,需要將當前 sock 放到該結構體中:

if (!sk->sk_prot->get_port(sk, inet->num))
這裡呼叫了 net/ipv4/inet_hashtables.c:inet_hash() 方法:

void inet_hash(struct sock *sk)

}static void __inet_hash(struct sock *sk)

bug_trap(sk_unhashed(sk));

// 計算hash值,取得鍊錶

list = &hashinfo->listening_hash[inet_sk_listen_hashfn(sk)];

lock = &hashinfo->lhash_lock;

inet_listen_wlock(hashinfo);

// 將sock新增到鍊錶中

__sk_add_node(sk, list);

sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);

write_unlock(lock);

wake_up(&hashinfo->lhash_wait);

}

了解到這裡,回答文初提出的 3 個問題,應該沒什麼問題了吧 :)

核心網路協議棧offload功能盤點

tso tcp segmentation offload的縮寫,只針對tcp包傳送,超過 mtu 大小的報文不需要在協議棧分段,直接offload到網絡卡,由網絡卡硬體實現分段,降低 cpu 負載。除了tso,還有乙個lso large segment offload lso的定義相對於tso更加寬...

Linux核心網路協議棧7 socket埠管理

一 前情回顧 上一節 socket 位址繫結 中提到,應用程式傳遞過來的埠在核心中需要檢查埠是否可用 if sk sk prot get port sk,snum 按照前面的例子來分析,這裡是呼叫了 tcp prot 結構變數中的 get prot 函式指標,該函式位於net ipv4 inet c...

linux核心網路協議棧 網絡卡報文收發(十六)

linux版本 3.10.103 網絡卡驅動 ixgbev 網絡卡驅動預設採用的是napi的報文處理方式。即中斷 輪詢的方式,網絡卡收到乙個報文之後會產生接收中斷,並且遮蔽中斷,直到收夠了netdev max backlog個報文 預設300 或者收完網絡卡上的所有報文之後,重新開啟中斷。網絡卡啟用...