scheduling 1 1
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(); 2
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(); 3
empirical evidence 8 0 374 01 210 012 30 0123 12 01234 3 012345 1 0123456 2 01234567 1 012345678 359 0123456789 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
logistics aside fjxed links on submission ‘task description’ Piazza link 7
last time shells POSIX redirection, pipelines assignment: checkpoint due Friday fjle descriptors per-process table of pointers to open fjle descriptions open(): assign new close(): set to NULL dup2(): assign one index to another read/write kernel bufgering POSIX choice: write usually waits to complete (if possible) POSIX choice: read waits for some data, but not everything 8
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 9
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 10
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. 11 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 12 ... ( 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 12 ... ( 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 12 ... ( 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 12 ... ( 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 12 ... ( 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 … 13
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) 14 // 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) 14 // 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 15
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 ) 16
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 ) 16
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 ) 16
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 ) 16
Recommend
More recommend