Source File
exec.go
Belonging Package
os
// Copyright 2009 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.package osimport ()// ErrProcessDone indicates a [Process] has finished.var ErrProcessDone = errors.New("os: process already finished")// processStatus describes the status of a [Process].type processStatus uint32const (// statusOK means that the Process is ready to use.statusOK processStatus = iota// statusDone indicates that the PID/handle should not be used because// the process is done (has been successfully Wait'd on).statusDone// statusReleased indicates that the PID/handle should not be used// because the process is released.statusReleased)// Process stores the information about a process created by [StartProcess].type Process struct {Pid int// state contains the atomic process state.//// This consists of the processStatus fields,// which indicate if the process is done/released.state atomic.Uint32// Used only when handle is nilsigMu sync.RWMutex // avoid race between wait and signal// handle, if not nil, is a pointer to a struct// that holds the OS-specific process handle.// This pointer is set when Process is created,// and never changed afterward.// This is a pointer to a separate memory allocation// so that we can use runtime.AddCleanup.handle *processHandle// cleanup is used to clean up the process handle.cleanup runtime.Cleanup}// processHandle holds an operating system handle to a process.// This is only used on systems that support that concept,// currently Linux and Windows.// This maintains a reference count to the handle,// and closes the handle when the reference drops to zero.type processHandle struct {// The actual handle. This field should not be used directly.// Instead, use the acquire and release methods.//// On Windows this is a handle returned by OpenProcess.// On Linux this is a pidfd.handle uintptr// Number of active references. When this drops to zero// the handle is closed.refs atomic.Int32}// acquire adds a reference and returns the handle.// The bool result reports whether acquire succeeded;// it fails if the handle is already closed.// Every successful call to acquire should be paired with a call to release.func ( *processHandle) () (uintptr, bool) {for {:= .refs.Load()if < 0 {panic("internal error: negative process handle reference count")}if == 0 {return 0, false}if .refs.CompareAndSwap(, +1) {return .handle, true}}}// release releases a reference to the handle.func ( *processHandle) () {for {:= .refs.Load()if <= 0 {panic("internal error: too many releases of process handle")}if .refs.CompareAndSwap(, -1) {if == 1 {.closeHandle()}return}}}// newPIDProcess returns a [Process] for the given PID.func newPIDProcess( int) *Process {:= &Process{Pid: ,}return}// newHandleProcess returns a [Process] with the given PID and handle.func newHandleProcess( int, uintptr) *Process {:= &processHandle{handle: ,}// Start the reference count as 1,// meaning the reference from the returned Process..refs.Store(1):= &Process{Pid: ,handle: ,}.cleanup = runtime.AddCleanup(, (*processHandle).release, )return}// newDoneProcess returns a [Process] for the given PID// that is already marked as done. This is used on Unix systems// if the process is known to not exist.func newDoneProcess( int) *Process {:= &Process{Pid: ,}.state.Store(uint32(statusDone)) // No persistent reference, as there is no handle.return}// handleTransientAcquire returns the process handle or,// if the process is not ready, the current status.func ( *Process) () (uintptr, processStatus) {if .handle == nil {panic("handleTransientAcquire called in invalid mode")}:= processStatus(.state.Load())if != statusOK {return 0,}, := .handle.acquire()if {return , statusOK}// This case means that the handle has been closed.// We always set the status to non-zero before closing the handle.// If we get here the status must have been set non-zero after// we just checked it above.= processStatus(.state.Load())if == statusOK {panic("inconsistent process status")}return 0,}// handleTransientRelease releases a handle returned by handleTransientAcquire.func ( *Process) () {if .handle == nil {panic("handleTransientRelease called in invalid mode")}.handle.release()}// pidStatus returns the current process status.func ( *Process) () processStatus {if .handle != nil {panic("pidStatus called in invalid mode")}return processStatus(.state.Load())}// ProcAttr holds the attributes that will be applied to a new process// started by StartProcess.type ProcAttr struct {// If Dir is non-empty, the child changes into the directory before// creating the process.Dir string// If Env is non-nil, it gives the environment variables for the// new process in the form returned by Environ.// If it is nil, the result of Environ will be used.Env []string// Files specifies the open files inherited by the new process. The// first three entries correspond to standard input, standard output, and// standard error. An implementation may support additional entries,// depending on the underlying operating system. A nil entry corresponds// to that file being closed when the process starts.// On Unix systems, StartProcess will change these File values// to blocking mode, which means that SetDeadline will stop working// and calling Close will not interrupt a Read or Write.Files []*File// Operating system-specific process creation attributes.// Note that setting this field means that your program// may not execute properly or even compile on some// operating systems.Sys *syscall.SysProcAttr}// A Signal represents an operating system signal.// The usual underlying implementation is operating system-dependent:// on Unix it is syscall.Signal.type Signal interface {String() stringSignal() // to distinguish from other Stringers}// Getpid returns the process id of the caller.func () int { return syscall.Getpid() }// Getppid returns the process id of the caller's parent.func () int { return syscall.Getppid() }// FindProcess looks for a running process by its pid.//// The [Process] it returns can be used to obtain information// about the underlying operating system process.//// On Unix systems, FindProcess always succeeds and returns a Process// for the given pid, regardless of whether the process exists. To test whether// the process actually exists, see whether p.Signal(syscall.Signal(0)) reports// an error.func ( int) (*Process, error) {return findProcess()}// StartProcess starts a new process with the program, arguments and attributes// specified by name, argv and attr. The argv slice will become [os.Args] in the// new process, so it normally starts with the program name.//// If the calling goroutine has locked the operating system thread// with [runtime.LockOSThread] and modified any inheritable OS-level// thread state (for example, Linux or Plan 9 name spaces), the new// process will inherit the caller's thread state.//// StartProcess is a low-level interface. The [os/exec] package provides// higher-level interfaces.//// If there is an error, it will be of type [*PathError].func ( string, []string, *ProcAttr) (*Process, error) {testlog.Open()return startProcess(, , )}// 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.func ( *Process) () error {// Unfortunately, for historical reasons, on systems other// than Windows, Release sets the Pid field to -1.// This causes the race detector to report a problem// on concurrent calls to Release, but we can't change it now.if runtime.GOOS != "windows" {.Pid = -1}:= .doRelease(statusReleased)// For backward compatibility, on Windows only,// we return EINVAL on a second call to Release.if runtime.GOOS == "windows" {if == statusReleased {return syscall.EINVAL}}return nil}// doRelease releases a [Process], setting the status to newStatus.// If the previous status is not statusOK, this does nothing.// It returns the previous status.func ( *Process) ( processStatus) processStatus {for {:= .state.Load():= processStatus()if != statusOK {return}if !.state.CompareAndSwap(, uint32()) {continue}// We have successfully released the Process.// If it has a handle, release the reference we// created in newHandleProcess.if .handle != nil {// No need for more cleanup.// We must stop the cleanup before calling release;// otherwise the cleanup might run concurrently// with the release, which would cause the reference// counts to be invalid, causing a panic..cleanup.Stop().handle.release()}return statusOK}}// Kill causes the [Process] to exit immediately. Kill does not wait until// the Process has actually exited. This only kills the Process itself,// not any other processes it may have started.func ( *Process) () error {return .kill()}// Wait waits for the [Process] to exit, and then returns a// ProcessState describing its status and an error, if any.// Wait releases any resources associated with the Process.// On most operating systems, the Process must be a child// of the current process or an error will be returned.func ( *Process) () (*ProcessState, error) {return .wait()}// Signal sends a signal to the [Process].// Sending [Interrupt] on Windows is not implemented.func ( *Process) ( Signal) error {return .signal()}// UserTime returns the user CPU time of the exited process and its children.func ( *ProcessState) () time.Duration {return .userTime()}// SystemTime returns the system CPU time of the exited process and its children.func ( *ProcessState) () time.Duration {return .systemTime()}// Exited reports whether the program has exited.// On Unix systems this reports true if the program exited due to calling exit,// but false if the program terminated due to a signal.func ( *ProcessState) () bool {return .exited()}// Success reports whether the program exited successfully,// such as with exit status 0 on Unix.func ( *ProcessState) () bool {return .success()}// Sys returns system-dependent exit information about// the process. Convert it to the appropriate underlying// type, such as [syscall.WaitStatus] on Unix, to access its contents.func ( *ProcessState) () any {return .sys()}// SysUsage returns system-dependent resource usage information about// the exited process. Convert it to the appropriate underlying// type, such as [*syscall.Rusage] on Unix, to access its contents.// (On Unix, *syscall.Rusage matches struct rusage as defined in the// getrusage(2) manual page.)func ( *ProcessState) () any {return .sysUsage()}
![]() |
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. |