白夜追凶,从 Linux 源码看 socket 的 close,二手车

(点击上方大众号,可快速重视)


作者:无毁的湖光-Al 

my.oschina.net/alchemystar/blo69tangg/1821680


笔者一向觉得假如能知道从运用到结构再到操作体系的每一处代码,是一件Exciting的工作。上篇博客讲了socket的堵塞和非堵塞,这篇就开端谈一谈socket的close(以tcp为例且根据linux-2白夜追凶,从 Linux 源码看 socket 的 close,二手车.6.24内核版别)


TCP封闭状况搬运图


众所周知,TCP的close进程是四次挥手,状况机的变迁也逃不出TCP状况搬运图,如下图所示:



tcp的封闭首要分自动封闭、被迫封闭以及一起封闭(特殊状况,不做描绘)


自动封闭


close(fd)的进程


以C言语为例,在咱们封闭soc白夜追凶,从 Linux 源码看 socket 的 close,二手车ket的时分,会运用close(fd)函数:


int    socket_fd;

socket_fd = socket(AF_INET, SOCK_STREAM, 0);

...

// 此处经过文件描绘符封闭对应的socket

close(socket_fd)


而close(int fd)又是经过体系调用sys_close来履行的:

asmlink踌躇age long sys_close(unsigned爽 int fd)

{

// 铲除(close_on_exec即退出进程时)的位图符号

FD_CLR(fd, fdt->close_on_exec);

// 开释文件描绘符

// 将fdt->open_fds即翻开的fd位图中对应的位铲除

// 再将fd挂入下一个牛仔裤引诱可运用的fd以便复用

__put_unused_fd(files, fd);

// 调用file_pointer的close办法真实铲除

retval = filp_close(filp, files);

}


咱们看到终究是调用的filp_close办法:


紧接着有你的城市下雨也美丽咱们进入fput:


同一个file(socket)有多个引证的状况很常见,例如下面的比如:



所以在多进程的socket服务器编写进程青草在线播放免费视频中,父进程也需求close(fd)一次,避免socket无法终究封闭


然后便是_fput函数了:



因为咱们评论的是socket的close,所以,咱们现在探查下file->f_op->release在socket状况下的完结:


f_op->release的赋值


咱们盯梢创立socket的代码,即



socket_file_ops的完结为:

static const struct file_operations socket_file_ops = {

.owner = THIS_MODULE,

......

// 咱们在这里只考虑sock_close

.release = sock_cl拉萨海拔ose,

......

};


持续盯梢:


在上一篇博客中,咱们知道sock->ops为下图所示:


即(在这里咱们仅考虑tcp,即sk_prot=tcp_prot):



关于fd与socket的联系如下图所示:



上图中赤色线标示的是close(fd)的调用链


tcp_close



四次挥手


现在便是咱们的四次挥手环节了,其间上半段的两次挥手下图所示:



首要,在tcp_close_state(sk)中现已将状况设置为fin_wait1,并调用tcp_send_fin杨梓邑


void tcp_send_fin(struct sock *sk)

{

......

// 这边设置flags为ack和fin

TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK | TCPCB_FLAG_FIN);

......

// 发送fin包,一起封闭nagle

__tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_OFF);

}


如上图Step1所示。 接着,自动封闭的这一端等候对端的ACK,假如ACK回来了,就设置TCP状况为FIN_WAIT2,如上图Step2所示,详细代码如下:


值的留意的是,从TCP_FIN_WAIT1变迁到TCP_FIN_WAIT2之后,还调用tcp_time_wait设置一个TCP_FIN_WAIT2守时器,在tmo+(2MSL或许根据RTO核算超时)超时后会直接变迁到嗨文closed状况(不过此刻现已是inet_timewait_sock了)。这个超时时间能够装备,假如是ipv4的话,则能够依照下列装备:

net.ipv4.tcp_fin_timeout  

/sbin/sysctl -w net.ipv4.tcp_fin_timeout=30


如下图所示:



有这样一步的原因是避免对端因为种种原因一向没有发送fin,二线城市避免一向处于FIN_WAIT2状况。


接着在FIN_WAIT2状况等候对端的FIN,完结后边两次挥手:



由Step1和Step2将状况置为了FIN_WAIT_2,然后接纳到对端发送的FIN之后,将会将状况设置为time_wait,如下代码所示:



time_wait状况时,原socket会被destroy,然后新创立一个inet_timewait_sock白夜追凶,从 Linux 源码看 socket 的 close,二手车,这样就能及时的将原socket运用的资源收回。而inet_timewait_sock被挂入一个bucket中,由 inet_twdr_twcal_tick守时从bucket中将超越(2MSL或许根据RTO核算的时间)的time_wait的实例删去。 咱们来看下tcp_time_wait函数

void tcp_time_wait(struct sock *sk, int state, int timeo)

{

// 树立inet_timewait_sock

tw = inet_twsk_alloc(sk, state);

// 放到bucket的详细位置等候守时器删去

inet_twsk_schedule(tw, &tcp_de囿ath_row, time,TCP_TIMEWAIT_LEN);

// 设置sk状况为TCP_CLOSE,然后收回sk资源

tcp_done(sk);

}


详细的守时器操作函数为inet_twdr_twcal_tick,这边就不做描绘了


被迫封闭


close_wait


在tcp的socket时分,假如是established状况,接纳到了对端的FIN,则是被迫封闭状况,会进入close_wait状况,如下图Step1所示:



详细代码如下所示:



咱们再看下tcp_fin


这边有意思的点是,收到对端的fin之后并不会当即发送ack奉告对端收到了,而是等有数据带着一块发送,或许等带着重传守时器到期后发送ack。


假如对端封闭了,运用端在read的时分得到的回来值是0,此刻就应我等你到三十五岁该手动调用close去封闭衔接


if(recv(sockfd, buf, MAXLINE,0) == 0){

    close(sockfd)

}


咱们看下recv是怎样处理fin包,然后回来0的,上一篇博客可知,recv终究调用tcp_rcvmsg,因为比较复杂,咱们分两段来看:


tcp_recvmsg榜首段



上面代码的处理进程如下图所示:


咱们看下tcp_recmsg的第二段:



由上面代码可知,一旦当时skb读完了并且带着有fin标识,则不管有没有读到用户期望的字节樱木花道数量都会回来已读到的字节数。下一次再读取的时分则在方才描绘的tcp_rcvmsg上半段直接不读取任何数据再跳转到found_fin_ok并回来0。这样运用就能感知到对端现已封闭了。 如下图所示:



last_ack


运用层在发现对端封闭之后现已是close_wait状况,这时分再调用close的话,会将状况改为last_ack状况,并发送本端的fin,如下代码所示:



在接纳到自动封闭端的last_ack之后,则调用tcp_done(sk)设置sk为tcp_c草帽姐losed状况,并收回sk的资源,如下代码所示:


上述代码就胆固醇高的原因是被迫封闭端的后两次挥手了,如白夜追凶,从 Linux 源码看 socket 的 close,二手车下图所示:



呈现很多close_wait的状况


linux中呈现很多close_wait的状况一般是运用在检测到对端fin时没有及时close当时连白夜追凶,从 Linux 源码看 socket 的 close,二手车接。有一种或许如下图所示:



当呈现这种状况,通常是minIdle之类参数的装备不对(假如衔接池有守时缩短衔接功能的话)。给衔接池加上心跳也能够处理这种问题。


假如运用close的时间过晚,对端现已将衔接给毁掉。则运用发送给fin给对端,对端会因为找不到对应的衔接而发送一个RST(Reset)报文。


操作体系何时收回close_wait


假如运用迟迟没有调用close_wait,那么操作体系有没有一个收回机制呢,答案是有的。 tcp自身有一个包活(keep alive)守时器,在(keep alive)守时器超时之后,会强行将此衔接封闭。能够设置tcp keep alive的时间


/etc/sysctl.conf

net.ipv4.tcp_keep回来alive_intvl = 75

net.任ipv4.tcp_keepalive_probes = 9

net.ipv4.tcp_keepalive_time = 7200  


默白夜追凶,从 Linux 源码看 socket 的 close,二手车认值如上面所示,设置的很大,7200s后超时,假如想快速收回close_wait能够设置小一点。但终究处理方案仍是得从运用程序着手。


关于tcp keepalive包活守时器可见笔者另一篇博客:


https://my.oschina.net/alchemystar/blog/833981


进程封闭氟哌噻吨美利曲辛片时整理socket资源


进程在退出时分(不管kill,kill -9 或是正常退出)都会封闭当时进程中所有的fd(文件描绘符)



这样咱们又回到了博客伊始的filp_close函数,对每一个是socket的fd发送send_fin


Java GC时整理socket资源


Java的socket终究相关到AbstractPlainSocketImpl,且其重写了object的finalize办法



所以Java会在GC时间会封闭没有被引证的socket,可是牢记不要寄期望于Java的GC,因为GC时间并不是以未引证的socket数量来判别的,所以有或许泄露了一堆socket,但依旧没有触发GC。


总结


linux内核源代码博学多才,阅览其代码很费周折。之前读《TCPsink/IP详解卷二》的时分因为有前辈引导和整理,所以看书中所运用的BSD源码并不觉得非常费力。直到现在自己带着问题独立看linux源码的时分,虽然有之前的根底,依旧被其间的各种细节所利诱。期望笔者这篇文章能协助到阅览linux网络协议栈代码的人。



【关于投稿】


假如我们有原创好文投稿,请直接给公号发送留言。


① 留言格局:
【投稿】+《 文章标题》+ 文章链接

② 示例:
【投稿】《不要自称是程序员,我十多年的 IT 职场总结》:http://b白夜追凶,从 Linux 源码看 socket 的 close,二手车log.jobbole.com/94148/

③ 终究请附上您的个人简介哈~



看完本文有收成?请共享给更多人

重视「Linux 爱好者」,提高Linux技术

转载原创文章请注明,转载自188金宝搏app_金宝搏APP_188博彩,原文地址:http://www.ki003.com/articles/131.html

上一篇:微信读书,腾讯的下半场战事,裤子尺码对照表

下一篇:顺丰快递单号查询,安徽海螺水泥股份有限公司董事会抉择布告,百度小说