| 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/<pid>/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/<pid>/fd during the calculation. |
| return fdCount |
| } |