linuxカーネル中のtcp接続の切断処理


今回は主に関連する二つの切断関数クローズドとshotdownと関連するセットインターフェースオプションSO_を分析します.LINGERここはSO_に注意しますLINGERはshutdownに対して何の影響もありません.それはcloseだけに役立ちます.
まずカンソウに来てくださいLINGERに対応するデータ構造:
struct linger {
///linger   
	int		l_onoff;	/* Linger active		*/
///      。
	int		l_linger;	/* How long to linger for	*/
};
ここではこのインターフェースのオプションについて詳しく紹介しません.unixネットワークプログラミングに詳しい紹介があります.ここではカーネルの処理コードだけを分析します.
まず、close関数を見にきてください.私たちは目が覚めていない場合、closeはすぐに戻りますが、セットインターフェースの送信バッファエリアにまだ未送信のデータがあれば、システムはこれらのデータを相手に送信してみます.この目が覚めない状況はSO_を通してできます.LINGERが変わる.もう一つの注意点は、closeの呼び出しは、必ずしもtcpの切断接続を引き起こすとは限らないことです.closeはこのsocketの引用数を一つ減らします.本当に切断を直接誘発するにはshutdown関数が必要です.
カーネルの中でsocketのcloseのシステム呼び出しはsock_です.close、そしてsock_closeでは、直接sock_を呼び出します.releaseは機能を実現しますので、ここで直接sock_を見ます.releaseのソースコード:


void sock_release(struct socket *sock)
{
	if (sock->ops) {
		struct module *owner = sock->ops->owner;

///  inet_stream_ops inet_release  
		sock->ops->release(sock);
/// ops  。
		sock->ops = NULL;
		module_put(owner);
	}

///      26.31   ,          。
	if (sock->fasync_list)
		printk(KERN_ERR "sock_release: fasync list not empty!
"); /// socket percpu_sub(sockets_in_use, 1); if (!sock->file) { /// inode iput(SOCK_INODE(sock)); return; } sock->file = NULL; }
そしてinet_を見に来ましたreleaseの実現には、この関数は主にSO_を通じて使用されます.LINGERソケットはタイムアウト時間を得て、tcp_を呼び出します.closeはsockをオフにします.
int inet_release(struct socket *sock)
{
	struct sock *sk = sock->sk;

	if (sk) {
		long timeout;

		/* Applications forget to leave groups before exiting */
		ip_mc_drop_socket(sk);

		timeout = 0;
///      SO_LINGER        shutdowning,   timeout l_linger(         ).
		if (sock_flag(sk, SOCK_LINGER) &&
		    !(current->flags & PF_EXITING))
			timeout = sk->sk_lingertime;
		sock->sk = NULL;
///  tcp_close.
		sk->sk_prot->close(sk, timeout);
	}
	return 0;
}
tcp_close関数が長いので、ここではセグメント化して分析します.まず第一部分を見にきます.ここで注意すべき点は何ですか?
1 closeがサービスの父socketを落とした時、カーネルは先に半分の接続列を処理して、それからすでにacceptの行列になって、最後にやっと父sockを処理します.
2バッファのデータを受信する場合は、直接にドライブを巡回します.queue(前のブログで紹介されています)そして未送信のsocketを統計します.私たちはcloseがbufを受信するかどうか、つまり彼は受信bufをリリースして、RStを相手に送ることを知っています.
3当so_lingerが設定されていて、タイムアウト時間が0ならば、rstを相手に送信し、バグの送信と受信をクリアします.これは最終的な4つのパケット終了シーケンスを引き起こすこともない.
4受信バッファに未読データがある場合は、RStを直接ペアに送信する.これは最終的な4つのパケット終了シーケンスを引き起こすこともない.
5当so_lingerに設定があり、タイムアウトは0ではないか、またはso_lingerが設定されていない場合は、最終的な4つのパケットがシーケンスを終了して接続を終了します.(send finを介してfinを送信し、4つのパケットの終端シーケンスを誘発する)一方、send_finでは、送信バッファのデータが送信されます.
ビューコード:
void tcp_close(struct sock *sk, long timeout)
{
	struct sk_buff *skb;
	int data_was_unread = 0;
	int state;

	lock_sock(sk);
	sk->sk_shutdown = SHUTDOWN_MASK;

///    tcp_listen         socket        socket。
	if (sk->sk_state == TCP_LISTEN) {
///  sock  .
		tcp_set_state(sk, TCP_CLOSE);

///               (           )
		/* Special case. */
		inet_csk_listen_stop(sk);
///      sock
		goto adjudge_to_death;
	}

///  sk_receive_queue     buf  。            。
	while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {
		u32 len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq -
			  tcp_hdr(skb)->fin;
		data_was_unread += len;
///free  skb
		__kfree_skb(skb);
	}

	sk_mem_reclaim(sk);


///   if      rfc2525 2.17,        ,    buf      ,     rst   。(         )
	if (data_was_unread) {
		/* Unread data was tossed, zap the connection. */
		NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
///    
		tcp_set_state(sk, TCP_CLOSE);
///  rst
		tcp_send_active_reset(sk, GFP_KERNEL);
	} 
///   if     so_linger   ,       0。                     
else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
		/* Check zero linger _after_ checking for unread data. */
///  tcp_disconnect,                ,         。
		sk->sk_prot->disconnect(sk, 0);
		NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONDATA);
	} 
///                fin,       。            。
else if (tcp_close_state(sk)) {

///  fin.
		tcp_send_fin(sk);
	}

///      。   timeout,     so_linger    l_linger.             buf  (        0).
	sk_stream_wait_close(sk, timeout);
........................

}
rfc 2525の2.17の紹介:
When an application closes a connection in such a way that it can no longer read any received data, the TCP SHOULD, per section 4.2.2.13 of RFC 1122, send a RST if there is any unread received data, or if any new data is received. A TCP that fails to do so exhibits "Failure to RST on close with data pending".
ok,上で出会った3つの関数を見に来ました.一つはinet_です.csk_listen_stop、一つはtcp_ですクローズドstate、一つはtcp_disconnect.私たちは皆彼らを見に来ます.
まずはinet_ですcsk_listen_stop関数この関数は主に全半接続キューを整理するために使われることを知っています.
void inet_csk_listen_stop(struct sock *sk)
{
	struct inet_connection_sock *icsk = inet_csk(sk);
	struct request_sock *acc_req;
	struct request_sock *req;

///    keepalive   。
	inet_csk_delete_keepalive_timer(sk);

	/* make all the listen_opt local to us */
///  accept   。
	acc_req = reqsk_queue_yank_acceptq(&icsk->icsk_accept_queue);

///             ,   listen_sock  
	reqsk_queue_destroy(&icsk->icsk_accept_queue);


///  accept          。
	while ((req = acc_req) != NULL) {
...............................................

///  tcp_disconnect         。         。
		sk->sk_prot->disconnect(child, O_NONBLOCK);

		sock_orphan(child);

		percpu_counter_inc(sk->sk_prot->orphan_count);

///    sock。
		inet_csk_destroy_sock(child);

........................................
	}
	WARN_ON(sk->sk_ack_backlog);
}
次にtcp_を見にきますdisconnect関数この関数は主にオフとオフの接続に使われます.書き込み待ち行列、送信rst、タイマーなど一連の操作を解除します.

int tcp_disconnect(struct sock *sk, int flags)
{
	struct inet_sock *inet = inet_sk(sk);
	struct inet_connection_sock *icsk = inet_csk(sk);
	struct tcp_sock *tp = tcp_sk(sk);
	int err = 0;
	int old_state = sk->sk_state;

	if (old_state != TCP_CLOSE)
		tcp_set_state(sk, TCP_CLOSE);
...................

///     ,  ,delack 。
	tcp_clear_xmit_timers(sk);
///  free   buf。
	__skb_queue_purge(&sk->sk_receive_queue);
///free  buf。
	tcp_write_queue_purge(sk);
	__skb_queue_purge(&tp->out_of_order_queue);
#ifdef CONFIG_NET_DMA
	__skb_queue_purge(&sk->sk_async_wait_queue);
#endif

	inet->dport = 0;

	if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK))
		inet_reset_saddr(sk);
..........................................
///    。
	tcp_set_ca_state(sk, TCP_CA_Open);
///          
	tcp_clear_retrans(tp);
	inet_csk_delack_init(sk);
	tcp_init_send_head(sk);
	memset(&tp->rx_opt, 0, sizeof(tp->rx_opt));
	__sk_dst_reset(sk);

	WARN_ON(inet->num && !icsk->icsk_bind_hash);

	sk->sk_error_report(sk);
	return err;
}
続いてtcp_ですクローズドstate関数という関数は、finを送るべきかどうかを判断するために使用されます.


///        close ,tcp     ,         ,   3  。       current   tcp     new state   close   ,    TCP_ACTION_FIN   ,  action
static const unsigned char new_state[16] = {
  /* current state:        new state:      action:	*/
  /* (Invalid)		*/ TCP_CLOSE,
  /* TCP_ESTABLISHED	*/ TCP_FIN_WAIT1 | TCP_ACTION_FIN,
  /* TCP_SYN_SENT	*/ TCP_CLOSE,
  /* TCP_SYN_RECV	*/ TCP_FIN_WAIT1 | TCP_ACTION_FIN,
  /* TCP_FIN_WAIT1	*/ TCP_FIN_WAIT1,
  /* TCP_FIN_WAIT2	*/ TCP_FIN_WAIT2,
  /* TCP_TIME_WAIT	*/ TCP_CLOSE,
  /* TCP_CLOSE		*/ TCP_CLOSE,
  /* TCP_CLOSE_WAIT	*/ TCP_LAST_ACK  | TCP_ACTION_FIN,
  /* TCP_LAST_ACK	*/ TCP_LAST_ACK,
  /* TCP_LISTEN		*/ TCP_CLOSE,
  /* TCP_CLOSING	*/ TCP_CLOSING,
};

static int tcp_close_state(struct sock *sk)
{
///  new state
	int next = (int)new_state[sk->sk_state];
	int ns = next & TCP_STATE_MASK;

	tcp_set_state(sk, ns);

///  action
	return next & TCP_ACTION_FIN;
}
次にtcp_を見にきますcloseの残りの部分のコードです.残りの部分はいくつかの状態を処理することとここに通知することです.注意すべきのはTCP_です.LINGER 2というソケットは、fin待ちのタイムアウト時間、つまりtcp_を設定できます.sockのドメインlinger 2.システムにもう一つのsyssctl_があると知っています.tcp_fintimeoutとは、sysファイルシステムのインターフェースを提供してこの値を修正しますが、linger 2を0より大きい値に設定すると、カーネルはlinger 2の値を取ります.


adjudge_to_death:

///  sock   。
	state = sk->sk_state;
	sock_hold(sk);
	sock_orphan(sk);

///       sock   (           )
	release_sock(sk);

	local_bh_disable();
	bh_lock_sock(sk);
	WARN_ON(sock_owned_by_user(sk));

///   cpu        。
	percpu_counter_inc(sk->sk_prot->orphan_count);

	/* Have we already been destroyed by a softirq or backlog? */
	if (state != TCP_CLOSE && sk->sk_state == TCP_CLOSE)
		goto out;

///     TCP_FIN_WAIT2,     ack,      fin。
	if (sk->sk_state == TCP_FIN_WAIT2) {
		struct tcp_sock *tp = tcp_sk(sk);
///      0,       ,     tcp_close,    rst   。
		if (tp->linger2 < 0) {
			tcp_set_state(sk, TCP_CLOSE);
			tcp_send_active_reset(sk, GFP_ATOMIC);
			NET_INC_STATS_BH(sock_net(sk),
					LINUX_MIB_TCPABORTONLINGER);
		} else {
///    fin     。        linger2 sysctl_tcp_fin_timeout    。
			const int tmo = tcp_fin_time(sk);
///        ,   keepalive        。
			if (tmo > TCP_TIMEWAIT_LEN) {
				inet_csk_reset_keepalive_timer(sk,
						tmo - TCP_TIMEWAIT_LEN);
			} else {
///    time_wait  。
				tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);
				goto out;
			}
		}
	}
......................................

///  sk    tcp_close destroy   sk
	if (sk->sk_state == TCP_CLOSE)
		inet_csk_destroy_sock(sk);
	/* Otherwise, socket is reprieved until protocol close. */

out:
	bh_unlock_sock(sk);
	local_bh_enable();
	sock_put(sk);
}
そしてsend_を見に来ますfinの実装では、この関数は一つのfinを送信するために使用され、送信バッファ内のデータをできるだけ送信します.

void tcp_send_fin(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
///   bufer   。
	struct sk_buff *skb = tcp_write_queue_tail(sk);
	int mss_now;

	/* Optimization, tack on the FIN if we have a queue of
	 * unsent frames.  But be careful about outgoing SACKS
	 * and IP options.
	 */
	mss_now = tcp_current_mss(sk);
///         ,         sk buffer    (   tcp       fin),          buffer   ,          buffer        )
	if (tcp_send_head(sk) != NULL) {
		TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_FIN;
		TCP_SKB_CB(skb)->end_seq++;
		tp->write_seq++;
	} else {
..................................
///            ,          sk buffer,       ,     buffer。
		skb_reserve(skb, MAX_TCP_HEADER);
		/* FIN eats a sequence byte, write_seq advanced by tcp_queue_skb(). */
		tcp_init_nondata_skb(skb, tp->write_seq,
				     TCPCB_FLAG_ACK | TCPCB_FLAG_FIN);
		tcp_queue_skb(sk, skb);
	}
///          。
	__tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_OFF);
}
void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
			       int nonagle)
{
	struct sk_buff *skb = tcp_send_head(sk);

	if (!skb)
		return;

	/* If we are closed, the bytes will have to remain here.
	 * In time closedown will finish, we empty the write queue and
	 * all will be happy.
	 */
	if (unlikely(sk->sk_state == TCP_CLOSE))
		return;
///    ,     nagle。              (    blog         ).
	if (tcp_write_xmit(sk, cur_mss, nonagle, 0, GFP_ATOMIC))
		tcp_check_probe_timer(sk);
}
次にshutdownの実現を見にきます.2.26.31では、システムコールの実装が若干変化しています.
ここではshutdownはバッファのデータを書き込みます.そして、ブロックのプロセスを起動して、バッファのデータを読みます.
このシステムの呼び出しに対応するカーネル関数がオウです.shutdown_socket


#define SHUT_RD 0
#define SHUT_WR 1
#define SHUT_RDWR 2

int os_shutdown_socket(int fd, int r, int w)
{
	int what, err;

	if (r && w)
		what = SHUT_RDWR;
	else if (r)
		what = SHUT_RD;
	else if (w)
		what = SHUT_WR;
	else
		return -EINVAL;

///  socket shutdown   kernel_sock_shutdown
	err = shutdown(fd, what);
	if (err < 0)
		return -errno;
	return 0;
}


int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how)
{
///      inet_shutdown
	return sock->ops->shutdown(sock, how);
}
ニートを見に来ましたshutdownの実装.この関数の主な仕事はsockの状態が異なると判断することによって関連関数を呼び出すことです.
int inet_shutdown(struct socket *sock, int how)
{
	struct sock *sk = sock->sk;
	int err = 0;

	/* This should really check to make sure
	 * the socket is a TCP socket. (WHY AC...)
	 */
///       how   1 ,           1,2,3
	how++; /* maps 0->1 has the advantage of making bit 1 rcvs and
		       1->2 bit 2 snds.
		       2->3 */
///  how    。
	if ((how & ~SHUTDOWN_MASK) || !how)	/* MAXINT->0 */
		return -EINVAL;
///  sock
	lock_sock(sk);

///SS_CONNECTING    sock        。state   socket       
	if (sock->state == SS_CONNECTING) {
///          ,            ,       SS_DISCONNECTING
		if ((1 << sk->sk_state) &
		    (TCPF_SYN_SENT | TCPF_SYN_RECV | TCPF_CLOSE))
			sock->state = SS_DISCONNECTING;
		else
///         
			sock->state = SS_CONNECTED;
	}

///  TCP_LISTEN  TCP_SYN_SENT              sk->sk_prot->shutdown   tcp_shutdown  。

	switch (sk->sk_state) {
///     tco_close      ,    default  
	case TCP_CLOSE:
		err = -ENOTCONN;
		/* Hack to wake up other listeners, who can poll for
		   POLLHUP, even on eg. unconnected UDP sockets -- RR */
	default:
		sk->sk_shutdown |= how;
		if (sk->sk_prot->shutdown)
			sk->sk_prot->shutdown(sk, how);
		break;

	/* Remaining two branches are temporary solution for missing
	 * close() in multithreaded environment. It is _not_ a good idea,
	 * but we have no choice until close() is repaired at VFS level.
	 */
	case TCP_LISTEN:
///    SHUT_RD   switch,    tcp_syn_sent   。
		if (!(how & RCV_SHUTDOWN))
			break;
		/* Fall through */
	case TCP_SYN_SENT:
///    ,    state
		err = sk->sk_prot->disconnect(sk, O_NONBLOCK);
		sock->state = err ? SS_DISCONNECTING : SS_UNCONNECTED;
		break;
	}

	/* Wake up anyone sleeping in poll. */
///       socket    ,                 。
	sk->sk_state_change(sk);
	release_sock(sk);
	return err;
}
tcp_を見に来ましたshutdown関数です
ここで注意したいのですが、読みをオフにするだけでは、送信finは発生しません.つまりマークだけを設定してデータを読み込むときにエラーを返します.書き込み端を閉じると、finが送信されます.
void tcp_shutdown(struct sock *sk, int how)
{
	/*	We need to grab some memory, and put together a FIN,
	 *	and then put it into the queue to be sent.
	 *		Tim MacKenzie([email protected]) 4 Dec '92.
	 */
///   SHUT_RD     。
	if (!(how & SEND_SHUTDOWN))
		return;

	/* If we've already sent a FIN, or it's a closed state, skip this. */
///                。
	if ((1 << sk->sk_state) &
	    (TCPF_ESTABLISHED | TCPF_SYN_SENT |
	     TCPF_SYN_RECV | TCPF_CLOSE_WAIT)) {
		/* Clear out any half completed packets.  FIN if needed. */
/// tcp_close      
		if (tcp_close_state(sk))
			tcp_send_fin(sk);
	}
}
最後にsock_に来てくださいdef_readableそれはsk->sk_です.state_changeつまり渋滞を呼び覚まします.
static void sock_def_readable(struct sock *sk, int len)
{
	read_lock(&sk->sk_callback_lock);
///            sk
	if (sk_has_sleeper(sk))
///   ,    ,             POLLIN,      。
	wake_up_interruptible_sync_poll(sk->sk_sleep, POLLIN |
						POLLRDNORM | POLLRDBAND);

///      ,        POLL_IN.
	sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
	read_unlock(&sk->sk_callback_lock);
}
shutdown関数はSEND_しか処理できません.SHUTDOWN.また、shutdownを呼び出した後、バッファエリアを読み続けても良いです.