Motivation Laboratory work in TDDI04 Introduction to Pintos Assignments 00, 0, 1 Viacheslav Izosimov 2009-02-02 viaiz@ida.liu.se Operating systems are nearly everywhere Crucial piece of software Perform concurrent tasks Synchronization C is the main language for real operating systems! Motivation Outline 1000000-5000000 LOC Nearly the first experience with a relatively large piece of software Direct jump from 1000 to 1000000 can be too difficult! Setting up the program environment Debugging of Pintos 5000-10000 LOC Lab 1 Pintos kernel and command line Execution of user programs in Pintos Argument passing to the user programs up to 1000 LOC Your programs so far. General questions Introduction to Pintos General description of labs Lab rules General algorithm for completing the labs Lab 0 Pintos. Real systems. Synchronization C pointers and address arithmetics General questions Lab lessons: More lessons than the last year! Exercises during lessons No quizzes Introduction to Pintos Labs: Groups of two students Sign up as soon as possible! Answer on the preparatory questions before implementing anything! Have your design ready before implementation! Work jointly! Shift from Nachos (1990s) to Pintos (2004) Pintos is the most up-to-date “toy operating” system Can boot as a real operating system Code is not as “messy” as in Nachos Pure C implementation Install on Sun machines during Lab #0 Pintos Extend during Lab #1 − #5 No cheating! 1 Introduction to Pintos Runs on x86 machine QEMU – a computer system emulator is used UNIX environment C implementation (both Kernel and user programs) Introduction to Pintos User programs User memory User stack Threads Kernel stack Synchronization User-exception handler primitives Memory management Scheduler Not complete… Interrupt handler Timer Your task! General Description of Labs Lab 00: “Introduction to C Programming” Checks your ability to complete the labs Lab 0: “Introduction and installation” Introductory lab, where you need to setup your program environment and try out debugging Lab 1: “Setting Up the Program Stack and Starting the First User Program” Learn how operating system kernel operates User program & arguments passing Simple synchronization tasks This lab is essential for the work on later labs and understanding of PintOS General Description of Labs Lab 5: “File system” Synchronization of read-write operations But before… File system General Description of Labs Lab 2: “System calls” System calls Single user program Console Lab 3: “Memory management, securing the system calls” Memory management & security issues Lab 4: “Execution, termination and synchronization of user programs” Handling program arguments Execution of several user programs Termination of a user program Synchronization of shared data structures Wait system call Lab Rules Every member of the group participate equally and every member should be able to answer questions related to labs Before doing the lab answer on preparatory questions on the lab page Understand first, then implement Try to find ”bugs” yourself at first No copying of source code from any sources, no cheating Pass assignments on time! 2 Lab Rules Pass assignments on time! Preliminary “self-control” schedule Lab 00, 0 – Friday! Lab 1 – 13th of February Lab 2 – 25th of February Lab 3 – 4th of March Lab 4 – 1st of April Lab 5 – 8th of May FINAL DEADLINE – 15th of May Lab 00 You will need to debug a small program “debugthis.c” with DDD after compiling it with GCC (not CC!) Create a linked list Learn how to browse the source code If you are not able to complete the lab, then you should focus on C before you proceed further or even consider to take the course next year. No formal pass/fail or other regulations though… Lab 0 You will have the following structure of directories pintos/src devices examples filesys lib misc tests threads userprog utils vm General Algorithm to Complete the Labs 1. Answer on all preparatory questions! 2. Understand what you are supposed to do (read instructions carefully) 3. Discuss all the issues with your lab partner to make sure that he/she also understands (That’s you future help!) 4. Understand the source code, which you are supposed to understand 5. Begin implementation! 6. Document carefully what you are doing (put date, name and short description around each new piece of code) 7. Work hard… and not only during lab hours, they are mostly for asking questions from lab assistants Lab 0 Set up the program environment (don’t forget!) module initadd ~TDDI04/labs2008/modules/pintos module add ~TDDI04/labs2008/modules/pintos Check if you have correct version of GCC and GDB module list GCC should be 3.4.x and you will know if GDB version is correct after completing Lab 00 Copy Pintos to your directory gzip -cd ~TDDI04/labs2008/pintos_ida.tar.gz | tar xvf - Lab 0 Compile and build Pintos cd pintos/src/threads gmake Test if Pintos works cd build pintos --qemu -- run alarm-multiple 3 Lab 0 cd ~/pintos/src/ gmake -C examples gmake -C userprog cd userprog/build pintos-mkdisk fs.dsk 2 pintos -v -p ../../examples/sumargv -a sumargv -- -f -q run sumargv Lab 0 Debugging (from build) To debug pintos you need two terminals. One to run pintos and one to run the debugger: pintos --qemu --gdb -- run testname ddd --gdb --debugger pintos-gdb kernel.o& Lab 0 Pintos Disk (1) Instead of the normal pintos command you run the command debugpintos: Dealing with Pintos disk In “userprog/build”: pintos-mkdisk fs.dsk 2 cd ??/build debugpintos -v -p ../../examples/sumargv -a sumargv --f -q run sumargv It will prepare pintos as usual, but stop and wait for the debugger to connect. In the debug terminal, run: This will create a file fs.dsk with a 2MB simulated disk in the directory. cd ??/build pintos-gdb kernel.o It starts gdb configured for pintos. In the debugger, enter the commands: debugpintos break main continue quit Format: pintos --qemu -- -f -q. Copy user programs: pintos --qemu -p programname -- -q with new name pintos --qemu -p programname -a newname -- -q Pintos Disk (2) Lab 1 Dealing with Pintos disk Example: Learn how operating system kernel operates pintos --qemu -p ../../examples/sumargv -a severe_student_program -- -q To copy a file from the disk: pintos --qemu -g programname -- -q or pintos --qemu -g programname -a newname -- -q * -p = put, -g = get To run: pintos --qemu -- run programname Example: pintos --qemu -- run severe_student_program Initialization Starting of the user program De-initialization User program & arguments passing The first user program Simple synchronization tasks Semaphores, Locks, Conditions This lab is essential for the work on later labs and understanding of PintOS Synchronization mechanisms 4 Lab 1 Lab 1 Your task is to study Pintos kernel and implement loading of the first user program and argument passing Argument passing is mostly about address arithmetics and learning the general structures used in the kernel There are two alternative implementations of the synchronization during the loading: Understand the source code in threads/init.c threads/synch.[h|c] threads/synchlist.[h|c] threads/thread.[h|c] userprog/process.[h|c] contains a skeleton of your implementations with Semaphores with Locks/Conditions Lab 1::Threads (1) A system contains several threads running in parallel Thread is a “basic unit of CPU utilization” §Thread ID §A program counter §A register set §A stack Thread 1 Lab 1::Threads (2) void SimpleThread(void * which) { int num; for (num = 0; num < 5; num++) { printf("*** thread %d looped %d times\n", (int)which, num); thread_yield(); Entering SimpleTest } *** thread 0 looped 0 times } void SimpleThreadTest(void) { char *t_name = "forked thread"; printf("Entering SimpleTest"); *** thread 1 looped 0 times *** thread 0 looped 1 times *** thread 1 looped 1 times … Thread 2 thread_create(t_name, PRI_MIN, SimpleThread, (void *)1); Thread execution sequence SimpleThread((void *)0); } Lab 1::Kernel Lab 1::Kernel 5 Kernel ex ec uti on ex ec uti on SP ini : sta tia ck liz ati on Signal / p sema_u pr oc e ge ss_ ne ex rat ecu e p te id : ex ec uti on User Program s ta r t_ pr oc ess (S P) SP :l oa din g Lab 1::Kernel & User Program (1) pr oc ess _e xe cu te Lab 1::Kernel (2) waiting until completion of start_process Wait / own sema_d process_execute(task); // starts the user program Critical Section Problem Critical Section: A set of instructions, operating on shared data or resources, that should be executed by a single process without interruption Atomicity of execution Mutual exclusion: At most one process should be allowed to operate inside at any time Consistency: inconsistent intermediate states of shared data not visible to other processes outside General structure, with structured control flow: Entry of critical section C … critical section C: operation on shared data Exit of critical section C Semaphores Semaphore S: shared integer variable two atomic operations to modify S: P() and V() Example: At most five passengers in the car P (S): while S <= 0 do Sleep(currentThread) S--; // Critical section V (S): WakeUp (sleepingThread) S++; Semaphores Semaphores // P(S) // V(S) void sema_down (struct semaphore *sema) { enum intr_level old_level; … old_level = intr_disable (); while (sema->value == 0) { list_push_back (&sema->waiters, &thread_current ()->elem); thread_block (); } sema->value--; intr_set_level (old_level); } void sema_up (struct semaphore *sema) { enum intr_level old_level; … old_level = intr_disable (); if (!list_empty (&sema->waiters)) thread_unblock (list_entry (list_pop_front (&sema->waiters), struct thread, elem)); sema->value++; Good for handling simple intr_set_level (old_level); } synchronization problems 6 Locks Conditions Binary semaphore (e.g. “taken” or “not taken”) Only the owner can release the lock Queuing mechanism on top of Locks Helps to manage the Locks Prevents deadlock situations Locks are often used together with Conditions Locks/Conditions are good for handling complex synchronization problems Advantage: No other thread can enter the critical section! Essential for security, for example, access to the shared data, arrays, lists, and etc. Problem: deadlock situations Example Lab 1::Part B Attention: never use a Condition with an empty Lock! pid = process ID tid = thread ID Lab 1::Kernel & User Program (2) Add your implementation of into process_execute() and process_start() in process.c process_execute() { start_process() { … loading – DONE! tid = thread_create initialization – DONE! … putting program generate pid from tid; arguments into stack wait until start_process(); signal to process_execute return pid or -1 } } pid = -1, if the program cannot load or run for any reason. Use an array or a list to keep track of pid:s. pid might equal tid, because we have only one thread per process. Limit the number of user programs (t.ex. 64 or 128). Lab 1::Stack (1) Lab 1::Stack (2) STEP 1. Parse the string: Use strtok_r(), prototyped in lib/string.h Read comments in lib/string.c or man page (run man strtok_r) Limit the number of arguments (for simplicity) Add command line parsing and stack initialization code here! STEP 2. Set up the stack: Necessary details about setting up the stack for this task you can find in Program Startup Details section of Pintos documentation. 7 Lab 1::Stack (3) Lab 1::Stack (4) nasty_program arg1 arg2 arg3 After parsing: nasty_program, arg1, arg2, arg3 Place the words at the top of the stack Align to 4-byte-words, add 0’s Reference the words through the pointers (pointers should point to the addresses of the words in the stack) Put the pointers to the stack (followed with NULL pointer) Lab 1::Stack (5) Put the address of the first pointer to the stack Put the number of words to the stack (the number of arguments + 1) Put “faked” return address to the stack (e.g. NULL). This is needed in order to meet x86 conventions for program arguments, even though this return address will not be used. So, what we get if we assume that PHYS_BASE is 0xc0000000 … Lab 1::Test Address Name 0xbffffffb argv[3][...] 0xbffffff6 argv[2][...] 0xbffffff1 argv[1][...] 0xbfffffe3 argv[0][...] 0xbfffffe0 word-align Data arg3\0 arg2\0 arg1\0 nasty_program\0 290--> 32 Type 0xbfffffdc 0xbfffffd8 argv[4] argv[3] 0 0xbffffffc char * char * 0xbfffffd4 argv[2] 0xbffffff8 char * 0xbfffffd0 0xbfffffcc argv[1] argv[0] 0xbffffff4 0xbfffffe6 char * char * 0xbfffffc8 argv 0xbfffffd0 char ** 0xbfffffc4 argc 0xbfffffc0 return address 4 0 void (*) () char[5] char[5] char[5] char[14] uint24_t int Exercise Time! To test your implementation use sumargv. In addition to sumargv the following tests should pass when you run gmake check if your implementation is correct: tests/userprog/args-none tests/userprog/args-single tests/userprog/args-multiple tests/userprog/args-many tests/userprog/args-dbl-space 8 Conclusions Introduction to Pintos and labs in general Labs 00, 0, and 1 Deadline for Lab #1 is 13th of February 9