天天看點

linux tcp shutdown,LinuxTCP shutdown和close系統調用

作者:henrystark

Blog: http://henrystark.blog.chinaunix.net/

日期:20140419

本文遵循CC協定:署名-非商業性使用-禁止演繹 2.5()。可以自由拷貝,轉載。但轉載請保持文檔的完整性,注明原作者及原連結。如有錯訛,煩請指出。

LinuxTCP shutdown和close系統調用

0.寫作目的

面試時被問及系統調用如何實作,這個問題不好說。往深處說,牽涉到NR……等中斷向量的實作【引 3】;往淺了說,就是系統提供的接口在核心代碼如何實作。我最開始說了printf和write系統調用的關系,說到一半接不下去了【注 1】。于是該說shutdown和close兩個系統調用。

1.引言

socket網絡程式設計中,常用這兩個系統調用,最主要的差別是:shutdown強制關閉套接字,close隻将引用計數減一。

2.功能和代碼

2.1 shutdown

準确的定義見【引 1】。該函數有三種關閉方式:單獨關閉讀(寫)、同時關閉讀寫。shutdown處理過程調用序列見【引 2】。shutdown不管引用計數,會直接關閉套接口。源碼如下:

linux/net/ipv4/tcp.c

void tcp_shutdown(struct sock *sk, int how)

{

if (!(how & SEND_SHUTDOWN))

return;

if ((1 << sk->sk_state) &

(TCPF_ESTABLISHED | TCPF_SYN_SENT |

TCPF_SYN_RECV | TCPF_CLOSE_WAIT)) {

if (tcp_close_state(sk))

tcp_send_fin(sk);

}

}

從注釋中可以看到,這個函數主要負責關閉套接口的讀端。注意,這裡為了處理用位與的方式來判斷是否是關閉讀端,how變量已經經過了處理,見shutdown系統調用在套接口層的實作inet_shutdown。

linux/net/ipv4/af_inet.c

int inet_shutdown(struct socket *sock, int how)

{

struct sock *sk = sock->sk;

int err = 0;

how++;

if ((how & ~SHUTDOWN_MASK) || !how)

return -EINVAL;

………………………………………………………………………………………………………………………………………………………………………………

linux/include/net/sock.h

#define SHUTDOWN_MASK 3

#define RCV_SHUTDOWN 1

#define SEND_SHUTDOWN 2

問題是,讀端怎麼關閉?實際上,shutdown導緻程序丢棄沒有讀取的或者後續到達的資料。這會在其他tcp接收函數中做處理,如tcp_poll、tcp_recvmsg等。

2.2 close

close系統調用的減引用計數操作主要由release函數完成,該函數最後調用close函數處理資料并發送fin。

linux/net/ipv4/af_inet.c

int inet_release(struct socket *sock)

{

struct sock *sk = sock->sk;

if (sk) {

long timeout;

//以下兩個函數實作引用計數-1

sock_rps_reset_flow(sk);

ip_mc_drop_socket(sk);

timeout = 0;

if (sock_flag(sk, SOCK_LINGER) &&

!(current->flags & PF_EXITING))

timeout = sk->sk_lingertime;

sock->sk = NULL;

sk->sk_prot->close(sk, timeout); //這裡調用tcp_close()

}

return 0;

}

linux/net/ipv4/tcp.c

void tcp_close(struct sock *sk, long timeout)

{

……………………………………………………………………………………………………………………………………………………

if (data_was_unread) {

NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);

tcp_set_state(sk, TCP_CLOSE);

tcp_send_active_reset(sk, sk->sk_allocation);

} else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {

sk->sk_prot->disconnect(sk, 0);

NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONDATA);

} else if (tcp_close_state(sk)) {

tcp_send_fin(sk); //這裡發送fin

}

sk_stream_wait_close(sk, timeout);

adjudge_to_death:

state = sk->sk_state;

sock_hold(sk);

sock_orphan(sk);

release_sock(sk);

……………………………………………………………………………………………………………………………………………………………………………………………………

}

可以看到,shutdown和close兩個系統調用最後都使用了send_fin函數來終止連接配接。

3.系統調用的實作機制

【引 3】中有系統調用的詳細實作機制。在核心中定義系統調用編号,應用程式用軟中斷通知系統切換到核心态,傳遞參數。

注解:

【1】printf是庫函數,write是系統調用,關于系統調用和庫函數的差別,也很複雜,【引 3】中講了一部分,關于printf的實作細節參見http://blog.csdn.net/dog250/article/details/23000909。