Source File
pidfd_linux.go
Belonging Package
os
// Copyright 2023 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.// Support for pidfd was added during the course of a few Linux releases:// v5.1: pidfd_send_signal syscall;// v5.2: CLONE_PIDFD flag for clone syscall;// v5.3: pidfd_open syscall, clone3 syscall;// v5.4: P_PIDFD idtype support for waitid syscall;// v5.6: pidfd_getfd syscall.//// N.B. Alternative Linux implementations may not follow this ordering. e.g.,// QEMU user mode 7.2 added pidfd_open, but CLONE_PIDFD was not added until// 8.0.package osimport (_ // for linkname)// ensurePidfd initializes the PidFD field in sysAttr if it is not already set.// It returns the original or modified SysProcAttr struct and a flag indicating// whether the PidFD should be duplicated before using.func ensurePidfd( *syscall.SysProcAttr) (*syscall.SysProcAttr, bool) {if !pidfdWorks() {return , false}var intif == nil {return &syscall.SysProcAttr{PidFD: &,}, false}if .PidFD == nil {:= * // copy.PidFD = &return &, false}return , true}// getPidfd returns the value of sysAttr.PidFD (or its duplicate if needDup is// set) and a flag indicating whether the value can be used.func getPidfd( *syscall.SysProcAttr, bool) (uintptr, bool) {if !pidfdWorks() {return 0, false}:= *.PidFDif {, := unix.Fcntl(, syscall.F_DUPFD_CLOEXEC, 0)if != nil {return 0, false}=}return uintptr(), true}// pidfdFind returns the process handle for pid.func pidfdFind( int) (uintptr, error) {if !pidfdWorks() {return 0, syscall.ENOSYS}, := unix.PidFDOpen(, 0)if != nil {return 0, convertESRCH()}return , nil}// pidfdWait waits for the process to complete,// and updates the process status to done.func ( *Process) () (*ProcessState, error) {// When pidfd is used, there is no wait/kill race (described in CL 23967)// because the PID recycle issue doesn't exist (IOW, pidfd, unlike PID,// is guaranteed to refer to one particular process). Thus, there is no// need for the workaround (blockUntilWaitable + sigMu) from pidWait.//// We _do_ need to be careful about reuse of the pidfd FD number when// closing the pidfd. See handle for more details., := .handleTransientAcquire()switch {case statusDone:// Process already completed Wait, or was not found by// pidfdFind. Return ECHILD for consistency with what the wait// syscall would return.return nil, NewSyscallError("wait", syscall.ECHILD)case statusReleased:return nil, syscall.EINVAL}defer .handleTransientRelease()var (unix.SiginfoChildsyscall.Rusage):= ignoringEINTR(func() error {return unix.Waitid(unix.P_PIDFD, int(), &, syscall.WEXITED, &)})if != nil {return nil, NewSyscallError("waitid", )}// Update the Process status to statusDone.// This also releases a reference to the handle..doRelease(statusDone)return &ProcessState{pid: int(.Pid),status: .WaitStatus(),rusage: &,}, nil}// pidfdSendSignal sends a signal to the process.func ( *Process) ( syscall.Signal) error {, := .handleTransientAcquire()switch {case statusDone:return ErrProcessDonecase statusReleased:return errors.New("os: process already released")}defer .handleTransientRelease()return convertESRCH(unix.PidFDSendSignal(, ))}// pidfdWorks returns whether we can use pidfd on this system.func pidfdWorks() bool {return checkPidfdOnce() == nil}// checkPidfdOnce is used to only check whether pidfd works once.var checkPidfdOnce = sync.OnceValue(checkPidfd)// checkPidfd checks whether all required pidfd-related syscalls work. This// consists of pidfd_open and pidfd_send_signal syscalls, waitid syscall with// idtype of P_PIDFD, and clone(CLONE_PIDFD).//// Reasons for non-working pidfd syscalls include an older kernel and an// execution environment in which the above system calls are restricted by// seccomp or a similar technology.func checkPidfd() error {// In Android version < 12, pidfd-related system calls are not allowed// by seccomp and trigger the SIGSYS signal. See issue #69065.if runtime.GOOS == "android" {ignoreSIGSYS()defer restoreSIGSYS()}// Get a pidfd of the current process (opening of "/proc/self" won't// work for waitid)., := unix.PidFDOpen(syscall.Getpid(), 0)if != nil {return NewSyscallError("pidfd_open", )}defer syscall.Close(int())// Check waitid(P_PIDFD) works.= ignoringEINTR(func() error {var unix.SiginfoChild// We don't actually care about the info, but passing a nil pointer// makes valgrind complain because 0x0 is unaddressable.return unix.Waitid(unix.P_PIDFD, int(), &, syscall.WEXITED, nil)})// Expect ECHILD from waitid since we're not our own parent.if != syscall.ECHILD {return NewSyscallError("pidfd_wait", )}// Check pidfd_send_signal works (should be able to send 0 to itself).if := unix.PidFDSendSignal(, 0); != nil {return NewSyscallError("pidfd_send_signal", )}// Verify that clone(CLONE_PIDFD) works.//// This shouldn't be necessary since pidfd_open was added in Linux 5.3,// after CLONE_PIDFD in Linux 5.2, but some alternative Linux// implementations may not adhere to this ordering.if := checkClonePidfd(); != nil {return}return nil}// Provided by syscall.////go:linkname checkClonePidfdfunc checkClonePidfd() error// Provided by runtime.////go:linkname ignoreSIGSYSfunc ignoreSIGSYS()//go:linkname restoreSIGSYSfunc restoreSIGSYS()
![]() |
The pages are generated with Golds v0.7.9-preview. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |