// File returns a copy of the underlying os.File. // It is the caller's responsibility to close f when finished. // Closing l does not affect f, and closing f does not affect l. // // The returned os.File's file descriptor is different from the // connection's. Attempting to change properties of the original // using this duplicate may or may not have the desired effect. func(l *TCPListener) File() (f *os.File, err error) { if !l.ok() { returnnil, syscall.EINVAL } f, err = l.file() if err != nil { returnnil, &OpError{Op: "file", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err} } return }
// DupCloseOnExec dups fd and marks it close-on-exec. funcDupCloseOnExec(fd int) (int, string, error) { if syscall.F_DUPFD_CLOEXEC != 0 && atomic.LoadInt32(&tryDupCloexec) == 1 { // 通过 fcntl 系统调用执行 dup,同时设置 FD_CLOEXEC 标志 // fcntl 系统调用,复制一个已有文件描述符,功能和dup和dup2相同,对应的cmd:F_DUPFD、F_DUPFD_CLOEXEC。 r0, e1 := fcntl(fd, syscall.F_DUPFD_CLOEXEC, 0) if e1 == nil { return r0, "", nil } switch e1.(syscall.Errno) { case syscall.EINVAL, syscall.ENOSYS: // Old kernel, or js/wasm (which returns // ENOSYS). Fall back to the portable way from // now on. atomic.StoreInt32(&tryDupCloexec, 0) default: return-1, "fcntl", e1 } } // 老版本内核的 fcntl 不支持 F_DUPFD_CLOEXEC 命令,单独处理 return dupCloseOnExecOld(fd) }
// dupCloseOnExecOld is the traditional way to dup an fd and // set its O_CLOEXEC bit, using two system calls. funcdupCloseOnExecOld(fd int) (int, string, error) { syscall.ForkLock.RLock() defer syscall.ForkLock.RUnlock() newfd, err := syscall.Dup(fd) if err != nil { return-1, "dup", err } syscall.CloseOnExec(newfd) return newfd, "", nil }
O_CLOEXEC (since Linux 2.6.23) Enable the close-on-exec flag for the new file descriptor. Specifying this flag permits a program to avoid additional fcntl(2) F_SETFD operations to set the FD_CLOEXEC flag.
Note that the use of this flag is essential in some multithreaded programs, because using a separate fcntl(2) F_SETFD operation to set the FD_CLOEXEC flag does not suffice to avoid race conditions where one thread opens a file descriptor and attempts to set its close-on-exec flag using fcntl(2) at the same time as another thread does a fork(2) plus execve(2). Depending on the order of execution, the race may lead to the file descriptor returned by open() being unintentionally leaked to the program executed by the child process created by fork(2). (This kind of race is in principle possible for any system call that creates a file descriptor whose close-on-exec flag should be set, and various other Linux system calls provide an equivalent of the O_CLOEXEC flag to deal with this problem.)
// Fd returns the integer Unix file descriptor referencing the open file. // The file descriptor is valid only until f.Close is called or f is garbage collected. // On Unix systems this will cause the SetDeadline methods to stop working. func(f *File) Fd() uintptr { if f == nil { return ^(uintptr(0)) }
// If we put the file descriptor into nonblocking mode, // then set it to blocking mode before we return it, // because historically we have always returned a descriptor // opened in blocking mode. The File will continue to work, // but any blocking operation will tie up a thread. if f.nonblock { f.pfd.SetBlocking() }
// Close closes the FD. The underlying file descriptor is closed by the // destroy method when there are no remaining references. func(fd *FD) Close() error { if !fd.fdmu.increfAndClose() { return errClosing(fd.isFile) }
// Unblock any I/O. Once it all unblocks and returns, // so that it cannot be referring to fd.sysfd anymore, // the final decref will close fd.sysfd. This should happen // fairly quickly, since all the I/O is non-blocking, and any // attempts to block in the pollDesc will return errClosing(fd.isFile). fd.pd.evict()
// The call to decref will call destroy if there are no other // references. err := fd.decref()
// Wait until the descriptor is closed. If this was the only // reference, it is already closed. Only wait if the file has // not been set to blocking mode, as otherwise any current I/O // may be blocking, and that would block the Close. // No need for an atomic read of isBlocking, increfAndClose means // we have exclusive access to fd. // 如果是阻塞模式,则会等待直到描述符关闭 if fd.isBlocking == 0 { runtime_Semacquire(&fd.csema) }