go源码解析之TCP连接系列基于go源码1.16.5
连接关闭
上一章我们通过跟踪TCPConn的Write方法,了解了发送数据的过程以及fd的读写锁,本章将通过TCPConn的Close方法的跟踪来了解连接关闭的过程。
1. conn的Close方法
从上一章了解到TCPConn继承自conn,它的Close方法就是conn的Close,代码如下:
src/net/net.go
func (c *conn) Close() error {
    ...
    err := c.fd.Close()
    ...
    return err
}
同样conn的Close调用了netFD的Close方法:
src/net/fd_posix.go
func (fd *netFD) Close() error {
    runtime.SetFinalizer(fd, nil)
    return fd.pfd.Close()
}
2. 回顾SetFinalizer
回顾一下SetFinalizer,它设置参数一被回收时需要执行的清理方法,Close这里将fd的清理方法删除,我们再回顾一下fd的清理方法是在什么时候被设置的:
src/net/fd_posix.go
func (fd *netFD) setAddr(laddr, raddr Addr) {
    fd.laddr = laddr
    fd.raddr = raddr
    runtime.SetFinalizer(fd, (*netFD).Close)
}
src/net/fd_unix.go
func (fd *netFD) accept() (netfd *netFD, err error) {
    d, rsa, errcall, err := fd.pfd.Accept()
    ...
    if netfd, err = newFD(d, fd.family, fd.sotype, fd.net); err != nil {
        poll.CloseFunc(d)
        return nil, err
    }
    if err = netfd.init(); err != nil {
        netfd.Close()
        return nil, err
    }
    lsa, _ := syscall.Getsockname(netfd.pfd.Sysfd)
    netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa))
    return netfd, nil
}
可以看到在初始化netFD之后调用的setAddr方法中设置了清理方法。清理方法指定为netFD的Close。在Close方法中将清理方法删除,防止netFD被回收时重复执行Close。
3. poll.FD的Close方法
src/internal/poll/fd_unix.go
func (fd *FD) Close() error {
    if !fd.fdmu.increfAndClose() {
        return errClosing(fd.isFile)
    }
    fd.pd.evict()
    err := fd.decref()
    if fd.isBlocking == 0 {
        runtime_Semacquire(&fd.csema)
    }
    return err
}
- increfAndClose方法将fd设置为关闭状态并唤醒其他所有等待锁的协程,在上一章有讲解。
 - evict方法将阻塞等待io消息的协程唤醒,这块将在后续go语言实现io多路复用的章节详细讲解。
 - decref与incref成对出现,如果fd的引用数量为0,将执行destory调用系统方法关闭fd
 - runtime_Semacquire,如果fd是非阻塞模式,则需要请求信号量csema,等待destory方法完成并发送信号量后,再被唤醒
 
destory方法:
src/internal/poll/fd_unix.go
func (fd *FD) destroy() error {
    ...
    err := CloseFunc(fd.Sysfd)
    fd.Sysfd = -1
    runtime_Semrelease(&fd.csema)
    return err
}
- CloseFunc进行系统调用关闭fd
 - runtime_Semrelease释放信号量csema
 
4. 小结
关闭方法不会第一时间调用系统方法销毁系统fd,系统fd的销毁需要在不再有引用时执行。








网友评论