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