15-213, Fall 06 Outline  Shell Lab  Processes  Signals Process IDs & process groups  A process has its own, unique process ID 

Download Report

Transcript 15-213, Fall 06 Outline  Shell Lab  Processes  Signals Process IDs & process groups  A process has its own, unique process ID 

15-213, Fall 06
Outline
 Shell Lab
 Processes
 Signals
1
Process IDs & process groups
 A process has its own, unique process ID
 pid_t getpid();
 A process belongs to exactly one process group
 pid_t getpgrp();
 A new process belongs to which process group?
 Its parent’s process group
 Make a new process group for myself and my
children:

setpgid(0, 0);
2
Process & concurrency
 int fork(void)



Create a new process identical to parent process
Return 0 to child process
Return child’s pid to the parent process
 Any scheduling order of processes is possible!!


Unless code uses explicit synchronization
Context switching can happen at any point
3
Lab 5
 A tiny shell with job control & I/O redirection
 Key points:



Reap all child processes
Handle SIGCHLD, SIGTSTP, SIGINT
Avoid race hazards
4
How to Send Signals
 To a single process:

int kill(pid_t pid, int sig)
 To every process in group abs(gid):

int kill(pid_t gid, int sig), where gid is negative
 Can we use signals to count events?


No
Why? Signals not queued!!
5
Signals: sending
Process 1
Process 2
kill(pid, SIGINT)
1
blocked
pending
OS signal manager
• divide by zero:
SIGFPE
• ctrl-c: SIGINT
• child process exit:
SIGCHLD
other events
OS Kernel
6
Signals: receiving
OS delivers the
pending nonblocked signals
Process 2
0
1
blocked
pending
OS signal manager
OS Kernel
7
Key signals in the shell lab
 SIGINT


Triggered by: Interrupt signal (ctrl-c)
Default action: the process terminates
 SIGTSTP



Triggered by: Stop signal from terminal (ctrl-z)
Default action: the process stops
Can be restarted later(by sending SIGCONT to it)
 SIGCHLD

Triggered by: a child process has stopped or
terminated
8
Shell: the process tree
pid=10
pgid=10
Shell
Background
job #1
pid=20 Forepgid=20 ground
job
pid=32
pgid=32
Background
process group 32
Child
pid=21
pgid=20
Child
pid=22
pgid=20
Foreground
process group 20
Background
job #2
pid=40
pgid=40
Backgroud
process group 40
Each job has a unique process group id
int setpgid(pid_t pid, pid_t pgid);
setpgid(0, 0);
9
Process tree for tsh
pid=5
pgid=5
pid=10
pgid=10
UNIX
shell
tsh
Background
job #1
pid=20 Forepgid=20 ground
job
Foreground job
receives SIGINT, SIGTSTP,
when you type ctrl-c, ctrl-z
pid=32
pgid=32
Background
process group 32
Child
pid=21
pgid=20
Child
pid=22
pgid=20
Foreground
process group 20
Background
job #2
pid=40
pgid=40
Backgroud
process group 40
int kill(pid_t pid, int sig)
pid > 0: send sig to process with PID=pid
pid = 0: send sig to all processes in my group
pid = -1: send sig to all processes with PID>1
10
pid < -1: send sig to group abs(pid)
Execute program
int execve(const char *fname,
char *const argv[],
char *const envp[]);
Examples:
execve(“/bin/ls”, NULL, NULL);
execve(“./mytest”, argv, envp);
 What happens to the caller process?

It effectively terminates
 The new program overwrites its state
and takes its PID
 Any signal handlers installed by the caller are
reset
11
Reaping terminated child processes
pid_t waitpid(pid_t pid, int *status, int options)
pid>0: wait for process with PID=pid
-1: wait for any process
pid<-1: wait for any process from group abs(pid)
By default, waitpid blocks until at least one zombie process becomes
available.
options:
WNOHANG: return immediately if no zombies available
WUNTRACED: also return if some process has been stopped
WNOHANG|WUNTRACED combination is very useful in the shell lab:
it detects all the necessary events, and doesn’t block if no ‘’events’’
12
Reaping terminated child processes
pid_t waitpid(pid_t pid, int *status, int options)
pid>0: wait for process with PID=pid
-1: wait for any process
pid<-1: wait for any process from group abs(pid)
Return value:
 pid of the process that exited,
 or zero if WNOHANG was used and no zombie process available,
 or -1 on error (then see errno)
status: gives info on why the process terminated (or stopped if
WUNTRACED used)
13
Status
 int status;
waitpid(pid, &status, WNOHANG|WUNTRACED)
Macros to evaluate status:
 WIFEXITED(status): process exited normally
 WEXITSTATUS(status): exit code of the process
 WIFSIGNALED(status): process exited because a signal
was not caught (SIGINT, SIGKILL, etc.)
 WTERMSIG(status): identifies the signal that was not caught
 WIFSTOPPED(status): process was stopped
 WSTOPSIG(status): identifies the stopping signal
14
Reaping child process in tsh
 Where to put waitpid(…) ?
 As the handout suggests:
One centralized reaping for both fg and bg:

In sigchld_handler()
15
Busy wait for foreground job
tsh should still wait for fg job to complete, how?
At an appropriate place in eval():
while(fg process still alive){
/* do nothing */
}
16
Better than simple busy-waiting: Sleep
At an appropriate place in eval():
while(fg process still alive){
sleep(1);
}
17
Race hazards
 A data structure is shared by two pieces of code
that can run concurrently
 Different behaviors of program depending upon
how the schedule interleaves the execution of
code.
18
An example of a race hazard
sigchld_handler() {
… waitpid(…)) … {
deletejob(pid);
}
}
eval() {
pid = fork();
if(pid == 0)
{ /* child */
execve(…);
}
/* parent */
/* signal handler may run BEFORE addjob()*/
addjob(…);
}
19
Solution: blocking signals
eval() {
sigprocmask(SIG_BLOCK, …)
pid = fork();
if(pid == 0)
{ /* child */
sigprocmask(SIG_UNBLOCK, …)
execve(…);
}
/* parent */
/* signal handler might run BEFORE addjob() */
addjob(…);
sigprocmask(SIG_UNBLOCK, …)
}
20
I/O Redirection
 Covered in Chapter 11
Make file descriptor ‘newfd’ a copy of ‘oldfd’:
dup2(int oldfd, int newfd);
Get input from my_infd instead of standard input
dup2(my_infd, STDIN_FILENO);
Make a copy of a file descriptor (standard output in
this case):
int my_outfd = dup(STDOUT_FILENO); 21
Reminders
 Some important system calls:

fork(), execve(), waitpid(), sigprocmask(),
setpgid(), kill() …
 Check man pages for details about system calls

man 2 kill
 Check return values of all system calls
 Flush output buffers: fflush(stdout);
 STEP by STEP

Test your shell by typing commands first
 Start now!
22
What are the possible program outputs?
#include <unistd.h>
#include <stdio.h>
int cnt = 0;
int main(void)
{
if (fork() == 0){
cnt ++; // in child
fork();
cnt++;
}
cnt ++;
printf("%d", cnt);
return 0;
}
Possible outputs:
133
313
331
23