package fileutils import ( "context" "fmt" "io" "os" "github.com/containerd/containerd/tracing" "github.com/containerd/log" "golang.org/x/sys/unix" ) // GetTotalUsedFds Returns the number of used File Descriptors by // reading it via /proc filesystem. func GetTotalUsedFds(ctx context.Context) int { ctx, span := tracing.StartSpan(ctx, "GetTotalUsedFds") defer span.End() name := fmt.Sprintf("/proc/%d/fd", os.Getpid()) // Fast-path for Linux 6.2 (since [f1f1f2569901ec5b9d425f2e91c09a0e320768f3]). // From the [Linux docs]: // // "The number of open files for the process is stored in 'size' member of // stat() output for /proc//fd for fast access." // // [Linux docs]: https://docs.kernel.org/filesystems/proc.html#proc-pid-fd-list-of-symlinks-to-open-files: // [f1f1f2569901ec5b9d425f2e91c09a0e320768f3]: https://github.com/torvalds/linux/commit/f1f1f2569901ec5b9d425f2e91c09a0e320768f3 var stat unix.Stat_t if err := unix.Stat(name, &stat); err == nil && stat.Size > 0 { return int(stat.Size) } f, err := os.Open(name) if err != nil { log.G(ctx).WithError(err).Error("Error listing file descriptors") return -1 } defer f.Close() var fdCount int for { select { case <-ctx.Done(): log.G(ctx).WithError(ctx.Err()).Error("Context cancelled while counting file descriptors") return -1 default: } names, err := f.Readdirnames(100) fdCount += len(names) if err == io.EOF { break } else if err != nil { log.G(ctx).WithError(err).Error("Error listing file descriptors") return -1 } } // Note that the slow path has 1 more file-descriptor, due to the open // file-handle for /proc//fd during the calculation. return fdCount }