670 likes | 684 Views
제 04 강 : Process Management. Process management. 1. Data structure 2. Functions. Kernel a.out. user a.out metadata* for user a.out. Process 1. PCB. Hardware. CPU. mem. disk. tty. CPU. mem. disk. tty.
E N D
제04강 : Process Management Process management 1. Data structure 2. Functions
Kernel a.out user a.out metadata* for user a.out Process 1 PCB Hardware CPU mem disk tty CPU mem disk tty * Metadata (管理者用 情報): 案內情報, 身上 情報, 敍述情報, ….
Meta-data for Process: PCB (Process Control Block) • PID (Process Identifier) • Priority (urgent) • Waiting Event (disk, KB) • Status (run, sleep, ..) • Location of image in disk • Location of image in memory • open files • directory • state vector save area (PC, R0 ..) • tty • parent, child process • execution time, .... a.out Actual Code Metadata & Environment for a.out (NOTE: In kernel a.out address space)
process tty Observation ... • # ttys/system a lot • # processes/tty (*1) of space • size of PCB/process (*2) for PCB’s • In UNIX, most processes interact with human • processes idle(swapped out) most of time • Minimize overhead while swapped out (*1) eg (sh, mail, ed, ....) => (50 tty), (3 process / tty) => 150 processes (*2) Kilo Bytes per PCB. Memory was expensive in 70’s
CPU -------- running Memory -------- ready sleep Disk -------- swapped out active (memory loaded) • UNIX is multi-user system • Many processes are loaded concurrently • Large space needed for PCB • Most UNIX processes interact with human Why not swap out • Processes idle most of time PCB’s too? (99.9% of time idle) • Swap out them (a.out) while idle • Save memory space
Per Process Data This part should remain in memory even if the rest is swapped out to disk with process image Otherwise, you cannot swap in later • PID • Priority • Waiting Event • Status • Location of image in disk • Location of image in memory • open files • directory • state vector save area (PC, R0, …) • tty • parent, child process • execution time, .... • ----up to Kilo Bytes -----
Per Process Data struct proc(few bytes) remain in memory all the time struct user (LARGE !) swapped out with a.out • PID • Priority • Waiting Event • Status • Location of image in disk • Location of image in memory • open files • directory • state vector save area (PC, R0,…) • tty • parent, child process • execution time, ....
struct proc • one per process • Always memory resident (never swapped out, always in memory) - PID - status, priority - waiting event - location of image in disk • data needed when process is not active (maybe in disk) • struct user • data needed only when process is active • called “u block” • always addressed by fixed virtual address (u) • since only one process is running on CPU at a time Most processes spend 99.9% time idle --> UNIX keeps only proc (few bytes) in core --> very low overhead
struct user { int u_rsav[2]; /* save r5,r6 when exchanging stacks */ int u_fsav[25]; /* save fp registers */ char u_uid; /* effective user id */ int u_procp; /* pointer to proc structure */ char *u_base; /* base address for IO */ char *u_count; /* bytes remaining for IO */ char *u_offset[2]; /* offset in file for IO */ int *u_cdir; /* pointer to inode of current directory */ char u_dbuf[DIRSIZ]; /* current pathname component */ char *u_dirp; /* current pointer to inode */ struct { /* current directory entry */ int u_ino; char u_name[DIRSIZ]; } u_dent; int *u_pdir; /* inode of parent directory of dirp */ int u_ofile[NOFILE] /* pointers to file structures of open files */ int u_arg[5]; /* arguments to current system call */ int u_stime; /* this process system time */ u_utime; /* this process user time */ } u;
struct proc { char p_stat; char p_flag; char p_pri; /* priority, negative is high */ char p_uid; /* user id, used to direct tty signals */ char p_time; /* resident time for scheduling */ int p_ttyp; /* controlling tty */ int p_pid; /* unique process id */ int p_ppid; /* process id of parent */ int p_addr; /* address of swappable image */ int p_size; /* size of swappable image (*64 bytes) */ int p_wchan; /* event process is awaiting */ } proc[NPROC]; /* stat codes */ #define SSLEEP 1 /* sleeping on high priority */ #define SWAIT 2 /* sleeping on low priority */ #define SRUN 3 /* running */
Kernel data structure for process user proc kernel stack User a.out Kernel a.out Process 1 Process 2 Process 3 user PCB PCB proc K-stack Hardware CPU mem disk tty CPU mem disk tty
Kernel data structure for process user proc kernel stack User a.out Kernel a.out Process 1 Process 2 Process 3 PCB PCB PCB ready queue (list) disk queue Hardware CPU mem disk tty CPU mem disk tty
Kernel data structure for process user proc kernel stack (1) P1 is executing P1’s a.out “P1 is running in user mode” usinguser stack in P1’s a.out P1 a.out P2 a.out P3 a.out (2) P1 is invokes system call “P1 is running in kernel mode” “kernel control path” usingkernel stackin kernel a.out kernel a.out
---PCB location of image current directory files, terminals …. --- chain of function calls ---Functions in kernel ---Stack frame for this function return address parameter local variables user proc K-stack sh mail user user Kernel Stack proc proc read( ) send( ) Global Variable kernel control path kernel control path CPU mem
sh Private data Private stack user proc Global Variable Shared data Shared code CPU mem
(kernel) stack • many processes invoke functions in kernel (system call) • OS kernel (a.out) is a shared code • kernel allocates one kernel stack per each process • private space while in kernel (for system call) • keep track of it’s own private execution state in kernel • Whereas, normal user a.out has only one stack • “user stack” • gcc can generate a.out in shared code format (option)
User a.out Kernel a.out Process 1 Process 2 Process 3 user PCB PCB proc K-stack Hardware CPU mem disk tty CPU mem disk tty
To create a new child process 1 Allocate & copy PCB in kernel space 2 Allocate & copy process image in memory (also link PCB to ready queue) ---------------------------------------- 3. Load a.out from disk 4. Initialize for new process Process 1 Process 2 Process 3 (1) user PCB PCB proc K-stack (4) (2) Note: 1, 3 …. only kernel can do them We need system call process_create() UNIX divides process_create() into two one --- fork() – does step 1 & 2 two --- exec() – does step 3 & 4 CPU mem disk tty CPU mem disk tty (3) Memory work Disk work
Parent child kernel kernel system callsfor process management [1] fork() system call (kernel function) • create a copy of parent (logical copy of parent) • Copy parent’s a.out image (just page table, ie pointers) • Copy parent’s PCB (struct user, proc) • all environment (struct user) are inherited • only PID differs (rest identical) • no sharing of memory • only way to inc number of processes struct user { ….. tty current dir open files loc of image } P A fork P A means both a.out & PCB P’A (Only PID differ)
Fork() copies parent’s PCB & image to child PCB & image Parent Child sh Process 2 user PCB proc K-stack fork() Hardware CPU mem disk tty CPU mem disk tty
P A P A fork P A exec P’A P B (New a.out ) [2] exec() system call • Retrieve a file from disk • overlay on old a.out image • Jump to start location • To create a new process: • Alloc & copy kernel data struct (u, proc,...) --- fork() • Alloc & copy a.out • put into ready queue • load a.out from disk --- exec() • init a.out
Alloc & copy kernel data struct (u, proc,...) • Alloc & copy a.out • put into ready queue fork() Example: fork() Try man fork --- returns twice (once to parent, once to child) main() {int pid; pid = fork(); if (pid = = 0) /* this is child */ printf(“\n Hello, I am child!\n”); else /* this is parent */ printf(“\n Hello, I am parent!\n”); } What is output?
Alloc & copy kernel data struct (u, proc,...) • Alloc & copy a.out • put into ready queue • Load a.out from disk • Init new a.out fork() exec() Example: exec() Try man exec main() {int pid; pid = fork(); if (pid = = 0) /* this is child */ {printf(“\n Hello, I am child! Now I’ll run date \n”); execlp(“/bin/date”, “/bin/date”, (char *) 0); } else /* this is parent */ if (pid > 0) printf(“\n Hello, I am parent!\n”); }
[3] wait() system call • If PA invokes wait()then kernelblocks PA (preempt CPU) • until child terminates (waiting for signal from child) • when child terminates, kernel wakes up parent • kernel puts parent into ready queue • parent is dispatched(give CPU) later • Parent may call wait() after creating child • eg $ ls /bin • eg $ ls /bin&
7. kernel dispatches sh later 1. fork 2. wait 6. sh is put into ready queue sh (parent) sh is sleeping Copy u, proc sh a.out ls (child) 3. shcode exec ls sh a.out is in ready queue 4. now load ls from the disk (overwrite sh code) ls runs ... later ... ls is done 5. child terminates (either by exit() system call or keyboard interrupt like kill) inform kernel Context switch Executing on CPU Ready Queue
7. kernel dispatches sh 1. fork 3. wait 6. sh is put into ready queue sh (parent) ls (child) 3. exec ls Concept of Timesharing User mode Kernel mode “Kernel control path”
More on exec • Kernel has only one exec system call • all other exec family functions are library • exec() • opens the the passed file • read first few bytes (magic number) • they indicate either the file is • “a.out” (load binary), or • “script (#!)” (text -- load interpreter’s binary first) (whose pathname follows #!) • build new execution stack (for a.out or sh interpreter) • (argv, envp) • start from child’s main() function
Booting • CPU locates loader program & load (bootstrapping) • loader looks for kernel a.out (in disk) & loads/run • kernel handcrafts process 0 (cannot use fork now) • process 0 (root process) • special type process without a.out image • used for subsequent scheduling • process 0 fork/exec process 1 (/etc/init) • process 1 (init process) • fork/exec sh for op console (single user mode) • sh lets operator to initialize -- file system check ...
Process hierarchy 0 f /e init f /e f /e sh No action yet for other tty’s for operator console <Single User Mode>
Process hierarchy 0 f /e init f /e f /e f /e f /e sh getty getty operator console 1. print ‘login’, then wait 2. user types userid 3. getty exec login (next page) 1. ‘login’, then wait <Multi-user Mode>
Process hierarchy 0 f /e init f /e f /e f /e f /e sh login getty for operator console 1. print ‘passwd’, then wait 2. user types passwd 3. login exec() sh (next page) <Multi-user Mode>
Process hierarchy 0 f /e init f /e f /e f /e f /e sh sh getty for operator console 1. sh ‘prompt’, wait, user types command 2. To execute command, sh creates child fork() -- child sh (copied parent’s image) wait() -- child runs -- parent sh sleeps
Process hierarchy 0 f /e init f /e f /e f /e f /e sh sh getty getty f /e for operator console mail
Process hierarchy 0 f /e init f /e f /e f /e f /e sh sh getty getty f /e for operator console mail Sometimes fork/exec Sometimes exec only Why? f /e ed
Q : why separate fork() and exec() and not create_process()? • A : Then • getty • login all have to be bundled in one • shell and they stay in memory all the time
6 run levels 0 Shutdown (reserved) 1 Single-user mode (reserved for administration) 2 Multi-user mode • Multi-user mode as network server • X – Window workstation • Reboot (reserved)
A B C ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- ------------- + • Overhead of fork • copy parent’s PCB to child’s • size --- Kilo bytes (thousands of memory read/write) • heavy-weight overhead • what if child runs only briefly after fork? • eg for (i =1 to 10) do {statements} /* assign one loop per CPU */ fork copy overhead >> parallel run gain !` CPU 0 …. CPU 1 (i = 1) CPU 10 (i =10) CPU 0
Per Process Data (revisited) Need to copy all fields for each CPU? No! Only “CPU related fields” ... such as state vector (PC, ….) status (run, done, ..) Not the rest (can share single copy) tty a.out directory, …. • PID • Priority • Waiting Event • Status • Location of image in disk • Location of image in memory • open files • directory • state vector save area • tty • parent, child process • execution time, .... CPU 0 …. CPU10 (i = 10) CPU 1 (i = 1) CPU 0
Thread Two parts : (1) “CPU related fields” ... such as state vector (PC, ….) status (run, done, ..) (2) all the rest (all other resources than CPU) tty a.out directory, …. • PID • Priority • Waiting Event • Status • state vector save area • Location of image in disk • Location of image in memory • open files • directory • tty • parent, child process • execution time, .... “thread” TSD: Thread Specific Data “task”
thread • Thread costs less to create (than process) • light weight • has only “CPU execution related” information • MACH concept of “thread & task” • Many different implementations • some are supported by kernel • others are supported by user library • some are real-time threads
Back to Process : Process Termination • Voluntary • program invokes exit() system call • Involuntary • signal(*) arrives (kill, break, …) from keyboard • interrupt kernel sets bit in user, proc (tty) • case) if target process is running on CPU • take action immediately • case) if target process is sleeping • take action when it wakes up (LATER) • * For more on signal refer to Chapter 10, Stevens, Advanced Programming in UNIX, Addison Wesley.
exit() system call • signals -- ignore them all • files -- close • image -- deallocate memory space • parent -- notify (send signal) • state -- set it ZOMBIE • Then kernel (while executing exit() system call) • takes away the CPU • gives CPU to other process • exit() calls kernel function swtch() to do this
Q: “my program never called exit() ?” A: “gcc puts it when main() { returns or last } is encountered in main()
Summary: system calls for process • fork create a child (copy) • exec overlay new image, analogous to goto • wait sleep until child is done • exit frees all the resources, notify parent
More on swtch() • Kernel internal function (not known outside a.out) • only other kernel functions call this • Called when kernel need to block the current process • Following system calls may invoke swtch() • read(), wait(), exit() …. • swtch(): • save current CPU state => write in struct user (of active process) (only one process is active, unique) • mark this process as SLEEP (in struct proc) • find next process to run (ready list -- highest priority process) • dispatch ==> load CPU registers (from new process’ struct user) new state (PC, SP, ….) => CPU ‘go’ CPU
Program execution (revisited) u k u k PA System call Return from kernel System call
Program execution (revisted) (1) System call read() u k PA
Program execution (revisted) (2) Insert disk request into disk I/O queue (Must wait till DMA is done) Calls swtch() to give up CPU (sleep inside kernel) (1) System call read() u k PA