15-213 Recitation 10
Processes, Signals, Tshlab
November 1, 2021 Your TAs
Copyright By PowCoder代写 加微信 powcoder
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
⬛ Logistics
⬛ Process Lifecycle
⬛ Error Handling
⬛ Signal Handling
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Learning Objectives
⬛ Expectations:
▪ Basic understanding of signals & processes
▪ Better understanding of signals & processes
▪ Understand what a shell does and how to interact with it
▪ Understand how to properly handle errors
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
⬛ Malloc Final due Nov 2nd ▪ THIS TUESDAY
▪ Can use up to 2 late days!
▪ Style grading mm.c (not checkheap) ▪ Sign up for malloc final code reviews
⬛ OH Update:
▪ In-person students will be prioritized on the OH
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
⬛ Due date: Nov 16th
⬛ Simulate a Linux-like shell
⬛ Review the writeup carefully.
▪ Review once before starting, and again when halfway through ▪ This will save you a lot of style points and a lot of grief!
⬛ Read Chapter 8 in the textbook:
▪ Process lifecycle and signal handling
▪ How race conditions occur, and how to avoid them
▪ Be careful not to use code from the textbook without understanding it first.
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Shell demo
Process Lifecycle
▪ This reports a snapshot of all the current processes. You can
identify them by PID
▪ $ ctrl+z sends SIGTSTP and stops the current foreground process ▪ fg/bg to run the most recently stopped process in the
foreground/background
▪ $ ./long_binary_with_lots_of_io &
▪ Appending & to the end of a command runs it in the background
I/O redirection
▪ $ ./hex2raw < exploit.txt > exploit-raw.txt
▪ < to redirect input and > to redirect output to the specified file Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Shell Demo
⬛ Login to shark machine
⬛ wget http://www.cs.cmu.edu/~213/activities/rec10.tar
⬛ tar -xvf rec10.tar
⬛ cd rec10
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Process “Lifecycle”
Create a duplicate, a “child”, of the process
⬛ execve()
Replace the running program
⬛ … [Complete Work]
End the running program
⬛ waitpid()
Wait for a child process to terminate
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Processes are separate
⬛ How many lines are printed?
⬛ Will the pid address be different?
⬛ Will the pid be different?
int main(void) { pid_t pid;
pid = fork();
printf(“pid addr: %p – pid: %d\n”,
&pid, pid);
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Processes are separate
⬛ How many lines are printed?
⬛ Will the pid address be different?
⬛ Will the pid be different?
int main(void) { pid_t pid;
pid = fork();
printf(“pid addr: %p – pid: %d\n”,
&pid, pid);
pid addr: 0x7fff2bcc264c – pid: 24750
pid addr: 0x7fff2bcc264c – pid: 0
The order and the child’s PID (printed by the parent) may vary, but the address will be the same in the parent and child.
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Processes Change
⬛ What does this program print?
int main(void) {
char *args[3] = {
“/bin/echo”, “Hi 18213!”, NULL
execv(args[0], args);
printf(“Hi 15213!\n”);
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Processes Change
⬛ What does this program print?
int main(void) {
char *args[3] = {
“/bin/echo”, “Hi 18213!”, NULL
execv(args[0], args);
printf(“Hi 15213!\n”);
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Processes Change
⬛ What about this program? What does it print?
⬛ Assume that /bin/blahblah does not exist.
int main(void) {
char *args[3] = {
“/bin/blahblah”, “Hi 15513!”, NULL
execv(args[0], args);
printf(“Hi 14513!\n”);
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Processes Change
⬛ What about this program? What does it print?
⬛ Assume that /bin/blahblah does not exist.
int main(void) {
char *args[3] = {
“/bin/blahblah”, “Hi 15513!”, NULL
execv(args[0], args);
printf(“Hi 14513!\n”);
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Exit values can convey information
⬛ Two values are printed. What are they?
int main(void) {
pid_t pid = fork();
if (pid == 0) { exit(0x213); } else {
int status = 0;
waitpid(pid, &status, 0);
printf(“0x%x exited with 0x%x\n”, pid,
exit(0); }
WEXITSTATUS(status));
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Exit values can convey information
⬛ Two values are printed. What are they?
int main(void) {
pid_t pid = fork();
if (pid == 0) { exit(0x213); } else {
int status = 0;
waitpid(pid, &status, 0);
printf(“0x%x exited with 0x%x\n”, pid,
exit(0); }
WEXITSTATUS(status));
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
0x7b54 exited with 0x13
WEXITSTATUS(status) will only return 1 byte of information
Processes have ancestry
⬛ What’s wrong with this code? (assume that fork succeeds)
int main(void) {
int status = 0, ret = 0; pid_t pid = fork();
if (pid == 0) {
pid = fork();
exit(getpid());
ret = waitpid(-1, &status, 0);
printf(“Process %d exited with %d\n”, ret, status);
ret = waitpid(-1, &status, 0);
printf(“Process %d exited with %d\n”, ret, status);
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Processes have ancestry
⬛ What’s wrong with this code? (assume that fork succeeds)
int main(void) {
int status = 0, ret = 0; pid_t pid = fork();
if (pid == 0) {
pid = fork();
exit(getpid());
ret = waitpid(-1, &status, 0);
printf(“Process %d exited with %d\n”, ret, status);
ret = waitpid(-1, &status, 0);
printf(“Process %d exited with %d\n”, ret, status);
waitpid will reap only children, not grandchildren, so the second waitpid call will return an error.
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Process Graphs
⬛ How many different sequences can be printed?
int main(void) { int status;
if (fork() == 0) {
pid_t pid = fork(); printf(“Child: %d\n”, getpid()); if (pid == 0) {
exit(0); }
// Continues execution…
pid_t pid = wait(&status);
printf(“Parent: %d\n”, pid);
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Process Graphs
⬛ How many different sequences can be printed?
int main(void) { int status;
exit(0); }
// Continues execution…
pid_t pid = wait(&status);
printf(“Parent: %d\n”, pid);
wait exit wait exit
if (fork() == 0) {
pid_t pid = fork(); printf(“Child: %d\n”, getpid()); if (pid == 0) {
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Error in UNIX – return value
⬛ Can syscalls fail?
⬛ How to tell the difference?
int main() {
int fd = open(“213Grades.txt”,
// Change grades to As or Fs
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Error in UNIX – What error?
⬛ Can syscalls fail?
⬛ How to tell the difference?
▪ Returned -1
⬛ So, my fantastic syscalls
⬛ How can I tell what went wrong?
int main() {
int fd = open(“213Grades.txt”,
O_RDWR); if (fd < 0) {
fprintf(stderr, "Failed to
exit(-1); }
// Change grades to As or Fs
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Error in UNIX - What error?
⬛ Can syscalls fail?
⬛ How to tell the difference?
▪ Returned -1
⬛ So, my fantastic syscalls
⬛ How can I tell what went wrong?
▪ errno is a global variable
that syscalls store information in when they fail
▪ strerror turns errno codes into printable messages
▪ perror (print error) is a handy shorthand
int main(void) {
int fd = open("213Grades.txt",
O_RDWR); if (fd < 0) {
fprintf(stderr,
"Failed to open %s: %s\n",
"213Grades.txt",
strerror(errno));
exit(1); }
// Change grades to As or Fs
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Always print strerror(errno) and the names of filenames involved in failing system calls
Process Graphs
⬛ How many different lines are printed?
int main(void) {
char *tgt = "child";
sigset_t mask, old_mask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigprocmask(SIG_SETMASK, &mask, &old_mask); // Block pid_t pid = fork();
if (pid == 0) {
pid = getppid(); // Get parent pid
tgt = "parent";
kill(pid, SIGINT);
sigprocmask(SIG_SETMASK, &old_mask, NULL); // Unblock
printf("Sent SIGINT to %s:%d\n", tgt, pid);
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Process Graphs
⬛ How many different lines are printed?
int main(void) {
char *tgt = "child";
sigset_t mask, old_mask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigprocmask(SIG_SETMASK, &mask, &old_mask); // Block pid_t pid = fork();
if (pid == 0) {
pid = getppid(); // Get parent pid
tgt = "parent";
kill(pid, SIGINT);
sigprocmask(SIG_SETMASK, &old_mask, NULL); // Unblock
printf("Sent SIGINT to %s:%d\n", tgt, pid);
0 or 1 line. The parent and child try to terminate each other.
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Signals and Handling
⬛ Signals can happen at any time
▪ Control when through blocking signals
⬛ Signals also communicate that events have occurred ▪ What event(s) correspond to each signal?
⬛ Write separate routines for receiving (i.e., signals)
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Counting with signals
⬛ Will this code terminate? volatile int counter = 0;
void handler(int sig) { counter++; }
int main(void) {
signal(SIGCHLD, handler);
for (int i = 0; i < 10; i++) {
if (fork() == 0) { exit(0); } }
while (counter < 10) { mine_bitcoin();
return 0; }
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Counting with signals
⬛ Will this code terminate? volatile int counter = 0;
void handler(int sig) { counter++; }
int main(void) {
signal(SIGCHLD, handler);
for (int i = 0; i < 10; i++) {
if (fork() == 0) { exit(0); } }
while (counter < 10) { mine_bitcoin();
(Don't use signal, use Signal or sigaction instead!)
return 0; }
(Don't busy-wait, use sigsuspend instead!)
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
It might not, since signals can coalesce.
sigsuspend
int sigsuspend(const sigset_t *mask);
- Suspend current process until a signal is received, you can specify which one using a mask
This is an atomic version of:
sigprocmask(SIG_SETMASK, &mask, &prev)
sigprocmask(SIG_SETMASK, &prev, NULL);
- This still doesn’t fix the issue of signals coalescing!
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Proper signal handling
⬛ How can we fix the previous code?
▪ Remember that signals will be coalesced, so the number of times a signal handler has executed is not necessarily the same as number of times a signal was sent.
▪ We need some other way to count the number of children.
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Proper signal handling
⬛ How can we fix the previous code?
▪ Remember that signals will be coalesced, so the number of times a signal handler has executed is not necessarily the same as number of times a signal was sent.
▪ We need some other way to count the number of children.
void handler(int sig) { pid_t pid;
while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) { counter++;
(This instruction isn’t atomic. Why won’t there be a race condition?)
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Error and signals : Recap
⬛ You can’t expect people to block signals around all error handling logic
⬛ Hence, your signal handler shouldn’t interfere with them
⬛ Solution:
▪ Do not make any system call that could set errno
▪ Save and restore errno (store at beginning of handler and restore after)
▪ Think about what would work for the case you are using, not one rule
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
If you get stuck
⬛ Read the writeup!
⬛ Do manual unit testing before runtrace and sdriver!
⬛ Read the writeup!!
⬛ Post private questions on Piazza!
⬛ Think carefully about error conditions.
▪ Read the man pages for each syscall when in doubt. ▪ What errors can each syscall return?
▪ How should the errors be handled?
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Appendix: Notes on Examples
⬛ Full source code of all programs is available ▪ TAs may demo specific programs
⬛ In the examples, exit() is called
▪ We do this to be explicit about the program’s behavior
▪ Exit should generally be reserved for terminating on error
⬛ Unless otherwise noted, assume all syscalls succeed ▪ Error checking code is omitted.
▪ Be careful to check errors when writing your own shell!
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Appendix: Example Question: Possible outputs?
1 int main( ) {
2 int val = 2;
3 printf(“%d”, 0);
4 fflush(stdout);
13 printf(“%d”, val); 14 fflush(stdout); 15 wait(NULL);
19 printf(“%d”, val);
20 fflush(stdout);
21 exit(0);
if (fork() == 0) {
printf(“%d”, val);
fflush(stdout);
⬛ There is no deterministic interleaving of the parent and child after the call to fork()
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Appendix: Blocking signals
⬛ Surround blocks of code with calls to sigprocmask.
▪ Use SIG_BLOCK to block signals at the start.
▪ Use SIG_SETMASK to restore the previous signal mask at the end.
⬛ Don’t use SIG_UNBLOCK.
▪ We don’t want to unblock a signal if it was already blocked. ▪ This allows us to nest this procedure multiple times.
sigset_t mask, prev;
sigemptyset(&mask, SIGINT);
sigaddset(&mask, SIGINT);
sigprocmask(SIG_BLOCK, &mask, &prev);
sigprocmask(SIG_SETMASK, &prev, NULL); Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Appendix: Errno
#include
⬛ Global integer variable used to store an error code.
▪ Its value is set when a system call fails.
▪ Only examine its value when the system call’s return code indicates that an error has occurred!
▪ Be careful not to call make other system calls before checking the value of errno!
⬛ Lets you know why a system call failed.
▪ Use functions like strerror, perror to get error messages.
⬛ Example: assume there is no “foo.txt” in our path
int fd = open(“foo.txt”, O_RDONLY);
if (fd < 0) perror("foo.txt");
// foo.txt: No such file or directory
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
Appendix: Writing signal handlers
⬛ G1. Call only async-signal-safe functions in your handlers.
▪ Do not call printf, sprintf, malloc, exit! Doing so can cause
deadlocks, since these functions may require global locks.
▪ We've provided you with sio_printf which you can use instead.
⬛ G2. Save and restore errno on entry and exit.
▪ If not, the signal handler can corrupt code that tries to read errno.
▪ The driver will print a warning if errno is corrupted.
⬛ G3. Temporarily block signals to protect shared data. ▪ This will prevent race conditions when writing to shared data.
⬛ Avoid the use of global variables in tshlab.
▪ They are a source of pernicious race conditions!
▪ You do not need to declare any global variables to complete tshlab.
▪ Use the functions provided by tsh_helper.
Bryant and O’Hallaron, Computer Systems: A Programmer’s Perspective, Third Edition
程序代写 CS代考 加微信: powcoder QQ: 1823890830 Email: powcoder@163.com