POSIX API (fjnish) / Scheduling intro 1
last time shells: program for users to run other programs fjles: open before use, read/write bytes, explicit close fjle descriptor: index into per-process table fork: copy table same index refers to same open fjle not deep copy — shared ofgsets, etc. dup2: assign one entry to another close: deallocate table entry pipe: create pair of connected fjle descriptors 2
shell assignment corrections make archive versus make submit phrasing on outputting exit statuses output must be in order of pipeline don’t care how you actually wait for commands (only that you do) 3
Unix API summary spawn and wait for program: fork (copy), then in child: setup, then execv , etc. (replace copy) in parent: waitpid fjles: open, read and/or write, close one interface for regular fjles, pipes, network, devices, … fjle descriptors are indices into per-process array index 0, 1, 2 = stdin, stdout, stderr dup2 — assign one index to another close — deallocate index redirection/pipelines open() or pipe() to create new fjle descriptors dup2 in child to assign fjle descriptor to index 0, 1 4
exercise ssize_t count = read(pipe_fds[0], buffer, 10); F. A, B, and C E. A and C D. A and B C. (nothing) B. 0 A. 0123456789 Which of these are possible outputs (if pipe, read, write, fork don’t fail) ? } printf("%c", buffer[i]); for ( int i = 0; i < count; ++i) { char buffer[10]; int pipe_fds[2]; pipe(pipe_fds); close(pipe_fds[1]); } exit(0); } write(pipe_fds[1], &c, 1); char c = '0' + i; for ( int i = 0; i < 10; ++i) { close(pipe_fds[0]); if (p == 0) { pid_t p = fork(); 5
exercise ssize_t count = read(pipe_fds[0], buffer, 10); F. A, B, and C E. A and C D. A and B C. (nothing) B. 0 A. 0123456789 Which of these are possible outputs (if pipe, read, write, fork don’t fail) ? } printf("%c", buffer[i]); for ( int i = 0; i < count; ++i) { char buffer[10]; int pipe_fds[2]; pipe(pipe_fds); close(pipe_fds[1]); } exit(0); } write(pipe_fds[1], &c, 1); char c = '0' + i; for ( int i = 0; i < 10; ++i) { close(pipe_fds[0]); if (p == 0) { pid_t p = fork(); 5
partial reads read returning 0 always means end-of-fjle by default, read always waits if no input available yet but can set read to return error instead of waiting read can return less than requested if not available e.g. child hasn’t gotten far enough 6
next topic: processes and scheduling 7
xv6: process table struct { struct spinlock lock; struct proc proc[NPROC] } ptable; fjxed size array of all processes lock to keep more than one thing from accessing it at once rule: don’t change a process’s state (RUNNING, etc.) without ‘acquiring’ lock 8
xv6: allocating a struct proc acquire(&ptable.lock); for (p = ptable.proc; p < &ptable.proc[NPROC]; p++) goto found; release(&ptable.lock); just search for PCB with “UNUSED” state not found? fork fails if found — allocate memory, etc. 9 if (p − >state == UNUSED)
xv6: creating the fjrst process // Set up first user process. set process as runnable set initial stack pointer to start at address 0 modify user registers calls execv() of /init hard-coded “initial program” load into user memory setup to return from swtch, then from exception struct proc with initial kernel stack ... // beginning of initcode.S 10 ... ( int )_binary_initcode_size); ... initproc = p; extern char _binary_initcode_start[], _binary_initcode_size[]; { userinit( void ) void struct proc *p; p = allocproc(); inituvm(p − >pgdir, _binary_initcode_start, p − >tf − >esp = PGSIZE; p − >tf − >eip = 0; p − >state = RUNNABLE;
xv6: creating the fjrst process // Set up first user process. set process as runnable set initial stack pointer to start at address 0 modify user registers calls execv() of /init hard-coded “initial program” load into user memory setup to return from swtch, then from exception struct proc with initial kernel stack ... // beginning of initcode.S 10 ... ( int )_binary_initcode_size); ... initproc = p; p = allocproc(); extern char _binary_initcode_start[], _binary_initcode_size[]; { userinit( void ) void struct proc *p; inituvm(p − >pgdir, _binary_initcode_start, p − >tf − >esp = PGSIZE; p − >tf − >eip = 0; p − >state = RUNNABLE;
xv6: creating the fjrst process // Set up first user process. set process as runnable set initial stack pointer to start at address 0 modify user registers calls execv() of /init hard-coded “initial program” load into user memory setup to return from swtch, then from exception struct proc with initial kernel stack ... // beginning of initcode.S 10 ... ( int )_binary_initcode_size); ... initproc = p; p = allocproc(); extern char _binary_initcode_start[], _binary_initcode_size[]; { userinit( void ) void struct proc *p; inituvm(p − >pgdir, _binary_initcode_start, p − >tf − >esp = PGSIZE; p − >tf − >eip = 0; p − >state = RUNNABLE;
xv6: creating the fjrst process // Set up first user process. set process as runnable set initial stack pointer to start at address 0 modify user registers calls execv() of /init hard-coded “initial program” load into user memory setup to return from swtch, then from exception struct proc with initial kernel stack ... // beginning of initcode.S 10 ... ( int )_binary_initcode_size); ... initproc = p; p = allocproc(); extern char _binary_initcode_start[], _binary_initcode_size[]; { userinit( void ) void struct proc *p; inituvm(p − >pgdir, _binary_initcode_start, p − >tf − >esp = PGSIZE; p − >tf − >eip = 0; p − >state = RUNNABLE;
xv6: creating the fjrst process // Set up first user process. set process as runnable set initial stack pointer to start at address 0 modify user registers calls execv() of /init hard-coded “initial program” load into user memory setup to return from swtch, then from exception struct proc with initial kernel stack ... // beginning of initcode.S 10 ... ( int )_binary_initcode_size); ... initproc = p; p = allocproc(); extern char _binary_initcode_start[], _binary_initcode_size[]; { userinit( void ) void struct proc *p; inituvm(p − >pgdir, _binary_initcode_start, p − >tf − >esp = PGSIZE; p − >tf − >eip = 0; p − >state = RUNNABLE;
threads versus processes for now — each process has one thread Anderson-Dahlin talks about thread scheduling thread = part that gets run on CPU saved register values (including own stack pointer) save program counter rest of process address space (accessible memory) open fjles current working directory … 11
xv6 processes versus threads // Process ID }; // Process name (debugging) char name[16]; // Current directory // Open files // If non-zero, have been killed int killed; // If non-zero, sleeping on chan // swtch() here to run process // Trap frame for current syscall xv6: one thread per process // Parent process int pid; // Process state so part of the process control block is really a thread control block // Per-process state struct proc { uint sz; // Size of process memory (bytes) 12 // Bottom of kernel stack for this process // Page table enum procstate state; pde_t* pgdir; char *kstack; struct proc *parent; struct trapframe *tf; struct context *context; void *chan; struct file *ofile[NOFILE]; struct inode *cwd;
xv6 processes versus threads // Process ID }; // Process name (debugging) char name[16]; // Current directory // Open files // If non-zero, have been killed int killed; // If non-zero, sleeping on chan // swtch() here to run process // Trap frame for current syscall xv6: one thread per process // Parent process int pid; // Process state so part of the process control block is really a thread control block // Per-process state struct proc { uint sz; // Size of process memory (bytes) 12 // Bottom of kernel stack for this process // Page table enum procstate state; pde_t* pgdir; char *kstack; struct proc *parent; struct trapframe *tf; struct context *context; void *chan; struct file *ofile[NOFILE]; struct inode *cwd;
single and multithread processes registers … stack stack stack registers registers PC code PC PC … … … multi-threaded process data … thread code thread thread thread fjles pid … data pid … stack registers PC … single-threaded process fjles 13
thread states fjnished done except for being waited for need external event to happen actually on CPU could be put on CPU being created, but not ready (xv6: ZOMBIE ) (xv6: SLEEPING ) new waiting (xv6: RUNNING ) running (xv6: RUNNABLE ) ready (xv6: EMBRYO ) 14
thread states fjnished done except for being waited for need external event to happen actually on CPU could be put on CPU being created, but not ready (xv6: ZOMBIE ) (xv6: SLEEPING ) new waiting (xv6: RUNNING ) running (xv6: RUNNABLE ) ready (xv6: EMBRYO ) 14
thread states fjnished done except for being waited for need external event to happen actually on CPU could be put on CPU being created, but not ready (xv6: ZOMBIE ) (xv6: SLEEPING ) new waiting (xv6: RUNNING ) running (xv6: RUNNABLE ) ready (xv6: EMBRYO ) 14
thread states fjnished done except for being waited for need external event to happen actually on CPU could be put on CPU being created, but not ready (xv6: ZOMBIE ) (xv6: SLEEPING ) new waiting (xv6: RUNNING ) running (xv6: RUNNABLE ) ready (xv6: EMBRYO ) 14
Recommend
More recommend