linux的Listen呼叫詳解
listen系統可以使一臺主機上的一個tcp socket在某個埠號被動偵聽,等待來自其它主機的tcp socket的連線請求,下面是listen系統呼叫的函式原型:
#include int listen(int s, int backlog);
1)backlog是偵聽佇列的長度,在核心函式中,首先對backlog作檢查,如果大於128,則強制使其等於128。
2)接下來要檢查結構體struct sock的成員sk_state,即當前socket的狀態,如果不為TCP_LISTEN,則開始啟動埠偵聽。
啟動埠偵聽首先要為結構體struct inet_connection_sock(它是struc sock的擴充套件,表示一個面向連線的socket)的成員icsk_accept_queue分配記憶體,icsk_accept_queue的型別是struct request_sock_queue,
定義如下:
struct request_sock_queue
{
struct request_sock *rskq_accept_head;
struct request_sock *rskq_accept_tail;
rwlock_t syn_wait_lock;
u8 rskq_defer_accept;
struct listen_sock *listen_opt;
};
tcp socket在偵聽的時候,那些來自其它主機的tcp socket的連線請求一旦被接受(完成三次握手協議),便會建立一個request_sock,建立與請求socket之間的一個tcp連線。該request_sock會被放在一個先進先出的佇列中,等待accept系統呼叫的處理。但上面的結構體中好像並沒有可以存放request_sock的地方,
下面是結構體struct listen_sock的定義:
struct listen_sock
{
u8 max_qlen_log;
int qlen;
int qlen_young;
int clock_hand;
u32 hash_rnd;
u32 nr_table_entries;
struct request_sock *syn_table[0];
};
新建立的request_sock就存放在syn_table中。這是一個雜湊陣列,總共有nr_table_entries項。實際上在分配記憶體時,分配的大小是TCP_SYNQ_HSIZE(512)項。成員nr_table_entries的值是512。成員max_qlen_log以2的對數的形式表示request_sock佇列的最大值。雜湊表有512項,但佇列的最大值的取值是1024。即max_qlen_log的值為10。qlen是佇列的當前長度。hash_rnd是一個隨機數,計算雜湊值用,結構體struct request_sock_queue中的rskq_accept_head和rskq_accept_tail分別指向request_sock佇列的佇列頭和佇列尾。 為struct inet_connection_sock分配完記憶體後,繼續處理,結構體struct sock有兩個成員sk_ack_backlog和sk_max_ack_backlog。sk_ack_backlog表示該偵聽socket上,當前連向該socket,但是還沒有完成三次握手協議的socket的數量,即還在連線過程中的socket的數量。初始值為0,sk_max_ack_backlog為該數量的最大值,也就是listen系統呼叫的第二個引數,即偵聽佇列的長度,它的真正含義是:偵聽socket能處理的最大併發連線請求數,其最大取值為128。
到這裡,把socket的狀態改為TCP_LISTEN,進入偵聽狀態。然後獨佔埠,使socket進入mytcp_hashinfo雜湊表集中的listening_hash表。偵聽建立完成。 由於偵聽socket始終在系統中進行偵聽工作,所以在程序結束時,還必須顯式結束偵聽,進行相應的清理工作。