Your Perl script is a program that resides on disk. When the program is placed in memory and starts execution, it is called a process. Each process has a number of attributes that are inherited from its parent, the calling process. Perl has a number of functions that will allow you to retrieve the information about the process. Before examining these functions, a short discussion about processes may help you to understand (or recall) the purpose of some of Perl's system calls.
Every process has a unique process ID, a positive integer called the pid. Every process has a parent except process 0, the swapper. The first process init, pid 1, is the ancestor of all future processes, called descendants, or more commonly, child processes.
In Figure 18.5, the Perl process is a descendant of the shell (sh).
Each process also belongs to a process group, a collection of one or more processes used for job control and signal handling. Each process group also has a unique pid and a process leader. When you log on, the process group leader may be your login shell. Any process created from your shell will be a member of this process group. The terminal opened by the process group leader is called the controlling terminal, and any processes it spawns inherit it. Any signals sent to the process will be sent to all processes in the group. That is why, when you press <Ctrl>-c, the process you are running and any of its children will terminate. Perl provides functions to obtain the process group ID and to set the process group.
When a process is created, it is assigned four numbers indicating who owns the process. They are the real and effective user ID and the real and effective group ID. The user ID, called the real uid, is a positive integer that is associated with your login name. The real uid is the third field in the /etc/passwd file. When you log on, the first process created is called the login shell, and it is assigned the user ID. Any processes spawned from the shell also inherit this uid. Any process running with the uid of zero is called a root, or superuser, process with special privileges.
There is also a group ID number, called the real gid, which associates a group with your login name. The default gid is the fourth field in the password file, and it is also inherited by any child process. The system administrator can allow users to become members of other groups by assigning entries in the /etc/group file.
The following is an entry from the passwd file, illustrating how the uid and gid values are stored (fields are separated by colons).
(Entry from /etc/passwd)
john:aYD17IsSjBMyGg:9495:41:John Doe:/home/dolphin/john:/bin/ksh Explanation(The Fields)
|
The effective uid (euid) and effective guid (guid) of a process are normally set to the same number as the real uid and real gid of the user who is running the process. UNIX determines what permissions are available to a process by the effective uid and gid. If the euid or guid of a file is changed to that of another owner, when you execute a program, you essentially become that owner and get his access permissions. Programs in which the effective uid or effective gid have been set are called set user ID programs, or setuid programs. When you change your password, the /bin/passwd program has a setuid to root, giving you the privilege to change your password in the passwd file, which is owned by root.
The process model for Windows differs from UNIX systems, and since Perl was originally designed for UNIX, a number of library routines were added to the standard Perl library to accommodate the Windows world. The Win32 directory (C:/Perl/lib/Win32) is a Windows-specific directory that comes with Windows versions of Perl and contains a number of modules for creating, suspending, resuming, and killing processes. The Win32::Process module contains a number of functions to manipulate processes. Here is a listing from the Win32 directory:
AuthenticateUser.pm Internet.pm Registry.pm ChangeNotify.pm Mutex.pm Semaphore.pm Client.pl NetAdmin.pm Server.pl Clipboard.pm NetResource.pm Service.pm Console.pm ODBC.pm Shortcut.pm Event.pm OLE Sound.pm EventLog.pm OLE.pm Test.pl File.pm PerfLib.pm TieRegistry.pm FileSecurity.pm Pipe.pm WinError.pm IPC.pm Process.pm test-async.pl
When you log on, your shell program inherits a set of environment variables initialized by either the login program or one of shell's startup files (.profile or .login). These variables contain useful information about the process, such as the search path, the home directory, the user name, and the terminal type. The information in environment variables, once set and exported, is inherited by any child processes that are spawned from the process (parent) in which they were initialized. The shell process will pass the environment information on to your Perl program.
The special associative array %ENV contains the environment settings. If you change the value of an environment setting in your Perl script, it is set for this process and any of its children. The environment variables in the parent process, normally the shell, will remain untouched.
As discussed in Chapter 10, "Getting a Handle on Files," processes can be opened in Perl via either an input or output filehandle. For example, if you want to see all the currently running processes on your machine, you could create a filehandle for the UNIX ps command. (See Chapter 10 for details. See also "The system Function" on page 750.)
The getlogin function returns the current login from /etc/utmp. If null is returned from getlogin, use getpwuid. The getpwuid function takes the uid of the user as an argument and returns an entry from the password file associated with that uid.
The $< variable evaluates to the real uid of this process.
Perl provides some special variables that store information about the Perl process executing your script. If you want to make your program more readable, you can use the English module in the standard Perl library to represent these variables in English.
$$ | The process ID of the Perl program running this script |
$< | The real uid of this process |
$> | The effective uid of this process |
$( | The real gid of this process |
$) | The effective gid of this process |
Each process on the system is identified by its process identification number (pid), a positive integer. The special variable $$ holds the value of the pid for this process. This variable is also used by the shell to hold the process ID number of the current process.
The getppid function returns the process ID of the parent process.
(The Script) 1 print "The pid of this process is $$\n"; 2 print "The parent pid of this process is ", getppid,"\n"; (Output) 1 The pid of this process is 3304 2 The parent pid of this process is 2340 (At the Command Line) 3 $ echo $$ 2340 Explanation
|
The pgrp function returns the current group process for a specified pid. Without an argument or with an argument of 0, the process group ID of the current process is returned.
Formatgetpgrp(PID); getpgrp PID; getpgrp; Example 18.38.
|
The kernel maintains the scheduling priority selected for each process. Most interactive and short-running jobs are favored with a higher priority. The UNIX nice command allows you to modify the scheduling priority of processes (BSD, pre-System V). On moderately or heavily loaded systems, it may be to your advantage to make CPU-intensive jobs run slower so that jobs needing higher priority get faster access to the CPU. Those jobs that don't hog the processor are called nice.
The nice value is used in calculating the priority of a process. A process with a positive nice value runs at a low priority, meaning that it receives less than its share of the CPU time. A process with a negative nice value runs at a high priority, receiving more than its share of the processor. The nice values range from –20 to 19. Most processes run at priority zero, balancing their access to the CPU. (Only the superuser can set negative nice values.)
The following functions, getpriority and setpriority, are named for the corresponding system calls, found in Section 2 of the UNIX man pages.
The getpriority function returns the current priority (nice value) for a process, a process group, or a user. Not all systems support this function. If not implemented, getpriority produces a fatal error. WHICH is one of three values: 0 for the process priority, 1 for the process group priority, and 2 for the user priority. WHO is interpreted relative to the process identifier for the process priority, process group priority, or user priority. A value of zero represents the current process, process group, or user.
Formatgetpriority(WHICH, WHO); Example 18.39.
|
The setpriority function sets the current priority (nice value) for a process, a process group, or a user. It modifies the scheduling priority for processes. If the setpriority system call is not implemented on your system, setpriority will return an error.
WHICH is one of three values: 0 for the process priority, 1 for the process group priority, and 2 for the user priority. WHO is interpreted relative to the process identifier for the process priority, process group priority, or user priority. A value of zero represents the current process, process group, or user. NICEVALUE is the nice value. A low nice value raises the priority of the process and a high nice value decreases the priority of the process. (Confusing!)
Unless you have superuser privileges, you cannot use a negative nice value. Doing so will not change the current nice value.
Formatsetpriority(WHICH, WHO, NICEVALUE); Example 18.40.
|
The following functions iterate through the /etc/passwd file and retrieve information from that file into an array. These functions are named for the same functions found in the system library (Section 3 of the UNIX manual) and perform the same tasks. If you are interested in obtaining information about the /etc/group file, the Perl functions getgrent, getgrgid, and getgrnam all return a four-element array with information about group entries. A description of these functions can be found in the UNIX manual pages. Here is an example of an /etc/passwd file:
root:YhTLR4heBdxfw:0:1:Operator:/:/bin/csh nobody:*:65534:65534::/: sys:*:2:2::/:/bin/csh bin:*:3:3::/bin uucp:*:4:8::/var/spool/uucppublic: news:*:6:6::/var/spool/news:/bin/csh sync::1:1::/:/bin/sync ellie:aVD17TSsBMfYg:9496:40:Ellie Shellie:/home/jody/ellie:/bin/ksh
Windows 2000 and NT store information about users in a binary database called SAM (Security Accounts Manager), part of the Registry. Because the data is stored in binary format, normal Perl read operations won't work. It is better to use the Win32 extensions to get user information. Win32::NetAdmin is bundled with ActiveState under \perl\site\lib\win32. (See Table 18.9.) A user account can be manipulated with two functions of this module: UserGetAttributes() and UserSetAttributes. Another good extension is David Roth's Win32::AdminMisc found at www.roth.net.
Win32::NetAdmin::UserGetAttributes($Machine, $UserName, $Password, $PasswordAge, $Privilege, $Homedir, $Comment, $Flags, $ScriptPath); |
Win32::NetAdmin::UserSetAttributes( $Machine, $UserName, $Password, $PasswordAge, $Privilege, $Homedir, $Comment, $Flags, $ScriptPath); |
The Win 32 net.exe command also displays information about the user and the system.
Code View: 1 C:\ net help The syntax of this command is: NET HELP command -or- NET command /HELP Commands available are: NET ACCOUNTS NET HELP NET SHARE NET COMPUTER NET HC:\ELPMSG NET START NET CONFIG NET LOCALGROUP NET STATISTICS NET CONFIG SERVER NET NAME NET STOP NET CONFIG WORKSTATION NET PAUSE NET TIME NET CONTINUE NET PRINT NET USE NET FILE NET SEND NET USER NET GROUP NET SESSION NET VIEW NET HELP SERVICES lists the network services you can start. NET HELP SYNTAX explains how to read NET HELP syntax lines. NET HELP command | MORE displays Help one screen at a time. 2 C:\ net user User accounts for \\HOMEBOUND ----------------------------------------------------------------- Administrator Ellie Quigley Guest SQLAgentCmdExec The command completed successfully. |
1 use Win32::NetAdmin qw(GetUsers UserGetAttributes) ; 2 GetUsers("", FILTER_NORMAL_ACCOUNT,\%hash)or die; 3 foreach $key(sort keys %hash){ print "$key\n"; } (Output) Administrator Ellie Quigley Guest SQLAgentCmdExec |
Encrypted passwords cannot be transferred from UNIX to Win32 systems and vice versa. They are cryptologically incompatible. To manage passwords, use the Win32::AdminMisc or the Win32::NetAdmin module extension.
Win32::AdminMisc::UserCheckPassword($Machine, $User, $Password) Win32::AdminMisc::SetPassword($Machine | $Domain), $User, $NewPassword); Win32::AdminMisc::UserChangePassword($Machine | $Domain), $User, $OldPassword, $NewPassword); Win32::NetAdmin::UserChangePassword(($Machine | $Domain), $User, $OldPassword, $NewPassword); |
For Windows users, the following functions for obtaining group and user information have not been implemented by ActiveState as of this printing:
endgrent(), endpwent(), getgrent(), getgrgid(), getgrnam(), getpwent(), getpwnam(), getpwuid(), setgrent(), setpwent()
The getpwent function retrieves information from the /etc/passwd file. The return value from getpwent is a nine-element array consisting of:
Login name
Encrypted password
User ID
Group ID
Quota
Comment
Gcos (user information)
Home directory
Login shell
Format($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell )=getpwent; Example 18.43.
|
The getpwnam function takes the user name as an argument and returns a nine-element array corresponding to that user's name field in the /etc/passwd file.
Formatgetpwnam(loginname); Example 18.44.
|
The getpwuid function takes a numeric user ID (uid) as an argument and returns a nine-element array corresponding to that user's uid entry in the /etc/passwd file.
Formatgetpuid(UID) Example 18.45.
|
When working in a computer environment, programs often need to obtain and manipulate the current date and time. UNIX systems maintain two types of time values: calendar time and process time.
The calendar time counts the number of seconds since 00:00:00 January 1, 1970, UTC (Coordinated Universal Time, which is a new name for Greenwich Mean Time).
The process time, also called CPU time, measures the resources a process utilizes in clock time, user CPU time, and system CPU time. The CPU time is measured in clock ticks per second.
Perl has a number of time functions that interface with the system to retrieve time information.
The times function returns a four-element array consisting of the CPU time for a process, measured in:
User time—Time spent executing user's code
System time—Time spent executing system calls
Children's user time—Time spent executing all terminated child processes
Children's system time—Time spent executing system calls for all terminated child processes
Format($user, $system, $cuser, $csystem) = times; Example 18.46.
|
The time function returns the number of nonleap seconds since January 1, 1970, UTC. Its return value is used with the gmtime and localtime functions to put the time in a human-readable format. The stat and utime functions also use the time functions when comparing file modification and access times.
The gmtime function converts the return value of the time function to a nine-element array consisting of the numeric values for the UTC. If you are a C programmer, you will recognize that these values are taken directly from the tm structure found in the header file /usr/include/time.h. (See Table 18.11.)
List Element | Meaning |
---|---|
$sec | Seconds after the minute: [0, 59] |
$min | Minutes after the hour: [0, 59] |
$hour | Hour since midnight: [0, 23] |
$monthday | Day of the month: [1, 31] |
$month | Months since January: [0, 11] |
$year | Years since 1900 |
$weekday | Days since Sunday: [0, 6] |
yearday | Days since January 1: [0, 365] |
isdaylight | Flag for daylight savings time |
Formatgmtime(EXPR); gmtime EXPR; ($sec, $min, $hour, $monthday, $month, $year, $weekday, $yearday, $isdaylight)=gmtime; |
(The Script) #!/bin/perl 1 ($sec, $min, $hour, $monthday, $month, $year, $weekday, $yearday, $isdaylight) = gmtime; 2 print "The weekday is $weekday and the month is $month.\n"; 3 print "The time in California since midnight is ", `date "+%H:%M"`; 4 print "The Coordinated Univeral Time is $hour:$min since midnight\n"; 5 print "Daylight saving is in effect.\n" if $isdaylight; (Output) 2 The weekday is 2 and the month is 6. 3 The time in California since midnight is 20:35. 4 The Coordinated Univeral Time is 3:35 since midnight. 5 <no output> Explanation
|
The localtime function converts the UTC to a nine-element array with the local time zone.
Formatlocaltime(EXPR); localtime EXPR; ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst)=localtime(time); Example 18.48.
|
Code View: (The Script) #!/bin/perl 1 ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst)= localtime(time); 2 % weekday=( "0"=>"Sunday", "1"=>"Monday", "2"=>"Tuesday", "3"=>"Wednesday", "4"=>"Thursday", "5"=>"Friday", "6"=>"Saturday", ); if ( $hour > 12 ){ 3 print "The hour is ", $hour - 12 ," o'clock.\n"; } else { print "The hour is $hour o'clock.\n"; } 4 print qq/Today is $weekday{"$wday"}.\n/; # day starts at zero 5 print "It is ",$mon + 1, "/$mday/" , 1900+$year,".\n"; 6 print "The isdst is $isdst.\n"; (Output) 3 The hour is 1 o'clock. 4 Today is Wednesday. 5 It is 5/30/2007. 6 The isdst is 1. Explanation
|
What happens when your Perl program starts executing? Here is a brief sketch of what goes on. Normally, the Perl program is executed from the shell command line. You type the name of your script (and its arguments) and then press the Enter key. At that point, the shell starts working. It first creates (forks) a new process called the child process. The child is essentially a copy of the shell that created it. There are now two processes running, the parent and child shells. After the child process is created, the parent shell normally sleeps (waits) while its child process gets everything ready for your Perl program; that is, handles redirection (if necessary) pipes, background processing, etc. When the child shell has completed its tasks, it then executes (execs) your Perl program in place of itself. When the Perl process completes, it exits (exits), and its exit status is returned to the waiting parent process, the shell. The shell wakes up, and a prompt appears on the screen. If you type in a UNIX command, this whole process is repeated.
It's conceivable that your Perl program may want to start up a child process to handle a specific task; for example, a database application or a client/server program.
The fork function is used to create processes on UNIX systems. The fork function is called once and returns twice. It creates a duplicate of the parent (calling) process. The new process is called the child process. The child process inherits its environment, open files, real and user IDs, masks, current working directory, signals, and so on. Both processes, parent and child, execute the same code, starting with the instruction right after the fork function call.
The fork function lets you differentiate between the parent and child because it returns a different value to each process. It returns 0 to the child process and the pid of the child to the parent process. It is not guaranteed which process will execute first after the call to the fork function.
Normally, the wait, exec, and exit functions work in conjunction with the fork function so that you can control what both the parent and the child are doing. The parent, for example, waits for the child to finish performing some task, and after the child exits, the parent resumes where it left off.
Figure 18.6 illustrates how the UNIX shell uses the fork system call to create a new process. After you type the name of your Perl program at the shell prompt, the shell forks, creating a copy of itself called the child process. The parent shell sleeps (waits). The child shell executes (execs) the Perl process in its place. The child never returns. Note that ENV variables, standard input, output, and standard error are inherited. When the Perl program completes, it exits and the parent shell wakes up. The shell prompt reappears on your screen. The Perl program could use the fork function to spawn off another application program.
Formatfork; Example 18.50.
|
Whereas fork creates a brand new process, the exec function is used to initiate a new program in place of the currently running program. Normally, the exec function is called after fork. Perl inherits attributes from the shell, and a process that is executed from within Perl also inherits Perl's attributes, such as pid, gid, uid, signals, directories, etc. If, then, the exec function is called directly (no fork) from within a Perl script, the new program executes in place of the currently running Perl program. When that program completes, you do not return to your Perl program. Since exec does not flush the output buffer, the $| variable needs to be set to ensure command buffering.
The filehandles STDIN, STDOUT, and STDERR remain open following a call to the exec function.
At the system level there are six different exec functions used to initiate new programs. Perl calls the C library function execvp if more than one argument is passed to the exec function. The arguments are the name of the program to execute and any other arguments that will be passed to that program. If a single scalar is passed to the exec function and it contains any shell metacharacters, the shell command /bin/sh -c is passed the command for interpretation.
Formatexec(UNIX COMMAND); exec UNIX COMMAND; Example 18.51.
|
The wait function waits for a child process to finish execution. After a fork call, both processes, parent and child, execute. The wait function forces the parent to wait until its child is finished and returns the pid of the child process to the parent. If there are no child processes, wait returns a –1.[11]
[11] The waitpid function also waits for a child process to finish execution, but it can specify which child it will wait for, and it has special flags that control blocking.
Formatwait; Example 18.52.
|
The exit function causes the program to exit. It can be given an integer argument ranging from values between zero and 255. The exit value is returned to the parent process via the wait function call. By convention, UNIX programs exiting with a zero status are successful, and those exiting with nonzero failed in some way. (Of course, the criteria for success for one programmer may not be the same as those for another.)
Formatexit (Integer); exit Integer; Example 18.53.
|
You can use the system and exec functions and backquotes on Win32 systems just the same as you would with UNIX.
The Perl system function is used by both Windows and UNIX to start an operating system command. The system function executes a program and doesn't return until that program finishes. If the Windows start command is given as an argument to the Perl system function, a new application will run, and your script will also continue to run.
use warnings; 1 $return_value = system ("start /Program Files/Netscape/Communicator/Program/netscape.exe"); 2 print "Program continues; Netscape is running.\n"; 3 print "The return_value from system is $return_value.\n"; (Output) Program continues; Netscape is running. The return_value from system is 0. Explanation
|
The Win32::Spawn function behaves like the system function and the Windows Start menu.
Formatuse Win32; Win32::Spawn($ProgramName, $CommandLine,$ProcessID); Example 18.55.
|
[a] MSK Toolkit for Windows NT and Windows 95 Release 6.1, Mortice Kern Systems Inc.
Another extension you can use to launch Windows applications is the object-oriented Win32::Process module. It provides a number of methods for creating and managing processes. Processes can be suspended, resumed, and killed with this module.
Method | What It Does |
---|---|
Create($Obj, $AppName, $CommandLine, $Inherit, $CreateFlags, $InitialDir); | Creates the process object |
$Obj–>GetExitCode($ExitCode); | Gets the exit code of the process |
$Obj–>GetPriorityClass($Class); | Gets the process priority class |
$ProcessObj–>GetProcessID() | Returns the process ID |
$Obj–>Kill($ExitCode); | Kills the process with exit code |
$Obj–>Resume(); | Resumes a suspended process |
$Obj–>SetPriorityClass($Class); | Sets the process affinity mask (NT) |
$Obj–>Suspend(); | Suspends the process |
$Obj–>Wait($Timeout); | Waits for the process to die |
1 use Win32::Process; 2 use Win32; 3 sub ErrorReport{ print Win32::FormatMessage( Win32::GetLastError() ); } 4 Win32::Process::Create($ProcessObj, "C:\\windows\\notepad.exe", "notepad myfile.txt", 0, NORMAL_PRIORITY_CLASS, ".") || die ErrorReport(); print "Notepad has started\n"; 5 print "The exit code is: ",$ProcessObj->GetExitCode($ExitCode),"\n"; (Output) Notepad has started The exit code is: 1 Explanation
|