316 lines
		
	
	
	
		
			9.7 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			316 lines
		
	
	
	
		
			9.7 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <title>L8</title>
 | |
| <html>
 | |
| <head>
 | |
| </head>
 | |
| <body>
 | |
| 
 | |
| <h1>Threads, processes, and context switching</h1>
 | |
| 
 | |
| <p>Required reading: proc.c (focus on scheduler() and sched()),
 | |
| setjmp.S, and sys_fork (in sysproc.c)
 | |
| 
 | |
| <h2>Overview</h2>
 | |
| 
 | |
| 
 | |
| <p>Big picture: more programs than processors.  How to share the
 | |
| limited number of processors among the programs?
 | |
| 
 | |
| <p>Observation: most programs don't need the processor continuously,
 | |
| because they frequently have to wait for input (from user, disk,
 | |
| network, etc.)
 | |
| 
 | |
| <p>Idea: when one program must wait, it releases the processor, and
 | |
| gives it to another program.
 | |
| 
 | |
| <p>Mechanism: thread of computation, an active active computation.  A
 | |
| thread is an abstraction that contains the minimal state that is
 | |
| necessary to stop an active and an resume it at some point later.
 | |
| What that state is depends on the processor.  On x86, it is the
 | |
| processor registers (see setjmp.S).
 | |
| 
 | |
| <p>Address spaces and threads: address spaces and threads are in
 | |
| principle independent concepts.  One can switch from one thread to
 | |
| another thread in the same address space, or one can switch from one
 | |
| thread to another thread in another address space.  Example: in xv6,
 | |
| one switches address spaces by switching segmentation registers (see
 | |
| setupsegs).  Does xv6 ever switch from one thread to another in the
 | |
| same address space? (Answer: yes, v6 switches, for example, from the
 | |
| scheduler, proc[0], to the kernel part of init, proc[1].)  In the JOS
 | |
| kernel we switch from the kernel thread to a user thread, but we don't
 | |
| switch kernel space necessarily.
 | |
| 
 | |
| <p>Process: one address space plus one or more threads of computation.
 | |
| In xv6 all <i>user</i> programs contain one thread of computation and
 | |
| one address space, and the concepts of address space and threads of
 | |
| computation are not separated but bundled together in the concept of a
 | |
| process.  When switching from the kernel program (which has multiple
 | |
| threads) to a user program, xv6 switches threads (switching from a
 | |
| kernel stack to a user stack) and address spaces (the hardware uses
 | |
| the kernel segment registers and the user segment registers).
 | |
| 
 | |
| <p>xv6 supports the following operations on processes:
 | |
| <ul>
 | |
| <li>fork; create a new process, which is a copy of the parent.
 | |
| <li>exec; execute a program
 | |
| <li>exit: terminte process
 | |
| <li>wait: wait for a process to terminate
 | |
| <li>kill: kill process
 | |
| <li>sbrk: grow the address space of a process.
 | |
| </ul>
 | |
| This interfaces doesn't separate threads and address spaces. For
 | |
| example, with this interface one cannot create additional threads in
 | |
| the same threads.  Modern Unixes provides additional primitives
 | |
| (called pthreads, POSIX threads) to create additional threads in a
 | |
| process and coordinate their activities.
 | |
| 
 | |
| <p>Scheduling.  The thread manager needs a method for deciding which
 | |
| thread to run if multiple threads are runnable.  The xv6 policy is to
 | |
| run the processes round robin. Why round robin?  What other methods
 | |
| can you imagine?
 | |
| 
 | |
| <p>Preemptive scheduling.  To force a thread to release the processor
 | |
| periodically (in case the thread never calls sleep), a thread manager
 | |
| can use preemptive scheduling.  The thread manager uses the clock chip
 | |
| to generate periodically a hardware interrupt, which will cause
 | |
| control to transfer to the thread manager, which then can decide to
 | |
| run another thread (e.g., see trap.c).
 | |
| 
 | |
| <h2>xv6 code examples</h2>
 | |
| 
 | |
| <p>Thread switching is implemented in xv6 using setjmp and longjmp,
 | |
| which take a jumpbuf as an argument.  setjmp saves its context in a
 | |
| jumpbuf for later use by longjmp.  longjmp restores the context saved
 | |
| by the last setjmp.  It then causes execution to continue as if the
 | |
| call of setjmp has just returned 1.
 | |
| <ul>
 | |
| <li>setjmp saves: ebx, exc, edx, esi, edi, esp, ebp, and eip.
 | |
| <li>longjmp restores them, and puts 1 in eax!
 | |
| </ul>
 | |
| 
 | |
| <p> Example of thread switching: proc[0] switches to scheduler:
 | |
| <ul>
 | |
| <li>1359: proc[0] calls iget, which calls sleep, which calls sched.
 | |
| <li>2261: The stack before the call to setjmp in sched is:
 | |
| <pre>
 | |
| CPU 0:
 | |
| eax: 0x10a144   1089860
 | |
| ecx: 0x6c65746e 1818588270
 | |
| edx: 0x0        0
 | |
| ebx: 0x10a0e0   1089760
 | |
| esp: 0x210ea8   2166440
 | |
| ebp: 0x210ebc   2166460
 | |
| esi: 0x107f20   1081120
 | |
| edi: 0x107740   1079104
 | |
| eip: 0x1023c9  
 | |
| eflags 0x12      
 | |
| cs:  0x8       
 | |
| ss:  0x10      
 | |
| ds:  0x10      
 | |
| es:  0x10      
 | |
| fs:  0x10      
 | |
| gs:  0x10      
 | |
|    00210ea8 [00210ea8]  10111e
 | |
|    00210eac [00210eac]  210ebc
 | |
|    00210eb0 [00210eb0]  10239e
 | |
|    00210eb4 [00210eb4]  0001
 | |
|    00210eb8 [00210eb8]  10a0e0
 | |
|    00210ebc [00210ebc]  210edc
 | |
|    00210ec0 [00210ec0]  1024ce
 | |
|    00210ec4 [00210ec4]  1010101
 | |
|    00210ec8 [00210ec8]  1010101
 | |
|    00210ecc [00210ecc]  1010101
 | |
|    00210ed0 [00210ed0]  107740
 | |
|    00210ed4 [00210ed4]  0001
 | |
|    00210ed8 [00210ed8]  10cd74
 | |
|    00210edc [00210edc]  210f1c
 | |
|    00210ee0 [00210ee0]  100bbc
 | |
|    00210ee4 [00210ee4]  107740
 | |
| </pre>
 | |
| <li>2517: stack at beginning of setjmp:
 | |
| <pre>
 | |
| CPU 0:
 | |
| eax: 0x10a144   1089860
 | |
| ecx: 0x6c65746e 1818588270
 | |
| edx: 0x0        0
 | |
| ebx: 0x10a0e0   1089760
 | |
| esp: 0x210ea0   2166432
 | |
| ebp: 0x210ebc   2166460
 | |
| esi: 0x107f20   1081120
 | |
| edi: 0x107740   1079104
 | |
| eip: 0x102848  
 | |
| eflags 0x12      
 | |
| cs:  0x8       
 | |
| ss:  0x10      
 | |
| ds:  0x10      
 | |
| es:  0x10      
 | |
| fs:  0x10      
 | |
| gs:  0x10      
 | |
|    00210ea0 [00210ea0]  1023cf   <--- return address (sched)
 | |
|    00210ea4 [00210ea4]  10a144
 | |
|    00210ea8 [00210ea8]  10111e
 | |
|    00210eac [00210eac]  210ebc
 | |
|    00210eb0 [00210eb0]  10239e
 | |
|    00210eb4 [00210eb4]  0001
 | |
|    00210eb8 [00210eb8]  10a0e0
 | |
|    00210ebc [00210ebc]  210edc
 | |
|    00210ec0 [00210ec0]  1024ce
 | |
|    00210ec4 [00210ec4]  1010101
 | |
|    00210ec8 [00210ec8]  1010101
 | |
|    00210ecc [00210ecc]  1010101
 | |
|    00210ed0 [00210ed0]  107740
 | |
|    00210ed4 [00210ed4]  0001
 | |
|    00210ed8 [00210ed8]  10cd74
 | |
|    00210edc [00210edc]  210f1c
 | |
| </pre>
 | |
| <li>2519: What is saved in jmpbuf of proc[0]?
 | |
| <li>2529: return 0!
 | |
| <li>2534: What is in jmpbuf of cpu 0?  The stack is as follows:
 | |
| <pre>
 | |
| CPU 0:
 | |
| eax: 0x0        0
 | |
| ecx: 0x6c65746e 1818588270
 | |
| edx: 0x108aa4   1084068
 | |
| ebx: 0x10a0e0   1089760
 | |
| esp: 0x210ea0   2166432
 | |
| ebp: 0x210ebc   2166460
 | |
| esi: 0x107f20   1081120
 | |
| edi: 0x107740   1079104
 | |
| eip: 0x10286e  
 | |
| eflags 0x46      
 | |
| cs:  0x8       
 | |
| ss:  0x10      
 | |
| ds:  0x10      
 | |
| es:  0x10      
 | |
| fs:  0x10      
 | |
| gs:  0x10      
 | |
|    00210ea0 [00210ea0]  1023fe
 | |
|    00210ea4 [00210ea4]  108aa4
 | |
|    00210ea8 [00210ea8]  10111e
 | |
|    00210eac [00210eac]  210ebc
 | |
|    00210eb0 [00210eb0]  10239e
 | |
|    00210eb4 [00210eb4]  0001
 | |
|    00210eb8 [00210eb8]  10a0e0
 | |
|    00210ebc [00210ebc]  210edc
 | |
|    00210ec0 [00210ec0]  1024ce
 | |
|    00210ec4 [00210ec4]  1010101
 | |
|    00210ec8 [00210ec8]  1010101
 | |
|    00210ecc [00210ecc]  1010101
 | |
|    00210ed0 [00210ed0]  107740
 | |
|    00210ed4 [00210ed4]  0001
 | |
|    00210ed8 [00210ed8]  10cd74
 | |
|    00210edc [00210edc]  210f1c
 | |
| </pre>
 | |
| <li>2547: return 1! stack looks as follows:
 | |
| <pre>
 | |
| CPU 0:
 | |
| eax: 0x1        1
 | |
| ecx: 0x108aa0   1084064
 | |
| edx: 0x108aa4   1084068
 | |
| ebx: 0x10074    65652
 | |
| esp: 0x108d40   1084736
 | |
| ebp: 0x108d5c   1084764
 | |
| esi: 0x10074    65652
 | |
| edi: 0xffde     65502
 | |
| eip: 0x102892  
 | |
| eflags 0x6       
 | |
| cs:  0x8       
 | |
| ss:  0x10      
 | |
| ds:  0x10      
 | |
| es:  0x10      
 | |
| fs:  0x10      
 | |
| gs:  0x10      
 | |
|    00108d40 [00108d40]  10231c
 | |
|    00108d44 [00108d44]  10a144
 | |
|    00108d48 [00108d48]  0010
 | |
|    00108d4c [00108d4c]  0021
 | |
|    00108d50 [00108d50]  0000
 | |
|    00108d54 [00108d54]  0000
 | |
|    00108d58 [00108d58]  10a0e0
 | |
|    00108d5c [00108d5c]  0000
 | |
|    00108d60 [00108d60]  0001
 | |
|    00108d64 [00108d64]  0000
 | |
|    00108d68 [00108d68]  0000
 | |
|    00108d6c [00108d6c]  0000
 | |
|    00108d70 [00108d70]  0000
 | |
|    00108d74 [00108d74]  0000
 | |
|    00108d78 [00108d78]  0000
 | |
|    00108d7c [00108d7c]  0000
 | |
| </pre>
 | |
| <li>2548: where will longjmp return? (answer: 10231c, in scheduler)
 | |
| <li>2233:Scheduler on each processor selects in a round-robin fashion the
 | |
|   first runnable process.  Which process will that be? (If we are
 | |
|   running with one processor.)  (Ans: proc[0].)
 | |
| <li>2229: what will be saved in cpu's jmpbuf?
 | |
| <li>What is in proc[0]'s jmpbuf? 
 | |
| <li>2548: return 1. Stack looks as follows:
 | |
| <pre>
 | |
| CPU 0:
 | |
| eax: 0x1        1
 | |
| ecx: 0x6c65746e 1818588270
 | |
| edx: 0x0        0
 | |
| ebx: 0x10a0e0   1089760
 | |
| esp: 0x210ea0   2166432
 | |
| ebp: 0x210ebc   2166460
 | |
| esi: 0x107f20   1081120
 | |
| edi: 0x107740   1079104
 | |
| eip: 0x102892  
 | |
| eflags 0x2       
 | |
| cs:  0x8       
 | |
| ss:  0x10      
 | |
| ds:  0x10      
 | |
| es:  0x10      
 | |
| fs:  0x10      
 | |
| gs:  0x10      
 | |
|    00210ea0 [00210ea0]  1023cf   <--- return to sleep
 | |
|    00210ea4 [00210ea4]  108aa4
 | |
|    00210ea8 [00210ea8]  10111e
 | |
|    00210eac [00210eac]  210ebc
 | |
|    00210eb0 [00210eb0]  10239e
 | |
|    00210eb4 [00210eb4]  0001
 | |
|    00210eb8 [00210eb8]  10a0e0
 | |
|    00210ebc [00210ebc]  210edc
 | |
|    00210ec0 [00210ec0]  1024ce
 | |
|    00210ec4 [00210ec4]  1010101
 | |
|    00210ec8 [00210ec8]  1010101
 | |
|    00210ecc [00210ecc]  1010101
 | |
|    00210ed0 [00210ed0]  107740
 | |
|    00210ed4 [00210ed4]  0001
 | |
|    00210ed8 [00210ed8]  10cd74
 | |
|    00210edc [00210edc]  210f1c
 | |
| </pre>
 | |
| </ul>
 | |
| 
 | |
| <p>Why switch from proc[0] to the processor stack, and then to
 | |
|   proc[0]'s stack?  Why not instead run the scheduler on the kernel
 | |
|   stack of the last process that run on that cpu? 
 | |
| 
 | |
| <ul>
 | |
| 
 | |
| <li>If the scheduler wanted to use the process stack, then it couldn't
 | |
|   have any stack variables live across process scheduling, since
 | |
|   they'd be different depending on which process just stopped running.
 | |
| 
 | |
| <li>Suppose process p goes to sleep on CPU1, so CPU1 is idling in
 | |
|   scheduler() on p's stack. Someone wakes up p. CPU2 decides to run
 | |
|   p. Now p is running on its stack, and CPU1 is also running on the
 | |
|   same stack. They will likely scribble on each others' local
 | |
|   variables, return pointers, etc.
 | |
| 
 | |
| <li>The same thing happens if CPU1 tries to reuse the process's page
 | |
| tables to avoid a TLB flush.  If the process gets killed and cleaned
 | |
| up by the other CPU, now the page tables are wrong.  I think some OSes
 | |
| actually do this (with appropriate ref counting).
 | |
| 
 | |
| </ul>
 | |
| 
 | |
| <p>How is preemptive scheduling implemented in xv6?  Answer see trap.c
 | |
|   line 2905 through 2917, and the implementation of yield() on sheet
 | |
|   22.
 | |
| 
 | |
| <p>How long is a timeslice for a user process?  (possibly very short;
 | |
|   very important lock is held across context switch!)
 | |
| 
 | |
| </body>
 | |
| 
 | |
| 
 | |
| 
 | 
