1 / 25

Discussions on hw3

Discussions on hw3. Objective To implement multitasking of 3 Tiny-UNIX processes on the SAPC Each process tries to output data to the COM2 output port. Process 1. Process 2. Process 3. Process 0. Details on the Processes. Processes 1, 2 and 3

ratley
Download Presentation

Discussions on hw3

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Discussions on hw3 • Objective • To implement multitasking of 3 Tiny-UNIX processes on the SAPC • Each process tries to output data to the COM2 output port Process 1 Process 2 Process 3 Process 0

  2. Details on the Processes • Processes 1, 2 and 3 • user processes doing outputs using syscall write and then exit • have output queues with length = 6 chars • Process 0 • kernel process turns on interrupt by setting IF =1. It then loops and calls the scheduler. It uses IF=0 when it enters critical regions and then IF=1when it exits • has a non-preemptive scheduler that will allow one (or more) process(es) to be blocked while another runs • the simple scheduler looks for user process (1,2, or 3) to run. If none, chooses 0

  3. Zombie Process • After a process (1, 2, or 3) exits, it is a "zombie" process. • The reason is: • All that is really left is its exit value, waiting to be picked up by its parent. • But there is no parent process to pick up.

  4. Program Operations • 3 user programs each outputting >6 chars to TTY • one user process runs until it has filled the output buffer, then blocks(call scheduler and looks for another process to run) • another process runs, also blocks because the output queue is full • a third runs, blocks and no user processes are left to run • so process 0 is chosen to run. As process 0 runs, output drains at interrupt level and the user processes are unblocked by a call to the scheduler from the TX int. handler. Process 0 calls the scheduler and it finds a process to run • the chosen user process runs and refills the output buffer, then blocks • the other two user processes run in turn and fill the output buffer and block again • process 0 runs again • over and over until the output is done

  5. startup0.s: Same as $pclibsrc/startup0.s - init stack User Program Flow Start from Tutor startup1.c: init kernel Init memory Part of tunix.c: new module -call ioinit, set_trap_gate(0x80, &syscall): -init PEntry[] for Proc 0,1,2,3 -call schedule() to start Proc 0 Init kernel sched.c: new module -schedule() ; sleep();wakeup() -call _ustart1, _ustart2, or _ustart3 Scheduler crt01.s, crt02.s,crt03.s: -entry points:_ustart1, _ustart2, _ustart3 -call _main1, _main2, or _main3 -call _exit Start-up Start-up Start-up User program User program uprog1.c,uprog2,uprog3: Modify from testio.c: -entry points: main1, main2, main3 -call write User program ulib.s: syscall lib setups; -add _write, _exit lib calls to do write, exit

  6. Kernel Program Flows sysentry.s: same as hw2 -push eax,ebx,ecx,edx on stack -call system call dispatcher: _syscallc -pop stack and iret Trap handler wrapper System call dispatcher Part of tunix.c: same as hw2 -entry point _syscallc -depend on syscall #, call the handler routine -need to modify sysexit to change state, and call schedule() System call trap handler routine tty.c: modify from hw2 -need to add debug_log -need to add wakeup at irqinthandc for tx interrupt -need to add sleep at ttywrite when queue is full

  7. Process Entry Table #define N_SAVED_REGS 7 /* 7 non-scratch CPU registers, saved in this order: */ #define ESP1 0x280000 /* process 1 stack starting address */ #define ESP2 0x290000 /* process 2 stack starting address */ #define ESP3 0x2a0000 /* process 3 stack starting address */ #define N_SAVED_REGS 7 enum cpu_regs {SAVED_ESP, SAVED_EBX, SAVED_ESI, SAVED_EDI, SAVED_EBP, SAVED_EFLAGS, SAVED_PC}; /* for p_status ( RUN=1, BLOCKED=2, ZOMBIE=3) */ typedef enum proc_status {RUN = 1, BLOCKED, ZOMBIE} ProcStatus; /* for p_waitcode, what the process is waiting for */ typedef enum proc_wait {TTY0_OUTPUT = 1, TTY1_OUTPUT} WaitCode; /* Process Entry */ typedef struct { int p_savedregs[N_SAVED_REGS]; /* saved non-scratch registers */ ProcStatus p_status; /* RUN, BLOCKED, or ZOMBIE */ WaitCode p_waitcode; /* valid only if status==BLOCKED: TTY0_OUT, etc. */ int p_exitval; /* valid only if status==ZOMBIE */ } PEntry;

  8. Process Entry Table(cont’d) /* the process table with four entries loaded with dummy values */ PEntry proctab[] = { {{0,0,0,0,0,0,0}, RUN, TTY1_OUTPUT, 0}, {{0,0,0,0,0,0,0}, RUN, TTY1_OUTPUT, 0}, {{0,0,0,0,0,0,0}, RUN, TTY1_OUTPUT, 0}, {{0,0,0,0,0,0,0}, RUN, TTY1_OUTPUT, 0}, }; proctab[3].p_savedregs[SAVED_ESP] = ESP3; proctab[3].p_savedregs[SAVED_PC] = (int) &ustart3; proctab[3].p_savedregs[SAVED_EFLAGS] = 0x1 << 9; proctab[3].p_savedregs[SAVED_EBP] = 0; proctab[3].p_status = RUN;

  9. debug_log function sprintf(buf, “^%c”, ch); debug_log(buf); • stores the report in memory starting at 0x300000 and then outputs them • If the program crashes, you can still use Tutor to see the log Tutor> md 300000 • use debug_log(“~”) to mark each interrupt debug_log(“e”) for an echo debug_log(“s”) for shutdown Tx interrupts

  10. debug_log function (cont’d) • For hw3, add debug_log() for process switches |(0-1) for switching from process 0 to process 1 |(1Z-2) for switching from process 1, now a zombie, to process 2 |(1b-2) for switching from process 1, now blocked, to process 2

  11. High address Stack newentryp oldpentryp return PC %esp after Low address Context Switching function: asmswtch.s .globl _asmswtch # asmswtch--process switch # (but we don't need to save C scratch reg's, since they are assumed # to be smashed by calling this anyway) # Call from C: two arguments, pointers to old and new PEntry's: # asmswtch(oldpentryp, newpentryp) # # Stack when reach here: # %esp--> return pc # 4(%esp) oldpentryp # 8(%esp) newpentryp # # # PEntry: saved esp # saved ebx # saved esi # saved edi # saved ebp # saved eflags # saved pc -- i.e., saved eip (where this was called from) %esp before

  12. Wait code Old Process Entry Proc status Save pc Save eflags High address Stack Save ebp newpentryp Save edi Save esi oldpentryp Save ebx return PC Save esp %esp Low address asmswtch.s (cont’d) +24 +20 _asmswtch: movl 4(%esp),%eax movl %esp, (%eax) movl %ebx, 4(%eax) movl %esi, 8(%eax) movl %edi, 12(%eax) movl %ebp, 16(%eax) movl (%esp), %ecx movl %ecx, 24(%eax) pushfl #push old eflags on stack popl 20(%eax)# pop it and store in loc movl 8(%esp), %eax movl (%eax), %esp movl 4(%eax), %ebx movl 8(%eax), %esi movl 12(%eax), %edi movl 16(%eax), %ebp pushl 20(%eax) # new eflags--push stored eflags on stack popfl # restore eflags movl 24(%eax), %ecx movl %ecx,(%esp) # restore top of stack=saved pc ret # return on newly reinstated stack +16 +12 +8 +4 +0

  13. sleep function: sleep() • Prototypevoid sleep(Waitcode event) • Block the calling process by setting the process status = BLOCKED and waitcode = event • Run the scheduler to start another process • Disable interrupt at the beginning and restore interrupt at the end

  14. wakeup function: wakeup() • Prototype: void wakeup(Waitcode event) • Go through all user processes. If process status = BLOCKED and waitcode = event, then change process status = RUN • Disable interrupt at the beginning and restore interrupt at the end

  15. Suggested Steps 1. Make sure your hw2 solution is fully working, or use the provided solution. Note that the provided solution has a "debug_log" service that writes notes to memory, and prints out the log when the kernel shuts down. Add this logging functionality to your own solution if you're using it for hw3. Note that you can add your own entries in the log to figure out what's happening. 2. Write trivial user programs that just kprintf a message each, and the crt0's for them that call mainx, and then do an exit syscall. Write a fake scheduler that just loops through proctab until it finds a status = RUN process and then calls it at ustartx. If there aren't any RUN ones left, bring down the system. Inside sysexit, call the scheduler after marking the process ZOMBIE. This simple system will work because each process only needs to run once. They use the same stack area one after another. Make sure your makefile is right: see that it rebuilds the right things after each edit. (mainx and ustartx where x =1,2,3)

  16. Suggested Steps(cont’d) 3. Now use the supplied asmswtch code to start each process. For each user process, you need to initialize the PEntry's saved-pc to _ustartx, and the saved-eflags to allow interrupts, and the saved-esp to the proper stack. Also the saved-ebx should be 0. In your new fake scheduler, loop through the proctab until you find a RUN user process (1, then 2, then 3) and then call asmswtch with old-process = proc 0, new-process = proc 1, to switch from proc 0 to proc 1, thus running uprog1. Later, when proc 1 exits, sysexit calls the scheduler again, which finds proc 2, and calls asmswtch with pointers for (proc1, proc2) to switch from proc 1 to proc 2, and that runs uprog2. When uprog2 exits, sysexit calls the scheduler... Eventually the scheduler calls asmswtch with (proc3, proc0) to switch back to proc 0 when no more RUN processes exist. Then you can shut down the system using process 0. (Of course you can do just part of this to start, then write a more finished product.) Make sure the user processes are each using their own stack. 4. Add a debug_log call to the scheduler to report on each process switch, for example, "|(2z-3)" for a switch from process 2, a zombie, to process 3.With just kprintf's in your user programs, your debug log should look like this now: |(0-1)|(1z-2)|(2z-3)|(3z-0)

  17. Suggested Steps(cont’d) 5. Add writes of one or two chars each to the three user programs and get this working--this output all fits in the 6-char queue, so no blocking is needed. This should work even though you haven't yet edited tty.c. Make sure the kernel waits for the output to drain while it's shutting down. Also make sure the user processes are running with interrupts enabled and using their appointed stack. You can check IF with get_eflags(), for example. Since the three writes return immediately, all the output will be done in the final kernel wait-for-output, and the debug log will look something like this (for 2 output chars for each process): |(0-1)~|(1z-2)~|(2z-3)|(3z-0)~~~~~s (Possibly more ~s will come earlier.) 6. Write the real scheduler after the class on this subject. Get it working in this too-easy user program environment. Change the code in tty.c to block and unblock at the right moments. Change to a 7-char output from one user program as soon as you want to try out blocking. When that works, try two, then all three with over-6 chars. Then the provided uprog's.

  18. More on hw3 • Define kernel global variables using PEntry structs proctab[], *curproc • There are 4 entries in proctab[]: for processes 0,1,2,3 • Processes 1,2,3 are user processes • Process 0 derives from the startup and is always runnable

  19. Major Kernel Functions • schedule() : • look for a user process to run. If not, run process 0. Call asmswtch. • Called from process 0: from startup, sleep and sysexit • sleep(event): • block the calling process by changing p_status to BLOCKED, p_waitcode to event and then calling schedule() • called from ttywrite (to replace the busy wait) • wakeup(event): • loop through proctab, changing all processes blocked on this event to p_status= RUN. • Called from tty output interrupt handler

  20. Interrupts for Major Functions • These are all critical codes and should be run with interrupt off (IF=0) • For wakeup(), it is only called from the interrupt handler with IF =0. Make sure the wakeup code does not set IF=1 • wakeup() does not call schedule(). It just set p_status = RUN

  21. Code Example on Critical Functions Save a copy of eflags register int sched(void){ … saved_eflags = get_eflags(); cli; …. asmswtch(…); set_eflags(saved_eflags); … } Restore the eflags register

  22. Decision code Call asmswtch return Schedule() Logic to determine which process to run Provide oldproc, curproc After return, it is running the new process

  23. More Details on sleep() • Modify ttywrite in hw2 to include sleep(): if (enqueue(…) = = FULLQUE) sleep(OUTPUT); /* can we enqueue now? Not for sure ! */ • Suppose there are 2 processes A, and B that have been sleeping. Then they are unblocked by the wakeup function. If A runs first and it fills up the output queue. B runs sometime later and finds the buffer full. It should go to sleep again. The above code does not work this way. • The correct code should look like: while (enqueue(…) = = FULLQUE) sleep(OUTPUT);

  24. More Details on mutex • In hw2, we use disabling/enabling interrupt (cli/sti) to protect concurrent modifications to the queue data structures • In hw3, we use disabling/enabling interrupts (cli/sti) in sleep(), wakeup(), schedule() • Where should we place the cli/sti’s? • Look at the following example:while (guarded_enqueue(…) = = FULLQUE) guarded_sleep(OUTPUT);where guarded_xxx is the xxx function surrounded by cli and sti Interrupt occurs here; runs dequeue which wakes up the task; Wakeup will be ignored because the task is going to sleep

  25. Solution to Lost Wakeup Problem • Expand the cli/sti pair:cli(); while (enqueue(…) = = FULLQUE) sleep(OUTPUT); sti(); • This works because sleep() calls schedule() which runs a new process with the new process’s EFLAGS • Need to exercise care when we use the above solution. Look at the following example:cli(); x=1; /* x is a global variable */ while (enqueue(…) = = FULLQUE) sleep(OUTPUT); /* what is value of x here? still =1? Not necessary */ sti();

More Related