230 likes | 242 Views
This shell lab covers topics such as process IDs, signals, reminders, and a quiz. It includes information on creating and managing process groups, sending signals to processes, and reaping terminated child processes.
E N D
Outline Shell Lab Processes Signals Reminders Quiz today Shell Lab: Due Next Thursday TA: Jernej Barbic Modified from: Kun Gao’s and Minglong Shao’s recitations, Spring 2005,Fall 2004 Recitation #8, Section F, 15-213, Sp 06
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);
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
Lab 5 • A tiny shell with job control & I/O redirection • Key points: • Reap all child processes • Handle SIGCHLD, SIGTSTP, SIGINT • Avoid race hazards
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!!
Process 1 blocked pending kill(pid, SIGINT) other events Signals: sending Process 2 1 OS signal manager • divide by zero: SIGFPE • ctrl-c: SIGINT • child process exit: SIGCHLD OS Kernel
blocked pending Signals: receiving Process 2 OS delivers the pending non-blocked signals 0 1 OS signal manager OS Kernel
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
Shell: the process tree Shell pid=10 pgid=10 Fore- ground job Back- ground job #1 Back- ground job #2 pid=20 pgid=20 pid=32 pgid=32 pid=40 pgid=40 Background process group 32 Backgroud process group 40 Child Child pid=21 pgid=20 pid=22 pgid=20 Each job has a unique process group id int setpgid(pid_t pid, pid_t pgid); setpgid(0, 0); Foreground process group 20
Process tree for tsh UNIX shell pid=5 pgid=5 Foreground job receives SIGINT, SIGTSTP, when you type ctrl-c, ctrl-z pid=10 pgid=10 tsh Forward signals Fore- ground job Back- ground job #1 Back- ground job #2 pid=20 pgid=20 pid=32 pgid=32 pid=40 pgid=40 Background process group 32 Backgroud process group 40 Child Child 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 pid < -1: send sig to group abs(pid) pid=21 pgid=20 pid=22 pgid=20 Foreground process group 20
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
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’’
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)
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
Reaping child process in tsh • Where to put waitpid(…) ? • As the handout suggests:One centralized reaping for both fg and bg: • In sigchld_handler()
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 */ }
Better than simple busy-waiting: Sleep At an appropriate place in eval(): while(fg process still alive){ sleep(1); }
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.
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(…); }
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, …) }
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);
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!
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