processes

A process is a program running under the auspices of an operating system.

See Also

concurrency concepts
inter-process comunication

Description

In Tcl, exec and open |... invoke new processes.


To obtain a ps(1)-like list of Windows processes, use pslist from pstools. [tlist?]

Alternatively, use get_process_ids [L1 ] from the TWAPI package.


To obtain a ps(1)-like list of Solaris or Linux processes, use procfs.tcl from http://inferno.slug.org/tarballs/procfs.tcl

lavaps

EF The link is dead but available via the Wayback machine, copying it here below:

## ******************************************************** 
##
## Name: procfs.tcl 
##
## Description:
## Tcl package for parsing the /proc filesystem on Linux
## and Solaris.
##
## Parameters:
##
## Usage:
##
## On Linux, the proc filesystem is text, so easily parsed:
##
## /proc/<pid>/stat contains:
##
## pid      (fname)  state       ppid     pgrp
## session   tty     tpgid       flags    minflt
## cminflt   majflt  cmajflt     utime    stime
## cutime    cstime  pri         nice     0
## itreadv   stt     vsz         rss      rlim
## startcode endcode startstack  kstkesp  kstkeip
## signal    blocked sigignore   sigcatch wchan
## nswap     cnswap  exit_signal processor 
##
## Except that there seems to be a mystery field somewhere
## between rss and processor.
##
## state: R - run S - sleep D - disk Z - zombie T - stop W - swap
##
## On Solaris, the parse the /proc/<pid>/psinfo files are binary:
##
## /* process ps(1) information file.  /proc/<pid>/psinfo */
##
## typedef struct psinfo {
##   int     pr_flag;   /* process flags               */
##   int     pr_nlwp;   /* number of lwps in process   */
##   pid_t   pr_pid;    /* unique process id           */
##   pid_t   pr_ppid;   /* process id of parent        */
##   pid_t   pr_pgid;   /* pid of process group leader */
##   pid_t   pr_sid;    /* session id                  */
##   uid_t   pr_uid;    /* real user id                */
##   uid_t   pr_euid;   /* effective user id           */
##   gid_t   pr_gid;    /* real group id               */
##   gid_t   pr_egid;   /* effective group id          */
##   uintptr_t pr_addr; /* address of process          */
##   size_t  pr_size;   /* size of process image in Kbytes */
##   size_t  pr_rssize; /* resident set size in Kbytes     */
##   size_t  pr_pad1;   /* padding                         */
##   dev_t   pr_ttydev; /* controlling tty device (or PRNODEV) */
##   /* The following percent numbers are 16-bit binary */
##   /* fractions [0 .. 1] with the binary point to the */
##   /* right of the high-order bit (1.0 == 0x8000)     */
##   ushort_t    pr_pctcpu; /* % of recent cpu time used by all lwps */
##   ushort_t    pr_pctmem; /* % of system memory used by process    */
##   timestruc_t pr_start;  /* process start time, from the epoch    */
##   timestruc_t pr_time;   /* usr+sys cpu time for this process     */
##   timestruc_t pr_ctime;  /* usr+sys cpu time for reaped children  */
##   char  pr_fname[PRFNSZ];   /* name of execed file (16 chars)     */
##   char  pr_psargs[PRARGSZ]; /* initial 80 characters of arg list  */
##   int   pr_wstat;      /* if zombie, the wait() status          */
##   int   pr_argc;       /* initial argument count                */
##   uintptr_t pr_argv;   /* address of initial argument vector    */
##   uintptr_t pr_envp;   /* address of initial environment vector */
##   char  pr_dmodel;     /* data model of the process */
##   char  pr_pad2[3];    /* 3 bytes padding           */
##   taskid_t pr_taskid;  /* task id                   */
##   projid_t pr_projid;  /* project id                */
##   int   pr_filler[5];  /* reserved for future use   */
##   lwpsinfo_t pr_lwp;   /* information for representative lwp */
## } psinfo_t;
##
## Comments:
##
## ******************************************************** 

;#barecode
# copyright 2003 by Phil Ehrens <[email protected]>
# conditions of GNU General Public License apply to this
# code. see http://www.gnu.org/licenses/
package provide procfs 1.0
namespace eval ps {}
;#end

## ******************************************************** 
##
## Name: ps::solaris 
##
## Description:
##
## Parameters:
##
## Usage:
##
## Comments:
##

proc ps::solaris { pid } {
     if { [ catch {
        set file /proc/${pid}/psinfo
        set now [ clock seconds ]
        set usr [ file attributes $file -owner ]
        set fid [ open $file r ]
        fconfigure $fid -encoding binary -translation binary
        set numinfo [ read $fid 88 ]
        set fname   [ read $fid 16 ]
        set args    [ read $fid 80 ]
        set more    [ read $fid 16 ]
        ::close $fid
     
        ;## the meaty part
        binary scan $numinfo IIIIIIIIIIIIIIISSI \
           . . pid ppid . . ruser . . . . vsz rsz . . pcpu pmem stt
     
        ;## try to make sense out of pr_wstat
        binary scan $more I state
        if { $state == 0 } {
           set state R
        } else {
           set state Z
        }
     
        regsub -all \x00 $fname {} fname
        regsub -all \x00 $args  {} args
        set etime [ expr { $now - $stt } ]
        set pmem [ format %.2f [ expr { $pmem / 327.67 } ] ]
        set pcpu [ format %.2f [ expr { $pcpu / 327.67 } ] ]
     } err ] } {
        return -code error "[ myName ]: $err"
     }
     return [ list $pid $state $usr $vsz $rsz $pcpu $pmem $etime $fname $args ]
}
## ******************************************************** 

## ******************************************************** 
##
## Name: ps::linux
##
## Description:
## Parses /proc/<pid>/stat
## Parameters:
##
## Usage:
##
## Comments:
## Funny, we seem to have some disagreement about the
## fields with vsz rsz, and state... not a good sign.

proc ps::linux { pid } {
     if { [ catch {
        set file /proc/${pid}/stat
        ;## we need the total memory on the box for pmem
        set fid  [ open /proc/meminfo r ]
        set mem  [ read $fid 1024 ]
        ::close $fid
        regexp {Mem:\s+(\S+)} $mem -> mem
   
        ;## we need the uptime (returned in seconds)
        set fid  [ open /proc/uptime r ]
        set uptime [ read $fid 80 ]
        ::close $fid
        set uptime [ lindex $uptime 0 ]
        ;## convert to jiffies at 100/sec
        set uptime [ expr { $uptime * 100 } ]
        
        set usr [ file attributes $file -owner ]
        ;## here's where we open the /proc/<pid>/stat file
        set fid   [ open $file r ]
        set data  [ read $fid 1024 ]
        ::close $fid
        set data  [ split $data ]
        set pid   [ lindex $data 0 ]
        set fname [ lrange $data 1 end-38 ]
        ;## R-unning, Z-ombie, ...
        set state [ lindex $data end-37 ]
        regexp {\((.+)\)} $fname -> fname
        set vsz   [ lindex $data end-17 ]
        set rsz   [ lindex $data end-16 ]
        set rsz   [ expr { $rsz * 4096 } ]
        ;## this is the time in jiffies that the process was
        ;## started after system boot...
        set etime [ lindex $data end-18 ]
        set etime [ expr { $uptime - $etime } ]
        ;## number of jiffies it has been scheduled...
        set utime [ lindex $data end-25 ]
   
        ;## and the calculated values
        set pcpu  [ expr { (double($utime) / double($etime)) * 100 } ]
        set etime [ expr { int($etime / 100) } ]
        set pmem  [ expr { (double($vsz) / double($mem)) * 100 } ]
        set pcpu  [ format %.2f $pcpu ]
        set pmem  [ format %.2f $pmem ]
        set vsz   [ expr { $vsz / 1000 } ]
        set rsz   [ expr { $rsz / 1000 } ]
     } err ] } {
        return -code error "[ myName ]: $err"
     }
     return [ list $pid $state $usr $vsz $rsz $pcpu $pmem $etime $fname $fname ]
}
## ******************************************************** 

## ******************************************************** 
##
## Name: ps::procfsValidate
##
## Description:
## Since there is always the possibility that the proc
## filesystem structure will change, it would be nice
## to be able to validate the state of the procfs that
## is being queried.
## 
## Parameters:
##
## Usage:
##
## Comments:
## Pending investigation of usable validation modes. I can
## validate the Solaris procfs.h by version, but the linux
## procfs.h does not use a version id.
##

proc ps::procfsValidate { args } {
     
     if { [ catch {
        set os [ lindex [ ps::os ] 0 ]
        set fname /usr/include/sys/procfs.h
        set sol_rx {pragma\s+ident.+procfs.h\s+([\d\.]+)}
        set mtime [ file mtime $fname ]
        set size [ file size $fname ]
        set fid [ open $fname r ] 
        set data [ read $fid $size ]
        close $fid
        switch -exact -- $os {
           solaris {
                     regexp $sol_rx $data -> version
                   }
           linux   {
                     set version unknown
                   }
           default {
                     set version unknown
                   }
        }
        set retval [ list $size $mtime $version ]
     } err ] } {
        return -code error "[ myName ]: $err"
     }
     return $retval
}
## ******************************************************** 

## ******************************************************** 
##
## Name: ps::os
##
## Description:
## Get the OS and the filename under /proc/<pid> that we
## are going to parse.
##
## Parameters:
##
## Usage:
##
## Comments:
##

proc ps::os { args } {
     set os $::tcl_platform(os)
     
     switch -exact $os {
          SunOS {
                 set os solaris
                 set filename psinfo
                }
          Linux {
                 set os linux
                 set filename stat
                }
        default {
                 return -code error "unsupported OS: $os"
                }
     }
     return [ list $os $filename ]
}
## ******************************************************** 

## ******************************************************** 
##
## Name: ps::type
##
## Description:
## Discriminate between pid's, executable programs, and
## users.
##
## Parameters:
##
## Usage:
##
## Comments:
##

proc ps::type { program } {
     if { [ catch {
           if { [ regexp {^\d+$} $program ] } {
           set type pid
        } elseif { [ string length [ auto_execok $program ] ] } {
           set type prog
        } elseif { [ file exists /home/$program ] } {
           set type user
        } else {
           set fid [ open /etc/passwd r ]
           set data [ read $fid [ file size /etc/passwd ] ]
           ::close $fid
           set data [ split $data "\n" ]
           set type unknown
           foreach user $data {
              set user [ lindex [ split $user : ] 0 ]
              if { [ string equal $program $user ] } {
                 set type user
                 break
              }
           }
        }
     } err ] } {
           return -code error "[ myName ]: $err"
        }
        return $type
}
## ******************************************************** 

## ******************************************************** 
##
## Name: ps::ps
##
## Description:
## 
## Parameters:
##
## Usage:
##
##   to see the pids of all rxvt processes:
##
##    ps::ps rxvt
##
##   to see the process info for pid 123:
##
##    ps::ps 123
##
##   a user may be specified as well
##
## Comments:
##

proc ps::ps { program { user all } } {
     if { [ catch { 
        set pids   [ list ]
        set noperm [ list ]
     
        foreach [ list os filename ] [ ps::os ] { break }
     
        set type [ ps::type $program ]
     
        foreach file [ ps::procfiles $user ] {
        
           foreach [ list owner file ] $file { break }
        
           set pid [ lindex [ split $file / ] end-1 ]
           if { [ file readable $file ] } {
           
              set data [ ps::$os $pid ]
           
              if { [ string equal all $program ] && \
                   [ string equal all $user ] } {
                 lappend pids $data
                 continue
              }     
           
              ;## return all data for the pid
              if { [ string equal $program $pid ] } {
                 set pids $data
                 break
              ;## collect all pids for the given program name   
              } elseif { ! [ string equal self $pid ] } {
                 set fname [ lindex $data end-1 ]
                 if { [ string length $program ] < 9 } {
                    if { [ string equal $fname $program ] } {
                       lappend pids [ lindex $data 0 ]
                    }
                 } elseif { [ string length $fname ] && \
                            [ string match $fname* $program ] } {
                    lappend pids [ lindex $data 0 ]
                 }
              }
           
           ;## what if file is owned by 'program' and is not
           ;## readable?   
           } elseif { [ string equal user $type ] } {
              lappend noperm $file
           }
        }
     
     } err ] } {
        return -code error "[ myName ]: $err"
     }
     return $pids
}
## ******************************************************** 
 
## ******************************************************** 
##
## Name: ps::userpids
##
## Description:
## Return all process i.d.'s owned by a user.
##
## Parameters:
##
## Usage:
##
## Comments:
##

proc ps::userpids { user } {
     
     if { [ catch {
        set pids [ list ]
        set files [ ps::procfiles $user ]
        foreach file $files {
           foreach [ list owner file ] $file { break }
           set pid [ lindex [ split $file / ] end-1 ]
           lappend pids $pid
        }
     } err ] } {
        return -code error "[ myName ]: $err"
     }
     return $pids
}
## ******************************************************** 

## ******************************************************** 
##
## Name: ps::procfiles
##
## Description:
## Return the full list of proc files on the box, or a list
## of all proc files owned by a user.
##
## Parameters:
##
## Usage:
##
## Comments:
## Typically takes 250 usec * number of processes running.

proc ps::procfiles { { user all } } {
     
     if { [ catch {
        set temp [ list ]
        
        foreach [ list os filename ] [ ps::os ] { break }
        
        set files [ glob /proc/*/$filename ]
        
           set files [ lsort -dictionary $files ]
           
        if { [ string match /proc/self* [ lindex $files end ] ] } {
           set files [ lrange $files 0 end-1 ]
        }
        
        if { [ string length $user ] } {
           foreach file $files {
              if { [ file exists $file ] } {
                    set owner [ file attributes $file -owner ]
                 if { [ string equal all $user ] || \
                      [ string equal $user $owner ] } {
                    lappend temp [ list $owner $file ]
                 }
              }
              set files $temp
           }
        }                 
     } err ] } {
        return -code error "[ myName ]: $err"
     }
     return $files
}
## ******************************************************** 

## ******************************************************** 
##
## Name: ps::procids
##
## Description:
##
## Parameters:
##
## Usage:
##
## Comments:
##

proc ps::procids { user } {
     
     if { [ catch {
        set pids [ list ]
        set files [ ps::procfiles $user ]
        foreach file $files {
           foreach [ list owner file ] $file { break }
           lappend pids [ lindex [ split $file / ] end-1 ]
        }
     } err ] } {
        return -code error "[ myName ]: $err"
     }
     return $pids
}
## ******************************************************** 

## ******************************************************** 
##
## Name: ps::zombies
##
## Description:
##
## Parameters:
##
## Usage:
##
## Comments:
##

proc ps::zombies { program } {
     
     if { [ catch {
        set data    [ list ]
        set zombies [ list ]
        if { [ string equal all $program ] } {
           set type user
        } else {   
           set type [ ps::type $program ]
        }
        
        foreach [ list os filename ] [ ps::os ] { break }
        
        switch -exact -- $type {
           pid {
                 set pids $program
               }
          prog {
                 set pids [ ps::ps $program ]
               }
          user {
                 set pids [ ps::procids $program ]
               }
       unknown {
                 return "argument of unknown type: '$program'"
               }
        }

        foreach pid $pids {
              set file /proc/${pid}/$filename
           if { [ file readable $file ] } {
                    set data [ ps::$os $pid ]
              set state [ lindex $data 1 ]
              if { [ string equal Z $state ] } {
                 set owner [ lindex $data 2 ]
                 lappend zombies [ list $owner $data ]
              }
           }
        }

     } err ] } {
        return -code error "[ myName ]: $err"
     }
     return $zombies
}
## ******************************************************** 

## ******************************************************** 
##
## Name: ps::kill
##
## Description:
##
## Parameters:
##
## Usage:
##
## Comments:
##

proc ps::kill { program } {
     
     if { [ catch {
        foreach pid [ ps::ps $program ] {
           puts stderr "calling kill -9 $pid ($program)"
           catch { ::exec kill -9 $pid }
        }
        after 1000
        if { [ catch {
           set undead [ ps::zombies $program ]
           if { [ llength $undead ] } {
              put stderr "zombie ${program}'s: '$undead'"
           }
        } err ] } {
           puts stderr "no zombie ${program}'s found."
           set undead [ ps::zombies all ]
           if { [ llength $undead ] } {
              puts stderr "other zombies: '$undead'"
           }
        }   
        puts stderr DONE!
     } err ] } {
        return -code error "[ myName ]: $err"
     }
}
## ******************************************************** 

## ******************************************************** 
##
## Name: ps::self 
##
## Description:
## Returns lots of useful and ACCURATE information about
## the current process.
##
## Parameters:
##
## Usage:
##
## Comments:
## typical runtime is 2 ms.

proc ps::self { } {
     
     if { [ catch {
        set self [ pid ]
        set me $::argv0
       
        set os [ lindex [ ps::os ] 0 ]
       
        set data [ ps::$os $self ]
        foreach \
         [ list pid state usr vsz rsz pcpu pmem etime fname args ] \
         $data { break }
        set etime [ ps::duration $etime ]
        
        set retval [ list $me $self $pcpu $pmem $vsz $rsz $etime ]
        
     } err ] } {
        return -code error "[ myName ]: $err"
     }
     return $retval
}
## ******************************************************** 

## ******************************************************** 
##
## Name: ps::children
##
## Description:
## Return ps info for all children of current process.
##
## Parameters:
##
## Usage:
##
## Comments:
## Need ppid for this to work.
##

proc ps::children { args } {
     
     if { [ llength $args ] == 1 } { 
        set args [ lindex $args 0 ] 
     }
     
     if { [ catch {
         return -code error UNIMPLEMENTED!
     } err ] } {
        return -code error "[ myName ]: $err"
     }
}
## ******************************************************** 

## ******************************************************** 
##
## Name: ps::all
##
## Description:
## Return a full ps of all procs on the host.
##
## Parameters:
##
## Usage:
##
## Comments:
## Roughly 2 ms per process.
##

proc ps::all { args } {
     
     if { [ catch {
        set os [ lindex [ ps::os ] 0 ]
           set files [ ps::procfiles {} ] 
           foreach file $files {
              if { [ file readable $file ] } {
                    lappend data \
                       [ ps::$os [ lindex [ split $file / ] end-1 ] ]
              }
           }
        } err ] } {
        return -code error "[ myName ]: $err"
     }
        return $data
}
## ******************************************************** 

## ******************************************************** 
##
## Name: ps::duration
##
## Description:
## Apply to etime value to get 'human' time.
## Parameters:
##
## Usage:
##
## Comments:
##

proc ps::duration { secs } {
     set timeatoms [ list ]
     if { [ catch {
        foreach div { 86400 3600 60 1 } \
                mod { 0 24 60 60 } \
               name { day hr min sec } {
           set n [ expr {$secs / $div} ]
           if { $mod > 0 } { set n [ expr {$n % $mod} ] }
           if { $n > 1 } {
              lappend timeatoms "$n ${name}s"
           } elseif { $n == 1 } {
             lappend timeatoms "$n $name"
           }
        }
        set timeatoms [ join $timeatoms ]
        if { ! [ string length $timeatoms ] } {
           set timeatoms [ list 0 sec ]
        }
     } err ] } {
        return -code error "duration: $err"
     }
     return $timeatoms
}
## ******************************************************** 

## ******************************************************** 
##
## Name: ps::server
##
## Description:
## Canned server definition so this library can be used
## as a server on a remote host.
##
## Parameters:
##
## Usage:
##
## Comments:
##

proc ps::server { port } {
     
     if { [ catch {
           ps::standalone
        set cid [ socket -server ps::servercfg $port ]
     } err ] } {
        puts stderr "[ myName ]: $err"
     }
}
## ******************************************************** 

## ******************************************************** 
##
## Name: ps::servercfg
##
## Description:
##
## Parameters:
##
## Usage:
##
## Comments:
##

proc ps::servercfg { cid addr port } {
     if { [ catch {  
        fileevent  $cid readable "ps::serverhandler $cid"
     } err ] } {
        return -code error "[ myName ]: $err"
     }
}
## ******************************************************** 

## ******************************************************** 
##
## Name: ps::serverhandler
##
## Description:
##
## Parameters:
##
## Usage:
##
## Comments:
##

proc ps::serverhandler { cid } {
     if { [ catch {
        ::gets $cid cmd
        
           if { ! [ regexp {^ps::[a-z]+(\s+\S+)?$} $cmd ] } {
              set err "invalid command received: '$cmd'"
                 return -code error $err
           }
           
        catch { eval $cmd } reply
        ::puts $cid $reply
        ::close $cid
     } err ] } {
        catch { ::close $cid }
        return -code error "[ myName ]: $err"
     }
}
## ********************************************************

## ******************************************************** 
##
## Name: ps::standalone
##
## Description:
## Some helper functions that may not be defined outside
## of my API's.
##
## call this procedure once before using any of the above
## functions.
##
## Parameters:
##
## Usage:
##
## Comments:
##

proc ps::standalone { args } {
     
     if { [ catch {
        proc myName { args } {
             return [ lindex [ info level -1 ] 0 ]
        }
        proc addLogEntry { args } {
             puts "[ uplevel myName ]: $args"
        }
           proc bgerror { args } {
             puts stderr $::errorInfo
        }
     } err ] } {
        return -code error "[ myName ]: $err"
     }
}
## ******************************************************** 

In Alpha, processes is a command that returns (drumroll) a list of descriptions of running processes (well, rather applications). Excerpt from join [processes] \n:

 loginwindow lgnw APPL 5793 0000000000020001 /System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow
 Dock dock APPL 8123 0000000000060001 /System/Library/CoreServices/Dock.app/Contents/MacOS/Dock
 SystemUIServer syui APPL 8189 0000000000080001 /System/Library/CoreServices/SystemUIServer.app/Contents/MacOS/SystemUIServer
 Finder MACS FNDR 8190 00000000000a0001 /System/Library/CoreServices/Finder.app/Contents/MacOS/Finder
 iTunesHelper ithp APPL 9456 00000000000c0001 /Applications/iTunes.app/Contents/Resources/iTunesHelper.app/Contents/MacOS/iTunesHelper
 iCalAlarmScheduler ???? APPL 9466 00000000000e0001 /Applications/iCal.app/Contents/Resources/iCalAlarmScheduler.app/Contents/MacOS/iCalAlarmScheduler
 {Acrobat Reader 5.0} CARO APPL 102108211 0000000006e40001 {/Applications/Acrobat Reader 5.0/Contents/MacOS/Acrobat Reader 5.0}

Newly released procfs is an interface to the proc pseudo-filesystem on linux. The interface comes with a test suite, is not complete, but yet a good start. Any volunteer? EF