UNIX System Programming: Processes
UNIX System Programming: Processes
UNIX System Programming: Processes
Processes
Objectives
Overview
1. 2. 3. 4. What is a Process? fork() exec() wait()
5. Process Data 6. File Descriptors across Processes 7. Special Exit Cases 8. IO Redirection 9. User/Group ID real and effective 10. getenv/putenv, ulimit()
1. What is a Process?
A A
A process is the context (the information/data) maintained for an executing program. Intuitively, a process is the abstraction of a physical processor.
Exists because it is difficult for the OS to otherwise coordinate many concurrent activities, such as incoming network data, multiple users, etc.
code machine registers global data stack open files (file descriptors) an environment (environment variables; credentials for security)
Effective User ID
Current directory File descriptor table Environment
VAR=VALUE pairs
continued
6
Memory for global vars Memory for local vars Dynamically allocated
Mother of all processes. init is started at boot time and is responsible for starting other processes.
init uses file inittab & directories: /etc/rc?.d
getty
Process 1 (init)
getty
A process ID or pid is a positive integer that uniquely identifies a running process, and is stored in a variable of type pid_t. You can get the process pid or parents pid
#include <sys/types> main() { pid_t pid, ppid; printf( "My PID is:%d\n\n",(pid = getpid()) ); printf( "Par PID is:%d\n\n",(ppid = getppid()) ); }
10
2. fork()
Creates
a child process by making a copy of the parent process --- an exact duplicate.
Implicitly specifies code, registers, stack, data, files
Both
11
fork() as a diagram
Parent
pid = fork() Returns a new PID: e.g. pid == 5 Data Child pid == 0 Shared Program Data Copied
12
pid = fork();
In
the child: pid == 0; In the parent: pid == the process ID of the child.
program almost always uses this pid difference to do different things in the parent and child.
13
else { /* child */ for( i=0; I < 1000; i++ ) printf( CHILD %d\n, i ); } return 0; }
15
Possible Output
CHILD 0 CHILD 1 CHILD 2 PARENT PARENT PARENT PARENT CHILD 3 CHILD 4 PARENT 4 : 0 1 2 3
16
Things to Note
The switching between the parent and child depends on many factors:
machine load, system process scheduling
3. exec()
Family
of functions for replacing processs program with the one inside the exec() call. e.g.
#include <unistd.h> int execlp(char *file, char *arg0, char *arg1, ..., (char *)0); execlp(sort, sort, -n, foobar, (char *)0); Same as "sort -n foobar"
18
tinymenu.c
#include <stdio.h> #include <unistd.h> void main() { char *cmd[] = {who, ls, date}; int i; printf(0=who 1=ls 2=date : ); scanf(%d, &i);
Execution
tinymenu
cmd[i] execlp() printf() not executed unless there is a problem with execlp()
20
exec(..) Family
There
are 6 versions of the exec function, and they all do about the same thing: they replace the current program with the text of the new program. Main difference is how parameters are passed.
21
int execl( const char *path, const char *arg, ... ); int execlp( const char *file, const char *arg, ... ); int execle( const char *path, const char *arg , ..., char *const envp[] ); int execv( const char *path, char *const argv[] ); int execvp( const char *file, char *const argv[] ); int execve( const char *filename, char *const argv [], char *const envp[] );
22
exec(..) Family
execl() execle() execlp()
execv()
execvp()
execve()
23
execv(new_program, argv[ ])
Initial process
Fork
Returns a new PID
fork() returns pid=0 and runs as a cloned parent until execv is called
new_Program (replacement)
execv(new_program)
24
4. wait()
Suspends
calling process until child has finished. Returns the process ID of the terminated child if ok, -1 on error.
can be (int *)0 or a variable which will be bound to status info. about the child.
statloc
25
wait() Actions
A
suspend (block) if all of its children are still running, or return immediately with the termination status of a child, or
return immediately with an error if there are no child processes.
26
menushell.c
#include #include #include #include <stdio.h> <unistd.h> <sys/types.h> <sys/wait.h>
void main() { char *cmd[] = {who, ls, date}; int i; while( 1 ) { printf( 0=who 1=ls 2=date : ); scanf( %d, &i ); :
continued
27
if(fork() == 0) { /* child */ execlp( cmd[i], cmd[i], (char *)0 ); printf( execlp failed\n ); exit(1); } else { /* parent */ wait( (int *)0 ); printf( child finished\n ); } } /* while */ } /* main */
28
Execution
menushell
fork() child cmd[i]
wait()
execlp()
29
WEXITSTATUS(status)
Evaluates to the least significant eight bits of the return code of the child which terminated, which may have been set as the argument to a call to exit( ) or as the argument for a return. This macro can only be evaluated if WIFEXITED returned non-zero.
30
Returns true if the child process exited because of a signal which was not caught.
WTERMSIG(status)
Returns the signal number that caused the child process to terminate. This macro can only be evaluated if WIFSIGNALED returned non-zero.
31
waitpid()
#include <sys/types.h> #include <sys/wait.h> pid_t waitpid( pid_t pid, int *status, int opts )
Wait for any child process whose process group ID is equal to the absolute value of pid.
pid == -1
Wait for any child process. Same behavior which wait( ) exhibits. pid == 0 Wait for any child process whose process group ID is equal to that of the calling process.
32
pid
>0
Wait for the child whose process ID is equal to the value of pid. options
Zero
WNOHANG Return immediately if no child has exited. WUNTRACED Also return for children which are stopped, and whose status has not been reported (because of signal).
Return value
The
process ID of the child which exited. -1 on error; 0 if WNOHANG was used and no child was available.
33
WIFSTOPPED(status)
Returns true if the child process which caused the return is currently stopped. This is only possible if the call was done using WUNTRACED.
WSTOPSIG(status)
Returns the signal number which caused the child to stop. This macro can only be evaluated if WIFSTOPPED returned non-zero.
34
Example: waitpid
#include <stdio.h> #include <sys/wait.h> #include <sys/types.h>
else
{ /* parent */ while (1) { waitpid( pid, &status, WUNTRACED ); if( WIFSTOPPED(status) ) { printf(child stopped, signal(%d)\n, WSTOPSIG(status)); continue; } else if( WIFEXITED(status) ) printf(normal termination with status(%d)\n, WEXITSTATUS(status)); else if (WIFSIGNALED(status)) printf(abnormal termination, signal(%d)\n, WTERMSIG(status)); exit(0); } /* while */ } /* parent */ } /* main */
36
5. Process Data
Since
a child process is a copy of the parent, it has copies of the parents data. change to a variable in the child will not change that variable in the parent.
37
Example
#include <stdio.h> #include <sys/types.h> #include <unistd.h>
(globex.c)
int globvar = 6; char buf[] = stdout write\n; int main(void) { int w = 88; pid_t pid;
continued
38
write( 1, buf, sizeof(buf)-1 ); printf( Before fork()\n ); if( (pid = fork()) == 0 ) { /* child */ globvar++; w++; } else if( pid > 0 ) /* parent */ sleep(2); else perror( fork error );
printf( pid = %d, globvar = %d, w = %d\n, getpid(), globvar, w ); return 0; } /* end main */
39
Output
Before fork() pid = 430, globvar = 7, w = 89 /*child chg*/ pid = 429, globvar = 6, w = 88 /* parent no chg */
$ globex > temp.out $ cat temp.out stdout write Before fork() pid = 430, globvar = 7, w = 89 Before fork() /* fully buffered */ pid = 429, globvar = 6, w = 88
40
child and parent have copies of the file descriptors, but the R-W pointer is maintained by the system:
the R-W pointer is shared
This
means that a read() or write() in one process will affect the other process since the R-W pointer is changed.
41
int main(void) { int fd; /* file descriptor */ pid_t pid; char buf[10]; /* for file data */ :
continued
42
if( (pid = fork()) == 0 ) { /* child */ printpos( Child before read, fd ); read( fd, buf, 10 ); printpos( Child after read, fd ); } :
continued
43
else if( pid > 0 ) { /* parent */ wait((int *)0); printpos( Parent after wait, fd ); } else perror( fork ); }
continued
44
void printpos( char *msg, int fd ) /* Print position in file */ { long int pos; if( (pos = lseek( fd, 0L, SEEK_CUR) ) < 0L ) perror(lseek); printf( %s: %ld\n, msg, pos ); }
45
Output
$ shfile Before fork: 10 Child before read: 10 Child after read: 20 Parent after wait: 20
what's happened?
46
continued
47
2)
48
9. I/O redirection
The
trick: you can change where the standard I/O streams are going/coming from after the fork but before the exec
49
dup2(int fin, int fout) - copies fin to fout in the file table
File table stdin dup2(3,1) 0 1 2 3 4
stdin
0 1 2 3 4
x.lis
50
Group ID
Real, effective
User ID
Real user ID
Effective user ID
Used to assign ownership of newly created files, to check file access permissions, and to check permission to send signals to processes. To change euid: executes a setuid-program that has the set-uid bit set or invokes the setuid( ) system call. The setuid(uid) system call: if euid is not superuser, uid must be the real uid or the saved uid (the kernel also resets euid to uid).
Read IDs
pid_t getuid(void);
Returns the real user ID of the current process
pid_t geteuid(void);
Returns the effective user ID of the current process
gid_t getgid(void);
Returns the real group ID of the current process
gid_t getegid(void);
Returns the effective group ID of the current process
53
Non-superuser process can set effective user ID to uid, only when uid equals real user ID or the saved set-user ID (set by executing a setuid-program in exec). In any other cases, setuid returns error.
54
saved set-uid
uid
unchanged
55
Sets real and effective user IDs of the current process. Un-privileged users may change the real user ID to the effective user ID and vice-versa. It is also possible to set the effective user ID from the saved user ID. Supplying a value of -1 for either the real or effective user ID forces the system to leave that ID unchanged. If the real user ID is changed or the effective user ID is set to a value not equal to the previous real user ID, the saved user ID will be set to the new effective user ID.
56
euid). Setuid-root program wishing to temporarily drop root privileges, assume the identity of a non-root user, and then regain root privileges afterwards cannot use setuid, because setuid issued by the superuser changes all three IDs. One can accomplish this with seteuid.
11. Environment
extern char **environ;
58
Example: environ
#include <stdio.h> void main( int argc, char *argv[], char *envp[] ) { int i; extern char **environ; printf( from argument envp\n ); for( i = 0; envp[i]; i++ ) puts( envp[i] ); printf(\nFrom global variable environ\n); for( i = 0; environ[i]; i++ ) puts(environ[i]); }
59
getenv
#include <stdlib.h>
60
putenv
#include <stdlib.h>
62