image.png
- socket_create:創(chuàng)建一個 struct socket 結(jié)構(gòu),然后通過 sock_map_fd 和文件描述符對應(yīng)起來。
- 參數(shù):
- family:表示地址族。
- type:也即 Socket 的類型。
- protocol:是協(xié)議。
分別是 SOCK_STREAM、SOCK_DGRAM 和 SOCK_RAW。
inetsw 數(shù)組是在系統(tǒng)初始化的時候初始化的,type 作為下標,里面的內(nèi)容是 struct inet_protosw,是協(xié)議,也即 inetsw 數(shù)組對于每個類型有一項,這一項里面是屬于這個類型的協(xié)議。
因為上述結(jié)構(gòu),我們可以通過 family》type》protocol 獲取到對應(yīng)到inet_protosw 最后得到不同的 ops并賦值給socket到ops TCP的就是 inet_stream_ops。
- 參數(shù):
- bind 函數(shù):sockfd_lookup_light 會根據(jù) fd 文件描述符,找到 struct socket 結(jié)構(gòu)。然后將 sockaddr 從用戶態(tài)拷貝到內(nèi)核態(tài),然后調(diào)用 struct socket 結(jié)構(gòu)里面 ops 的 bind 函數(shù),即調(diào)用 inet_bind。bind 里面會調(diào)用 sk_prot 的 get_port 函數(shù),也即 inet_csk_get_port 來檢查端口是否沖突,是否可以綁定。如果允許,則會設(shè)置 struct inet_sock 的本方的地址 inet_saddr 和本方的端口 inet_sport,對方的地址 inet_daddr 和對方的端口 inet_dport 都初始化為 0。
- listen 函數(shù):我們還是通過 sockfd_lookup_light,根據(jù) fd 文件描述符,找到 struct socket 結(jié)構(gòu)。接著,我們調(diào)用 struct socket 結(jié)構(gòu)里面 ops 的 listen 函數(shù)也即調(diào)用 inet_listen。 這里面建立了一個新的結(jié)構(gòu) inet_connection_sock,客戶端和服務(wù)端都是有一個結(jié)構(gòu)維護連接的狀態(tài),就是指這個結(jié)構(gòu)。
在內(nèi)核中,為每個 Socket 維護兩個隊列。一個是已經(jīng)建立了連接的隊列,這時候連接三次握手已經(jīng)完畢,處于 established 狀態(tài);一個是還沒有完全建立連接的隊列,這個時候三次握手還沒完成,處于 syn_rcvd 的狀態(tài)。服務(wù)端調(diào)用 accept 函數(shù),其實是在第一個隊列中拿出一個已經(jīng)完成的連接進行處理。如果還沒有完成就阻塞等待。這里的 icsk_accept_queue 就是第一個隊列。
- accept 函數(shù):原來的 socket 是監(jiān)聽 socket,這里我們會找到原來的 struct socket,并基于它去創(chuàng)建一個新的 newsock。這才是連接 socket。除此之外,我們還會創(chuàng)建一個新的 struct file 和 fd,并關(guān)聯(lián)到 socket。
inet_csk_accept 的實現(xiàn),印證了上面我們講的兩個隊列的邏輯。如果 icsk_accept_queue 為空,則調(diào)用 inet_csk_wait_for_connect 進行等待;等待的時候,調(diào)用 schedule_timeout,讓出 CPU,并且將進程狀態(tài)設(shè)置為 TASK_INTERRUPTIBLE。如果再次 CPU 醒來,我們會接著判斷 icsk_accept_queue 是否為空,同時也會調(diào)用 signal_pending 看有沒有信號可以處理。一旦 icsk_accept_queue 不為空,就從 inet_csk_wait_for_connect 中返回,在隊列中取出一個 struct sock 對象賦值給 newsk。
image.png
- conect 函數(shù):三次握手過程:
- 通過 tcp_v4_connect函數(shù),查詢路由表,選擇從那個網(wǎng)卡出去。然后將狀態(tài)置為TCP_SYN_SEND,然后初始化TCP 的 seq num,也即 write_seq,然后調(diào)用 tcp_connect 進行發(fā)送SYN 包。
- 服務(wù)端處理函數(shù) tcp_rcv_state_process ,根據(jù)不同的狀態(tài)回應(yīng)請求,由于當前服務(wù)端在TCP_LISTEN狀態(tài),會調(diào)用 tcp_v4_send_synack,服務(wù)端將狀態(tài)改為TCP_SYN_RECV并發(fā)送SYN_ACK保存。
- 客戶端到 tcp_rcv_state_process ,由于客戶端處于 TCP_SYN_SEND,會調(diào)用 tcp_send_ack,然后將客戶端狀態(tài)改為TCP_ESTABLISHED,然后發(fā)送ACK-ACK包。
- 服務(wù)端到tcp_rcv_state_process,由于服務(wù)端處于TCP_SYN_RECV狀態(tài),將狀態(tài)轉(zhuǎn)為TCP_ESTABLISHED,然后就可以將這個連接放到就緒隊列然后接收請求。

