880 likes | 1k Views
COMP 111: Operating Systems (Fall 2013). The Life and Death of A Process. Noah Mendelsohn Tufts University Email: noah@cs.tufts.edu Web: http://www.cs.tufts.edu/~noah Based on a presentation by Professor Alva Couch. Today. How processes are created, managed and terminated
E N D
COMP 111: Operating Systems (Fall 2013) The Life and DeathofA Process Noah Mendelsohn Tufts UniversityEmail: noah@cs.tufts.edu Web: http://www.cs.tufts.edu/~noah Based on a presentation by Professor Alva Couch
Today • How processes are created, managed and terminated • Sharing the computer (redux) • From file.c to a.out to running image • Library routines and shared libraries
Operating systems do two things for us: • They make the computer easier to use • The facilitate sharing of the computer by multiple programs and users
…actually, Unix & Linux have one more goal: • To facilitate running the same program (and OS!) on different types of computer
The protected OS “Kernel” The operating system is a special, privileged program, with its own code and data. We call the protected, shared part of the OS the “kernel”. Multiple Programs Running at once Play Video Angry Birds Browser MAIN MEMORY OPERATING SYSTEM KERNEL CPU
We need help from the hardware to protect the kernel! • The hardware has memory mapping features that the OS can use to: • Hide the kernel from other programs • Hide programs from each other • Convince each program it’s got its own private memory starting at address zero Play Video Angry Birds Browser MAIN MEMORY OPERATING SYSTEM KERNEL CPU
Privileged instructions only the OS can use The hardware has special instructions that only the kernel can use to:* Initiate I/O * Set clocks and timers * Control memory mappingThe Kernel runs in “privileged” or “kernel” or “supervisor” state. Ordinary programs run in “user mode”. If a user program tries a privileged operation, the hardware will tell the kernel! Play Video Angry Birds Browser MAIN MEMORY OPERATING SYSTEM KERNEL CPU
A process is an instance of a running program Play Video Angry Birds Browser OPERATING SYSTEM KERNEL MEMORY CPU Printer Disk Keyboard,mouse,display
A process is an instance of a running program How does this process get started? How does the OS know what code to run? Play Video Angry Birds Browser OPERATING SYSTEM KERNEL MEMORY CPU Printer Disk Keyboard,mouse,display
Today • How processes are created, managed and terminated • Sharing the computer (redux) • From file.c to a.out to running image • Library routines and shared libraries
Process creation in Unix/Linux • Each process starts life as a clone of its parent • Use the fork() system call to create a clone • When it’s born, each process inherits from its parent • Open files (related processes share a file pointer) • Environment variables • Many other things: e.g. current directory • An exact copy of all memory segments from the parent • The same code running at the same place, I.E. dropping through the fork! • Each copy can tell whether it is parent or child • Parent gets process ID as return value from fork • Child gets zero
Example of fork() system call intmain(intargc, char *argv[]) { pid_tchild_pid; /* child’s process id or zero */ fprintf(stderr,"PARENT: Parent has started\n"); child_pid= fork(); if (child_pid) { fprintf(stderr, "PARENT: my pid is %d and my parent is %d\n", getpid(), getppid()); wait(child_pid); fprintf(stderr,"PARENT: my child with pid=%d has died\n", child_pid); } else { fprintf(stderr,"CHILD: my pid is %d and my parent is %d\n", getpid(), getppid()); } } Using stderr instead of stdout because it’s unbuffered…output is never delayed
Print startup message and fork into two processes – a parent and a child Example of fork() system call intmain(intargc, char *argv[]) { pid_tchild_pid; /* child’s process id or zero */ fprintf(stderr,"PARENT: Parent has started\n"); child_pid= fork(); if (child_pid) { fprintf(stderr, "PARENT: my pid is %d and my parent is %d\n", getpid(), getppid()); wait(child_pid); fprintf(stderr,"PARENT: my child with pid=%d has died\n", child_pid); } else { fprintf(stderr,"CHILD: my pid is %d and my parent is %d\n", getpid(), getppid()); } } Using stderr instead of stdout because it’s unbuffered…output is never delayed
Example of fork() system call The parent gets the child’s process id..the child gets zero. intmain(intargc, char *argv[]) { pid_tchild_pid; /* child’s process id or zero */ fprintf(stderr,"PARENT: Parent has started\n"); child_pid= fork(); if (child_pid) { fprintf(stderr, "PARENT: my pid is %d and my parent is %d\n", getpid(), getppid()); wait(child_pid); fprintf(stderr,"PARENT: my child with pid=%d has died\n", child_pid); } else { fprintf(stderr,"CHILD: my pid is %d and my parent is %d\n", getpid(), getppid()); } } Using stderr instead of stdout because it’s unbuffered…output is never delayed
Example of fork() system call • In the parent… • Print a message • Wait for the child to complete its work • Announce that the child has been “reaped” • Exit (drop through) intmain(intargc, char *argv[]) { pid_tchild_pid; /* child’s process id or zero */ fprintf(stderr,"PARENT: Parent has started\n"); child_pid= fork(); if (child_pid) { fprintf(stderr, "PARENT: my pid is %d and my parent is %d\n", getpid(), getppid()); wait(child_pid); fprintf(stderr,"PARENT: my child with pid=%d has died\n", child_pid); } else { fprintf(stderr,"CHILD: my pid is %d and my parent is %d\n", getpid(), getppid()); } } Using stderr instead of stdout because it’s unbuffered…output is never delayed
Example of fork() system call • In the child… • Print a message • Exit intmain(intargc, char *argv[]) { pid_tchild_pid; /* child’s process id or zero */ fprintf(stderr,"PARENT: Parent has started\n"); child_pid= fork(); if (child_pid) { fprintf(stderr, "PARENT: my pid is %d and my parent is %d\n", getpid(), getppid()); wait(child_pid); fprintf(stderr,"PARENT: my child with pid=%d has died\n", child_pid); } else { fprintf(stderr,"CHILD: my pid is %d and my parent is %d\n", getpid(), getppid()); } } Using stderr instead of stdout because it’s unbuffered…output is never delayed
Example of fork() system call Remember: On a multi-core machine, the parent and the child may really be running at the same time! intmain(intargc, char *argv[]) { pid_tchild_pid; /* child’s process id or zero */ fprintf(stderr,"PARENT: Parent has started\n"); child_pid= fork(); if (child_pid) { fprintf(stderr, "PARENT: my pid is %d and my parent is %d\n", getpid(), getppid()); wait(child_pid); fprintf(stderr,"PARENT: my child with pid=%d has died\n", child_pid); } else { fprintf(stderr,"CHILD: my pid is %d and my parent is %d\n", getpid(), getppid()); } } Using stderr instead of stdout because it’s unbuffered…output is never delayed
OUTPUT $ fork1 PARENT: Parent has started PARENT: my pid is 26928 and my parent is 24979 CHILD: my pid is 26930 and my parent is 26928 PARENT: my child with pid=26930 has died $ Example of fork() system call intmain(intargc, char *argv[]) { pid_tchild_pid; /* child’s process id or zero */ fprintf(stderr,"PARENT: Parent has started\n"); child_pid= fork(); if (child_pid) { fprintf(stderr, "PARENT: my pid is %d and my parent is %d\n", getpid(), getppid()); wait(child_pid); fprintf(stderr,"PARENT: my child with pid=%d has died\n", child_pid); } else { fprintf(stderr,"CHILD: my pid is %d and my parent is %d\n", getpid(), getppid()); } } Using stderr instead of stdout because it’s unbuffered…output is never delayed
Example of fork() system call Question? What’s the parent’s parent? intmain(intargc, char *argv[]) { pid_tchild_pid; /* child’s process id or zero */ fprintf(stderr,"PARENT: Parent has started\n"); child_pid= fork(); if (child_pid) { fprintf(stderr, "PARENT: my pid is %d and my parent is %d\n", getpid(), getppid()); wait(child_pid); fprintf(stderr,"PARENT: my child with pid=%d has died\n", child_pid); } else { fprintf(stderr,"CHILD: my pid is %d and my parent is %d\n", getpid(), getppid()); } } Using stderr instead of stdout because it’s unbuffered…output is never delayed
$ ps PID TTY TIME CMD 24979 pts/22 00:00:00 tcsh 26834 pts/22 00:00:00 ps $ $ $ fork1 PARENT: Parent has started PARENT: my pid is 26928 and my parent is 24979 CHILD: my pid is 26930 and my parent is 26928 PARENT: my child with pid=26930 has died $ Example of fork() system call intmain(intargc, char *argv[]) { pid_tchild_pid; /* child’s process id or zero */ fprintf(stderr,"PARENT: Parent has started\n"); child_pid= fork(); if (child_pid) { fprintf(stderr, "PARENT: my pid is %d and my parent is %d\n", getpid(), getppid()); wait(child_pid); fprintf(stderr,"PARENT: my child with pid=%d has died\n", child_pid); } else { fprintf(stderr,"CHILD: my pid is %d and my parent is %d\n", getpid(), getppid()); } } Most commands have the shell as a parent! Using stderr instead of stdout because it’s unbuffered…output is never delayed
Some things to note about fork • Code for parent and child is the same • …we haven’t learned to run another program yet • Parent and child run in parallel • Child gets a copy of variables – changes are not seen by the parent • There is a tree of processes rooted at a special system “init” process, which is always pid=1 … every process except init has a parent! • Parent must wait for the child to die or it becomes a “zombie”
Zombies, process IDs and the process table • Every process has an ID (you’ve seen that) • Inside the kernel, there is a data structure known as a process descriptor for each process that hasn’t been “reaped” by a wait call from the parent • On 32 bit Linux systems, there are typically 32767 process IDs…if they get used up, the system can’t make new processes • Process IDs and descriptors can’t be reused until the parent has waited • Therefore: be sure to wait for the death of every process you create! • What happens if the parent dies without waiting? • Children get inherited by init, which will reap them • The big problem is if you keep running and don’t reap your children!
Killing a process • Send the process a kill signal to ask/tell it to die • How? • From another process: kill(victim_pid, SIGKILL) system call • From the console: kill -9 victim_pid • SIGKILL (integer value -9) is a magic signal that the victim cannot intercept: it will kill the process immediately • Other signals are used for many purposes...covered next week • The parent still must wait/reap or the result is a zombie…the shell will reap processes of commands it launches
Review: How can your process “call” the kernel? Your program can use system calls to ask the kernel for service (e.g. read, kill, etc.) Play Video Angry Birds Browser OPERATING SYSTEM KERNEL Filesystem, Graphics System, Window system, TCP/IP Networking, etc., etc. MEMORY CPU Printer Disk Keyboard,mouse,display
Signals: How the kernel can call your program! Before signal can be caught, child must issue to identify the handler function:signal(SIGNAME, handler_function) If no handler, OS supplies default behavior The kernel can cause a preset signal handler in your program to run…this alerts your program to some news from the kernel Parent Child OPERATING SYSTEM KERNEL MEMORY CPU
timeisup is the signal handler Signal handling example /* sigalarm.c */ #include <stdio.h> #include <signal.h> typedefenum { false, true } bool; int sleeping = false; void timeisup(int sig) { fprintf(stderr, "Quit bothering me, I was sleeping!\n", sig); sleeping = false; } main() { signal(SIGALRM,timeisup); alarm(5); /* Please wake me in 5 seconds */ sleeping = true; while (sleeping) { printf("zzz...\n"); sleep(1); } fprintf(stderr, "Dang, you woke me up!\n"); } Tell the OS to call the timeisup function when the alarm signal arrives.
Signal handling example /* sigalarm.c */ #include <stdio.h> #include <signal.h> typedefenum { false, true } bool; int sleeping = false; void timeisup(int sig) { fprintf(stderr, "Quit bothering me, I was sleeping!\n", sig); sleeping = false; } main() { signal(SIGALRM,timeisup); alarm(5); /* Please wake me in 5 seconds */ sleeping = true; while (sleeping) { printf("zzz...\n"); sleep(1); } fprintf(stderr, "Dang, you woke me up!\n"); } Tell the OS to send SIGALRM in 5 seconds
Signal handling example /* sigalarm.c */ #include <stdio.h> #include <signal.h> typedefenum { false, true } bool; int sleeping = false; void timeisup(int sig) { fprintf(stderr, "Quit bothering me, I was sleeping!\n", sig); sleeping = false; } main() { signal(SIGALRM,timeisup); alarm(5); /* Please wake me in 5 seconds */ sleeping = true; while (sleeping) { printf("zzz...\n"); sleep(1); } fprintf(stderr, "Dang, you woke me up!\n"); } QUESTION: does sleeping ever become false? Loop printing “zzz”
Signal handling example YES!! When the signal arrives! /* sigalarm.c */ #include <stdio.h> #include <signal.h> typedefenum { false, true } bool; int sleeping = false; void timeisup(int sig) { fprintf(stderr, "Quit bothering me, I was sleeping!\n", sig); sleeping = false; } main() { signal(SIGALRM,timeisup); alarm(5); /* Please wake me in 5 seconds */ sleeping = true; while (sleeping) { printf("zzz...\n"); sleep(1); } fprintf(stderr, "Dang, you woke me up!\n"); } QUESTION: does sleeping ever become false?
Advanced topic On most machines, this will work fine if you declare sleeping as an int, however… There are two issues in principle: The compiler working on main() needs to know that sleeping could get updated by code it’s not seeing (the handler – volatile warns it) For some data types, updating a value takes multiple instructions, and the alarm could ring while the data is in an inconsistent state. Very unlikely for an int, but sigatomic_t is guaranteed to be updated atomically Glad you asked?Lesson: asynchronous programming is tricky, and OS’s do it all the time! Signal handling example /* sigalarm.c */ #include <stdio.h> #include <signal.h> typedefenum { false, true } bool; volatile sigatomic_t sleeping = false; void timeisup(int sig) { fprintf(stderr, "Quit bothering me, I was sleeping!\n", sig); sleeping = false; } main() { signal(SIGALRM,timeisup); alarm(5); /* Please wake me in 5 seconds */ sleeping = true; while (sleeping) { printf("zzz...\n"); sleep(1); } fprintf(stderr, "Dang, you woke me up!\n"); }
One process can ask kernel to signal another Parent Child OPERATING SYSTEM KERNEL MEMORY CPU
One process can ask kernel to signal another Before signal can be caught, child must issue:signal(SIG_XXXX, handler_function) If no handler, OS supplies default behavior kill(SIG_XXXX, child_pid)Oddly, kill is used not just for kill signals, but for all signals! Parent Child OPERATING SYSTEM KERNEL handler_function()called MEMORY CPU
One process can ask kernel to signal another Before signal can be caught, child must issue:signal(SIGNAME, handler_function) If no handler, OS supplies default behavior kill(SIG_STP, child_pid)Oddly, kill is used not just for kill signals, but for all signals! Parent Child OPERATING SYSTEM KERNEL By the way, the shell has a kill command you can use to send signals to any of your processes (or other people’s processes if you have permission). Use “man 1 kill” for more info on the shell command, and “man 2 kill” & “man 2 signal” for the system calls . MEMORY CPU
Background and suspended processes • From most shells: • emacs myfile.txt shell (parent process) stays busy while Emacs runs • emacs myfile.txt & & says: run in background: let shell run while Emacs runs • If you start a program in the foreground and want to do something else • emacs myfile.txt shell (parent process) stays busy while Emacs runs • CTRL-Z: suspendEmacs and let shell run • Choices after CTRL-Z: fg puts job back in foreground; bg resumes it in background • To find out about running background jobs • Run the jobs command • Each job is named: %1, %2, etc. • You can do things like kill -9 %1 or fg %2 • Most of this is implemented with signals (e.g. CTRL-Z sends SIGSTP which by default pauses the process) – so you can do this from a program as well as from the shell Summary from Prof. Couch: “ typing ./a.outin the shell is an explicit wait. typing ./a.out&in the shell is a background execution. ”
Using exec to launch a new program • Fork creates parallel copies of the same program • Exec replaces the code for a process with a brand new program and calls its “main” function • Common idiom: to run a new program • fork() /* to create a child process */ • exec() /* have the child replace itself with the program to be run */ • [ optional: continue to do work in the parent while the child runs ] • wait(): /* in the parent for the new program to complete */ • Ever wondered where your return values from exit() go? • The are available to the parent via: pid = wait(int *child_exit_status) • So, the parent can find out if the child returned success or an error
Examples of fork/exec • Example: running a "cat" command in the foreground: • http://www.cs.tufts.edu/comp/111/examples/The_Visible_OS/wait1.c • Example: running a "cat" command in the foreground with explicit wait: • http://www.cs.tufts.edu/comp/111/examples/The_Visible_OS/wait2.c • Example: running a "cat" command in the background with implicit wait: • http://www.cs.tufts.edu/comp/111/examples/The_Visible_OS/wait3.c • Example: running a user-typed command in the foreground without arguments: • http://www.cs.tufts.edu/comp/111/examples/The_Visible_OS/shell.c • Example: running a user-typed command in the background without arguments: • http://www.cs.tufts.edu/comp/111/examples/The_Visible_OS/shell2.c These examples are from Prof. Couch’s lecture
Some things to watch with exec • Read the man page to find out the arguments it takes – there are several flavors • As with fork, the new program retains: • Open files, environment variables, current working directory, owner, etc., etc. • All data and variables from the caller are replaced – if you have buffered I/O that is be lost Consider the following: main() { printf("this won't get seen at all…"); execl("/bin/cat", "cat", "/dev/null", 0); } // prints nothing at all, because // the execl erases the unwritten line buffer!
Some things to watch with exec By default, stderr does not buffer it’s output… …as soon as you print it goes out. Try it! • Read the man page to find out the arguments it takes – there are several flavors • As with fork, the new program retains: • Open files, environment variables, current working directory, owner, etc., etc. • All data and variables from the caller are replaced – if you have buffered I/O that will be lost Consider the following: main() { printf("this won't get seen at all…"); fprintf(stderr, "this will get seen, because stderr ” “flushes buffers on each write…"); execl("/bin/cat", "cat", "/dev/null", 0); } /* prints “this will get seen, because stderr flushes buffers on each write” */ /* the execl erases the unwritten stdout buffer, but the stderr output is already done */
Today • How processes are created, managed and terminated • Sharing the computer (redux) • From file.c to a.out to running image • Library routines and shared libraries
Sharing the CPU Multiple Programs Running at once CPU is shared…can only do one thing at a time* Play Video Angry Birds Browser OPERATING SYSTEM MAIN MEMORY CPU *Modern multi-core CPUs can schedule one process/core at a time
Process scheduling (For now, assume we have a simple one core CPU) • If processes are ready to run, the OS picks one and runs it • The chosen process is in the runningstate • Processes have priority and high priority processes are run more often • The others are marked as ready (or runnable)(I.e. they’d like to run but need to wait their turn) • Some processes are healthy but waiting for something • Reasons: sleep(), waiting for I/O, wait(), select(), page fault • These processes are in the blocked state and they aren’t scheduled until that changes • Multicore CPUs: exactly the same, but we can have one running process on each core! Designing process schedulers is an art. The strategy that gives good interactive response on a shared server may not be what you need for a massive database system!
The five state process model “Run queue” (processes in line for CPU) Dispatch release Admit Exit New Ready Running Timeout Event wait Event occurs Blocked See: Stallings 7th Edition Page 118
In some OS’s, a blocked process can die without running any cleanup. The five state process model “Run queue” (processes in line for CPU) Dispatch release Admit Exit New Ready Running Timeout Event wait Event occurs Blocked See: Stallings 7th Edition Page 118
All programs share memory Sharing Memory Multiple Programs Running at once Play Video Angry Birds Browser OPERATING SYSTEM MAIN MEMORY CPU