日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術(shù)文章
文章詳情頁

詳解linux里的backlog參數(shù)

瀏覽:387日期:2023-03-07 14:40:03

問題

我們在linux上服務(wù)器起了一個serversocket,并且設(shè)置了backlog為2,并沒有讓serversock.accept()

在客戶端上,我們一個一個的啟動了連接socket, 當(dāng)連接數(shù)目超過3的時候,客戶端依然可以繼續(xù)新建連接。

什么是backlog

說起backlog, 都會想起socket編程中的listen backlog 參數(shù),而這個backlog 是linux內(nèi)核中處理的backlog么?

int listen(int sockfd, int backlog)

listen 中的backlog解釋

The backlog argument defines the maximum length to which the queue of pending connections for sockfd may grow. If a connection request arrives when the queue is full, the client may receive an error with an indication of ECONNREFUSED or, if the underlying protocol supports retransmission, the request may be ignored so that a later reattempt at connection succeeds.

實際上在linux內(nèi)核2.2版本以后,backlog參數(shù)控制的是已經(jīng)握手成功的還在accept queue的大小。

握手過程中的結(jié)構(gòu)體

struct request_sock_queue {
/*Points to the request_sock accept queue, when after 3 handshake will add the request_sock from syn_table to here*/
    struct request_sock    *rskq_accept_head;
    struct request_sock    *rskq_accept_tail;
    rwlock_tsyn_wait_lock;
    u8    rskq_defer_accept;
    /* 3 bytes hole, try to pack */
    struct listen_sock    *listen_opt;
};
struct listen_sock {
    u8    max_qlen_log; /*2^max_qlen_log is the length of the accpet queue, max of max_qlen_log is 10. (2^10=1024)*/
    /* 3 bytes hole, try to use */
    int    qlen; /* qlen is the current length of the accpet queue*/
    int    qlen_young;
    int    clock_hand;
    u32    hash_rnd;
    u32    nr_table_entries; /*nr_table_entries is the number of the syn_table,max is 512*/
    struct request_sock    *syn_table[0];
};
struct request_sock {
	struct request_sock		*dl_next; /* Must be first member! */
	u16				mss;
	u8				retrans;
	u8				cookie_ts; /* syncookie: encode tcpopts in timestamp */
	/* The following two fields can be easily recomputed I think -AK */
	u32				window_clamp; /* window clamp at creation time */
	u32				rcv_wnd;	  /* rcv_wnd offered first time */
	u32				ts_recent;
	unsigned long			expires;
	const struct request_sock_ops	*rsk_ops;
	struct sock			*sk;
	u32				secid;
	u32				peer_secid;
};
struct sock{
	unsigned short		sk_ack_backlog;
	unsigned short		sk_max_ack_backlog;
}

首先在linux里可以簡單的認(rèn)為有2個隊列,一個就是在握手過程中的隊列,而另一個就是握手成功的隊列

簡單的描述一下3個結(jié)構(gòu)體

request_sock

 是每一個client的連接(無論是握手成功,還是不成功) 里面的 expires代表的是這個request在隊列里的存活時間,而 *sk 就是連接成功的socket的數(shù)目

request_sock_queue

rskq_accept_head 隊列,也就是握手成功的隊列,*listen_opt 是指listen過程中的sock

listen_sock

*syn_table 是指握手沒有成功的隊列,而qlen,qlen_young 分別指的是隊列的長度和隊列新成員的個數(shù)

在結(jié)構(gòu)體中,我們已經(jīng)清楚的看到了一個listen_sock中的syn_table,另一個是request_sock_queue中的rskq_accept_head,這就是我們剛才說的兩個隊列,一個是為正在握手的隊列,另一個是已經(jīng)握手成功的隊列。

我們在上面都看到了結(jié)構(gòu)體中只是看到了未握手的隊列的長度,并沒有看到握手的隊列長度統(tǒng)計,實際上握手成功的隊列長度是在sock 結(jié)構(gòu)中

sock

當(dāng)握手成功后每一個client就是一個sock, sk_ack_backlog 是隊列長度,而sk_max_ack_backlog是指最大的隊列長度

在這里我們會有疑問,難道是沒個連接上的 sock都會保留隊列的長度么?實際上在此時的sock 代表的是server端listen 的sock而不是客戶端的sock,也就是在握手沒有成功的過程中,在linux使用的sock都是server的listen的sock, 對客戶端只是保留成request_sock

TCP握手的幾個階段

收到客戶端的syn請求 ->將這個請求放入syn_table中去->服務(wù)器端回復(fù)syn-ack->收到客戶端的ack->放入accept queue中

我們把整個過程分為5個部分,其中將請求放入syn_table和accept queue中的過程也是backlog相關(guān)的,在下面我們會詳細(xì)闡述。

我們先簡單的描述一下幾個tcp的操作函數(shù),下面針對的也是ip4協(xié)議的

const struct inet_connection_sock_af_ops ipv4_specific = {
	.queue_xmit	   = ip_queue_xmit,
	.send_check	   = tcp_v4_send_check,
	.rebuild_header	   = inet_sk_rebuild_header,
	.conn_request	   = tcp_v4_conn_request,
	.syn_recv_sock	   = tcp_v4_syn_recv_sock,
	.remember_stamp	   = tcp_v4_remember_stamp,
	.net_header_len	   = sizeof(struct iphdr),
	.setsockopt	   = ip_setsockopt,
	.getsockopt	   = ip_getsockopt,
	.addr2sockaddr	   = inet_csk_addr2sockaddr,
	.sockaddr_len	   = sizeof(struct sockaddr_in),
	.bind_conflict	   = inet_csk_bind_conflict,
#ifdef CONFIG_COMPAT
	.compat_setsockopt = compat_ip_setsockopt,
	.compat_getsockopt = compat_ip_getsockopt,
#endif
};

在剛才所說的兩個步驟,也就是結(jié)構(gòu)體中的 conn_request 和 syn_recv_sock,  所對應(yīng)的函數(shù)是 tcp_v4_conn_request 和 tcp_v4_syn_recv_sock

我們所重點關(guān)注的主要是方法中的drop邏輯

tcp_v4_conn_request 函數(shù)

int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
{
	/* Never answer to SYNs send to broadcast or multicast */
	if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
		goto drop;

	/* TW buckets are converted to open requests without
	 * limitations, they conserve resources and peer is
	 * evidently real one.
	 */
	if (inet_csk_reqsk_queue_is_full(sk) && !isn) {
#ifdef CONFIG_SYN_COOKIES
		if (sysctl_tcp_syncookies) {
			want_cookie = 1;
		} else
#endif
		goto drop;
	}

	/* Accept backlog is full. If we have already queued enough
	 * of warm entries in syn queue, drop request. It is better than
	 * clogging syn queue with openreqs with exponentially increasing
	 * timeout.
	 */
	if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
		goto drop;
....
}
1. inet_csk_reqsk_queue_is_full(sk)

判斷的是  queue->listen_opt->qlen >> queue->listen_opt->max_qlen_log;

這里有個 qlen 代表的是listen_opt的 syn_table的長度,那什么是max_qlen_log呢?

nr_table_entries = min_t(u32, nr_table_entries, sysctl_max_syn_backlog);
nr_table_entries = max_t(u32, nr_table_entries, 8);
nr_table_entries = roundup_pow_of_two(nr_table_entries + 1);
for (lopt->max_qlen_log = 3;
	     (1 << lopt->max_qlen_log) < nr_table_entries;
	     lopt->max_qlen_log++);

也就是max_qlen 是listen 傳入的backlog和sysctl_max_syn_backlog最小值,并且一定大于16 , roudup_pow_of_two 代表著找最靠近nr_table_entries+1的2的倍數(shù) sysctl_max_syn_backlog 就是我們熟悉的

/proc/sys/net/ipv4/tcp_max_syn_backlog

我們看一下listen 函數(shù)在kernel的實現(xiàn)

SYSCALL_DEFINE2(listen, int, fd, int, backlog)
{
	struct socket *sock;
	int err, fput_needed;
	int somaxconn;
 
	sock = sockfd_lookup_light(fd, &err, &fput_needed);
	if (sock) {
		<span>somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;
		if ((unsigned)backlog > somaxconn)
			backlog = somaxconn;</span>
 
		err = security_socket_listen(sock, backlog);
		if (!err)
			err = sock->ops->listen(sock, backlog);
 
		fput_light(sock->file, fput_needed);
	}
	return err;
}

我們清楚的看到backlog 并不是按照你調(diào)用listen的所設(shè)置的backlog大小,實際上取的是backlog和somaxconn的最小值

somaxconn的值定義在

/proc/sys/net/core/somaxconn

2.sk_acceptq_is_full

static inline int sk_acceptq_is_full(struct sock *sk)
{
	return sk->sk_ack_backlog > sk->sk_max_ack_backlog;
}
int inet_listen(struct socket *sock, int backlog)
{
    sk->sk_max_ack_backlog = backlog;
}

就是等于我們剛才在前面部分看到的listen中的值

3.inet_csk_reqsk_queue_young

在判斷sk_acceptq_is_full 的情況下,同是也要求了判斷inet_csk_reqsk_queue_young>1,也就是剛才的結(jié)構(gòu)體listen_sock的qlen_young

qlen_young 是對syn_table的計數(shù),進(jìn)入 syn_table 加1,出了syn_table  -1

有的人可能會有疑問了

如果accept queue滿了,那么qlen_young不就是一直增加,而新來的客戶端都會被條件if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) 而drop syn的ack包,那么客戶端會出現(xiàn)connected timeout, 而實際上你在測試linux的環(huán)境中會發(fā)現(xiàn)并沒有出現(xiàn)這樣的情況。

實際上linux在server起socket的時候會調(diào)用tcp_keepalive_timer啟動tcp_synack_timer,會調(diào)用函數(shù)inet_csk_reqsk_queue_prune

 if (sk->sk_state == TCP_LISTEN) {
		tcp_synack_timer(sk);
		goto out;
}
static void tcp_synack_timer(struct sock *sk)
{
	inet_csk_reqsk_queue_prune(sk, TCP_SYNQ_INTERVAL,
				   TCP_TIMEOUT_INIT, TCP_RTO_MAX);
}

而inet_csk_reqsk_queue_prune會在去檢查syn的table, 而刪除一些這個request 過期后并且完成retry 的syn ack包的請求

為了提高inet_csk_reqsk_queue_prune的效率,在request_sock 里加入了 expires(才前面的結(jié)構(gòu)體中已經(jīng)提到過) , 這個expires初始值是hardcode的3HZ 時間, inet_csk_reqsk_queue_prune會輪訓(xùn)syn_table里的已經(jīng)exprie request, 發(fā)現(xiàn)如果還沒有到到retry的次數(shù),那么會增加expire的時間直到重試結(jié)束,而expire的時間為剩余retry 次數(shù)*3HZ ,并且不大于120HZ

關(guān)于retry, retry的參數(shù)可以通過設(shè)置 

/proc/sys/net/ipv4/tcp_syn_retries

當(dāng)然你可以通過設(shè)置

/proc/sys/net/ipv4/tcp_abort_on_overflow 為1 不允許syn ack 重試

因為被inet_csk_reqsk_queue_prune函數(shù)清除了syn_table,在沒有并發(fā)的前提下基本上不會出現(xiàn)inet_csk_reqsk_queue_young>1的情況,也就是說不會出現(xiàn)drop sync的情況,在客戶端表現(xiàn),不會出現(xiàn)connect timeout 的情況(這里的實現(xiàn)linux和mac的實現(xiàn)有很大的不同)而剛開始的問題也能得到合理的解釋了

通過函數(shù)tcp_v4_conn_request的分析,在linux的設(shè)計初衷是盡力的允許新的連接握手,而期望服務(wù)器端能更快的響應(yīng)accept.

我們也許會問,剛才的服務(wù)器syn ack回去后,如果客戶端也回復(fù)了ack的話,而此時accept的queue滿了,將會如何處理

我們回到前面提到的步驟,處理客戶端的ack 函數(shù)也就是

tcp_v4_syn_recv_sock 函數(shù)

struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
				  struct request_sock *req,
				  struct dst_entry *dst)
{
	struct inet_request_sock *ireq;
	struct inet_sock *newinet;
	struct tcp_sock *newtp;
	struct sock *newsk;
#ifdef CONFIG_TCP_MD5SIG
	struct tcp_md5sig_key *key;
#endif
 
	if (sk_acceptq_is_full(sk))
		goto exit_overflow;
 
	if (!dst && (dst = inet_csk_route_req(sk, req)) == NULL)
		goto exit;
 
	newsk = tcp_create_openreq_child(sk, req, skb);
	if (!newsk)
		goto exit;
 
	newsk->sk_gso_type = SKB_GSO_TCPV4;
	sk_setup_caps(newsk, dst);
 
	newtp		      = tcp_sk(newsk);
	newinet		      = inet_sk(newsk);
	ireq		      = inet_rsk(req);
	newinet->inet_daddr   = ireq->rmt_addr;
	newinet->inet_rcv_saddr = ireq->loc_addr;
	newinet->inet_saddr	      = ireq->loc_addr;
	newinet->opt	      = ireq->opt;
	ireq->opt	      = NULL;
	newinet->mc_index     = inet_iif(skb);
	newinet->mc_ttl	      = ip_hdr(skb)->ttl;
	inet_csk(newsk)->icsk_ext_hdr_len = 0;
	if (newinet->opt)
		inet_csk(newsk)->icsk_ext_hdr_len = newinet->opt->optlen;
	newinet->inet_id = newtp->write_seq ^ jiffies;
 
	tcp_mtup_init(newsk);
	tcp_sync_mss(newsk, dst_mtu(dst));
	newtp->advmss = dst_metric(dst, RTAX_ADVMSS);
	if (tcp_sk(sk)->rx_opt.user_mss &&
	    tcp_sk(sk)->rx_opt.user_mss < newtp->advmss)
		newtp->advmss = tcp_sk(sk)->rx_opt.user_mss;
 
	tcp_initialize_rcv_mss(newsk);
 
#ifdef CONFIG_TCP_MD5SIG
	/* Copy over the MD5 key from the original socket */
	key = tcp_v4_md5_do_lookup(sk, newinet->inet_daddr);
	if (key != NULL) {
		/*
		 * We"re using one, so create a matching key
		 * on the newsk structure. If we fail to get
		 * memory, then we end up not copying the key
		 * across. Shucks.
		 */
		char *newkey = kmemdup(key->key, key->keylen, GFP_ATOMIC);
		if (newkey != NULL)
			tcp_v4_md5_do_add(newsk, newinet->inet_daddr,
					  newkey, key->keylen);
		newsk->sk_route_caps &= ~NETIF_F_GSO_MASK;
	}
#endif
 
	__inet_hash_nolisten(newsk, NULL);
	__inet_inherit_port(sk, newsk);
 
	return newsk;
 
exit_overflow:
	NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
exit:
	NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
	dst_release(dst);
	return NULL;
}

我們看到了熟悉的函數(shù) sk_acceptq_is_full, 而在此時在無函數(shù)inet_csk_reqsk_queue_young>1來保護(hù),也就是說在此時如果發(fā)現(xiàn)queue是滿的,將直接丟棄只是統(tǒng)計了參數(shù)LINUX_MIB_LISTENOVERFLOWS,LINUX_MIB_LISTENDROPS而這些參數(shù)的值可以通過

netstat -s 來查看到

在函數(shù)tcp_v4_syn_recv_sock中我們看到tcp_create_openreq_child,此時才clone出一個新的socket ,也就是只有通過了3次握手后,linux才會產(chǎn)生新的socket, 而在3次握手中所傳的socket 實際上是server的listen的 socket, 那也就是說這個socket 只有一個狀態(tài)TCP_LISTEN

netstat的狀態(tài)

通過在tcp_rcv_state_process可以置socket 的狀態(tài),而我們通常使用netstat 中看到這些socket的狀態(tài)

case TCP_SYN_RECV:
			if (acceptable) {
				tp->copied_seq = tp->rcv_nxt;
				smp_mb();
				tcp_set_state(sk, TCP_ESTABLISHED);

我們看到從 SYN_RECV的狀態(tài)直接設(shè)置成ESTABLISHED,也就是當(dāng)server收到client的ack回來,狀態(tài)置為 TCP_SYN_RECV,而馬上進(jìn)入tcp_rcv_state_process函數(shù)置為狀態(tài)ESTABLISHED,基本沒有TCP_SYN_RECV 的狀態(tài)期,但我們通過netstat  的使用,還是會發(fā)現(xiàn)有部分socket 還是會處于SYN_RECV狀態(tài),實際上這通常是在syn_table的request, 為了顯示還沒有通過三次握手的連接的狀態(tài),這時候request 還在syn table里,并且還沒有屬于自己的socket對象,linux 把這些信息寫到了

/proc/net/tcp

而在TCP_SEQ_STATE_OPENREQ 的情況下(就是 syn synack ack)的3個狀態(tài)下都顯示成TCP_SYN_RECV

static void get_openreq4(struct sock *sk, struct request_sock *req,
			 struct seq_file *f, int i, int uid, int *len)
{
	const struct inet_request_sock *ireq = inet_rsk(req);
	int ttd = req->expires - jiffies;
 
	seq_printf(f, "%4d: %08X:%04X %08X:%04X"
		" %02X %08X:%08X %02X:%08lX %08X %5d %8d %u %d %p%n",
		i,
		ireq->loc_addr,
		ntohs(inet_sk(sk)->inet_sport),
		ireq->rmt_addr,
		ntohs(ireq->rmt_port),
		TCP_SYN_RECV,
		0, 0, /* could print option size, but that is af dependent. */
		1,    /* timers active (only the expire timer) */
		jiffies_to_clock_t(ttd),
		req->retrans,
		uid,
		0,  /* non standard timer */
		0, /* open_requests have no inode */
		atomic_read(&sk->sk_refcnt),
		req,
		len);
}

而對ESTABLISHED狀態(tài),并不需要server.accept,只要在accept queue里就已經(jīng)變成狀態(tài)ESTABLISHED

到此這篇關(guān)于詳解linux里的backlog參數(shù)的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持。

標(biāo)簽: Linux Apache
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
亚洲精品一级二级| 亚洲国产日韩欧美在线| 国产麻豆综合| 最新亚洲一区| 亚洲女人av| 一区二区亚洲视频| 日韩国产欧美在线播放| 久久精品99久久久| 欧美成a人片免费观看久久五月天| 国产精品v一区二区三区| 美女精品久久| 欧美男人天堂| 国产传媒在线观看| 日韩在线免费| 亚洲免费播放| 亚洲精品成人一区| 国产亚洲一区二区三区不卡 | 91亚洲人成网污www| 国产传媒在线| 国产伊人精品| 亚洲精品系列| 久久精品国产网站| 国产一区二区三区亚洲综合| 日韩欧美一区二区三区在线视频 | 欧美手机在线| 久久亚洲影院| 天堂久久av| 国产一区二区三区久久| 99久久久国产精品美女| 久久一二三区| 国产精品极品| 91精品国产91久久久久久黑人| 亚洲黄页一区| 国产乱人伦精品一区| 黄色aa久久| 欧美日韩免费观看一区=区三区| 蜜臀精品一区二区三区在线观看| 国产亚洲电影| 日韩欧美三级| 亚洲一区二区日韩| 免费亚洲婷婷| 91精品观看| 久久精品凹凸全集| 国产尤物精品| 国产欧美自拍| 欧美日韩在线网站| 国产亚洲高清在线观看| 久久婷婷久久| 欧美日韩亚洲国产精品| 久久国产电影| 日韩不卡免费视频| 久久久9色精品国产一区二区三区| 亚洲理论在线| 久久男人av资源站| 亚州精品视频| 精品成人免费一区二区在线播放| 一本综合精品| 日韩欧美国产精品综合嫩v| 亚洲精品韩国| 久久久久99| 国产毛片一区二区三区 | 久久久久免费| 亚洲免费毛片| 蜜桃精品在线| 亚洲69av| 亚洲精品一区二区妖精| 国产乱子精品一区二区在线观看| 米奇777超碰欧美日韩亚洲| 国产精品久久777777毛茸茸| 美女被久久久| 欧美午夜精彩| 成人在线视频区| 欧美有码在线| 亚洲免费在线| 99热国内精品| 久久影院一区二区三区| 亚洲日产av中文字幕| 日韩美女一区二区三区在线观看| 国产日产精品_国产精品毛片 | 亚洲欧洲日本mm| 国产欧洲在线| 国产精品一区二区精品| 国产精品毛片在线| 福利视频一区| 国产精品第一| 午夜电影一区| 亚洲激情二区| 欧美a级一区| 久久精品国产亚洲aⅴ| 欧美亚洲tv| 亚洲乱码久久| 亚洲欧美日韩视频二区| 91精品亚洲| 高清av不卡| 精品视频在线观看网站| 日韩成人在线看| 亚洲深夜福利在线观看| 婷婷激情综合| 成人免费网站www网站高清| 精品99在线| 国产精品蜜月aⅴ在线| 日韩中文一区二区| 免费日韩av片| 欧美日韩一二三四| 7777精品| 777久久精品| 欧美日一区二区三区在线观看国产免 | 国产精品久久免费视频| 日韩精品91亚洲二区在线观看| 亚洲欧美日韩精品一区二区 | 91精品国产乱码久久久久久久| av资源新版天堂在线| 国产成人免费| 精品亚洲自拍| 精品三级在线观看视频| 久久99性xxx老妇胖精品| 欧美日韩xxxx| 国产精品xxx在线观看| 国产精品男女| 久久中文字幕一区二区| 久久中文欧美| 成人台湾亚洲精品一区二区| 久久三级中文| 成人在线黄色| 日韩欧美在线中字| 成人看片网站| 欧美国产91| 亚洲一区二区日韩| 日韩国产在线一| 国产精品久久久久久久久久白浆| 国产精品.xx视频.xxtv| 韩国女主播一区二区三区| 成人日韩av| 私拍精品福利视频在线一区| 欧美日韩一二三四| 午夜在线一区| 日韩精品欧美精品| 国产精品22p| 欧美韩日一区| 91精品推荐| 午夜在线一区| 91欧美日韩在线| 久久精品毛片| 亚洲午夜天堂| 99成人在线| 中文字幕一区二区三区日韩精品 | 一区二区三区网站| 欧美亚洲tv| 国产+成+人+亚洲欧洲在线| 中文字幕在线高清| 婷婷综合激情| 无码日韩精品一区二区免费| 欧美中文高清| 久久久久久自在自线| 在线 亚洲欧美在线综合一区| 日韩欧美久久| 精品丝袜久久| 伊人久久亚洲美女图片| 一本综合精品| 久久精品国产99国产| 久久中文字幕av| 亚洲久久视频| 国内自拍视频一区二区三区| 99精品一区| 日本精品久久| 热三久草你在线| 中文字幕一区二区三区四区久久 | 午夜精品网站| 欧美精品观看| 亚洲一级高清| 欧美在线观看天堂一区二区三区| 91亚洲成人| 亚洲精品中文字幕99999| 国产一区二区精品福利地址| 国产一区二区高清| 精品国产欧美| 免费人成在线不卡| av最新在线| 日韩精品高清不卡| 久久亚洲专区| 日韩国产在线观看一区| 久久青草久久| 欧美精品国产一区| 黄色免费成人| 久久一区视频| 蜜芽一区二区三区| 国产欧美一区二区三区精品酒店| 在线视频亚洲欧美中文| 国产精品精品| 日韩一区二区三区四区五区| 亚洲精品一级二级| 国产欧美日韩影院| 亚洲一区二区毛片| 日韩成人a**站| 日本成人一区二区| 国产精品av久久久久久麻豆网| 国产精品sss在线观看av| 久久电影一区| 日韩在线不卡| 国产精品天天看天天狠|