See Also edit
Description edit
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 [1] 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.tcllavapsEF 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 <phil@slug.org> # 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