You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Describe the bug
When using gopsutil to traverse all processes on Linux systems which support pidfd_open(2), the process will hold too many file descriptors with pidfd type.
The bug is caused by the wrong usage of os.FindProcess() in PidExistsWithContext, we need call proc.Release() immediately after os.FindProcess() returns:
ifisMount(common.HostProcWithContext(ctx)) { // if /<HOST_PROC>/proc exists and is mounted, check if /<HOST_PROC>/proc/<PID> folder exists
The correct code would be:
funcPidExistsWithContext(ctx context.Context, pidint32) (bool, error) {
ifpid<=0 {
returnfalse, fmt.Errorf("invalid pid %v", pid)
}
proc, err:=os.FindProcess(int(pid))
iferr!=nil {
returnfalse, err
}
deferproc.Release()
ifisMount(common.HostProcWithContext(ctx)) { // if /<HOST_PROC>/proc exists and is mounted, check if /<HOST_PROC>/proc/<PID> folder exists
The doc in os library:
Release releases any resources associated with the Process p, rendering it unusable in the future. Release only needs to be called if Process.Wait is not.
funcfindProcess(pidint) (p*Process, errerror) {
h, err:=pidfdFind(pid)
iferr==ErrProcessDone {
// We can't return an error here since users are not expecting// it. Instead, return a process with a "done" state already// and let a subsequent Signal or Wait call catch that.returnnewDoneProcess(pid), nil
} elseiferr!=nil {
// Ignore other errors from pidfdFind, as the callers// do not expect them. Fall back to using the PID.returnnewPIDProcess(pid), nil
}
// Use the handle.returnnewHandleProcess(pid, h), nil
}
func (p*Process) handlePersistentRelease(reasonprocessStatus) processStatus {
ifp.mode!=modeHandle {
panic("handlePersistentRelease called in invalid mode")
}
for {
refs:=p.state.Load()
status:=processStatus(refs&processStatusMask)
ifstatus!=statusOK {
// Both Release and successful Wait will drop the// Process' persistent reference on the handle. We// can't allow concurrent calls to drop the reference// twice, so we use the status as a guard to ensure the// reference is dropped exactly once.returnstatus
}
ifrefs==0 {
// This should never happen because dropping the// persistent reference always sets a status.panic("release of handle with refcount 0")
}
new:= (refs-1) |uint64(reason)
if!p.state.CompareAndSwap(refs, new) {
continue
}
ifnew&^processStatusMask==0 {
p.closeHandle()
}
returnstatus
}
}
compile it by executing go build -o demo main.go. After executing the demo code, use ls -l /proc/$(pgrep demo)/fd | grep pidfd | wc -l command to count the number of pidfds. Expected behavior
Environment (please complete the following information):
Windows: [paste the result of ver]
Linux: 5.10.134-008.2.ali5000.al8.x86_64
Mac OS: [paste the result of sw_vers and uname -a
FreeBSD: [paste the result of freebsd-version -k -r -u and uname -a]
Thank you for reminding me. I see, indeed, the Process.Release() document says:
Release only needs to be called if Process.Wait is not.
As you pointed out, os.Process does seem to hold the handle internally. Therefore, calling Process.Release() appears to be reasonable. Thank you for your contribution, and I apologize for the delayed response.
Describe the bug
When using
gopsutil
to traverse all processes on Linux systems which supportpidfd_open(2)
, the process will hold too many file descriptors withpidfd
type.The bug is caused by the wrong usage of
os.FindProcess()
inPidExistsWithContext
, we need callproc.Release()
immediately afteros.FindProcess()
returns:gopsutil/process/process_posix.go
Lines 103 to 112 in e89f21d
The correct code would be:
The doc in
os
library:The reason is for kernels which support
pidfd_open(2)
,os.FindProcess()
will callpidfd_open(2)
and save the fd intoProcess.handle
, and then close it when(*Process).Release()
called.https://github.com/golang/go/blob/e9a500f47dadcd73c970649a1072d28997617610/src/os/exec_unix.go#L156-L170
https://github.com/golang/go/blob/e9a500f47dadcd73c970649a1072d28997617610/src/os/exec.go#L219-L221
Besides
SendSignalWithContext
is also affected.To Reproduce
compile it by executing
go build -o demo main.go
. After executing the demo code, usels -l /proc/$(pgrep demo)/fd | grep pidfd | wc -l
command to count the number of pidfds.Expected behavior
Environment (please complete the following information):
ver
]sw_vers
anduname -a
freebsd-version -k -r -u
anduname -a
]uname -a
]Additional context
The text was updated successfully, but these errors were encountered: