EC440 Project 2 - User Mode Thread Library

EC440: Project 2 - User Mode Thread Library Project Goals ● To understand the idea of threads. ● To implement independent, parallel execution within a process

c/c++代写,java代写,python代写,matlab代写,作业代写,留学生作业代写

EC440: Project 2 - User Mode Thread Library Project Goals ● To understand the idea of threads. ● To implement independent, parallel execution within a process. ● You are implementing something that looks alot like real OS code. Collaboration policy ● You are encouraged to discuss this project with your classmates/instructors but are required to turn in your own solution. ● You must be able to fully explain your solution during oral examination. Deadline It is due on Friday, October 19, 23:59:59 EDT. Project Description The main deliverable for this project is a basic thread system for Linux. In the lectures, we learned that threads are independent units of execution that run (virtually) in parallel in the address space of a single process. As a result, they share the same heap memory, open files (file descriptors), process identifier, etc. Each thread has its own context, which consists of a set of CPU registers and a stack. The thread subsystem provides a set of library functions that applications may use to create, start and terminate threads, and manipulate them in various ways. The most well-known and widespread standard that specifies a set of interfaces for multi-threading programming on Unix-style operating systems is called POSIX threads (or pthreads). Recall that pthreads merely prescribes the interface of the threading functionality. The implementation then either implement user-mode threads, take advantage of kernel-mode threads (if provided by the operating system), or mix the two approaches. In this project, you will implement a small subset of the pthread API exclusively in user-mode. In particular, we aim to implement the following three functions from the pthread interface in user mode on Linux (prototypes and explanations partially taken from the respective man pages): int pthread_create( pthread_t thread, const pthread_attr_t attr, void (start_routine) (void ), void arg); The pthread_create() function creates a new thread within a process. Upon successful completion, pthread_create() stores the ID of the created thread in the location referenced by thread. In our implementation, the second argument (attr) shall always be NULL. The thread is created (i.e., your library must create a new Thread context, cf. slides ec440lecture04) and executes start_routine with arg as its sole argument. If the start_routine returns, the effect shall be as if there was an implicit call to pthread_exit() using the return value of start_routine as the exit status. Note that the thread in which main() was originally invoked differs from this. When it returns from main(), the effect shall be as if there was an implicit call to exit() using the return value of main() as the exit status. void pthread_exit(void *value_ptr); The pthread_exit() function terminates the calling thread. In our current implementation, we ignore the value passed in as the first argument (value_ptr) and clean up all information related to the terminating thread. The process shall exit with an exit status of 0 after the last thread has been terminated. The behavior shall be as if the implementation called exit() with a zero argument at thread termination time. pthread_t pthread_self(void); The pthread_self() function shall return the thread ID of the calling thread. For more details about error handling, please refer to the respective man pages. Submission Guidelines ● The threading library must be implemented in C ● To facilitate grading, you must include the pthreads header file (#include) in your source(s). We will compile your thread library against our test application that will call your pthread functions and check whether threads are properly started and terminated. ● Your makefile should compile your source files into an object threads.o file. Note, make sure to specify -m32, since some bits of below are dependant on 32 bit calling code convention. gcc/g++ -m32 -c -o threads.o threads.c ● In your home directory create a folder project2 and place all of your source files, makefile and README there. Switch to the project2 directory and execute submit2 ● A confirmation mail of your submission is sent to your account.. You can read this mail by executing mail. ● In the README file explain what you did. If you had problems, tell us why and what. ● You are allowed to resubmit your files. The latest submission before the deadline will be graded Oral Examination ● Deadline: Friday, October 26th , 18.00 EST ● You are required to meet with one member of the course staff (excluding Prof. Krieger) during office hours to explain your solution. Implementation ● You probably will need to use a data structure that can store information about the state of the thread (its set of registers), its stack (e.g., a pointer to the thread’s stack area), and status of the thread (whether it is running, ready to run, or has exited). This data structure is often referred to as a thread control block (TCB). As you may need to accommodate multiple threads in the same time (assume a maximum of 128 threads can be created), the thread control blocks should be stored in a list or a table (array). ● You should create a helper function that initializes your thread subsystem after the application calls pthread_create() for the first time. Before the call to the helper function, there is only one thread running (the main program). ● The process of picking another thread is called scheduling. You may cycle through the available threads in a round robin fashion, giving an equal, fair share to each thread. ● In order to switch between threads, the currently executing thread needs to call the setjump library function. setjmp will save the current state of the thread into a jmp_buf structure. Then your thread subsystem can pick another thread, use longjmp function along with saved jmp_buf to restore to a previously saved state and resume the execution of the new thread. ● Think about how often you want the thread of a program to call setjump (and thus, give up control of the CPU). In particular, application developers may be unaware of your thread implementation, so it is unlikely that they will periodically call setjmp and allow your scheduler to switch to a new process. To solve this issue, we can employ signals and alarms. We can use the ualarm or the setitimer function to set up a periodic timer that sends a SIGALRM signal every X milliseconds (assume that X=50ms for this project). Whenever the alarm goes off, the operating system will invoke the signal handler for SIGALRM. So, you can install your own, custom signal handler that performs the scheduling (switching between threads) for you. To install this signal handler, you should use sigaction with the SA_NODEFER flag. Otherwise (e.g., when using the deprecated signal function), alarms are automatically blocked while you are running the signal handler. ○ We require that your thread system supports thread preemption and switches between multiple threads that are ready. It is not okay to run each individual thread to completion before giving the next one a chance to execute. ○ To create a new thread, the system has to properly initialize the TCB for the new thread: create a new thread ID, allocate a new stack (malloc can come in handy) of 32,767 byte size and initialize the thread’s state so that it “resumes” execution from the start function that is given as argument to the pthread_create function. For this, we could use setjmp to save the state of the current thread in a jmp_buf, and then, modify this jmp_buf in two important ways. First, we want to change the program counter (the EIP) to point to the start function. Second, we want the stack pointer (the ESP) to point to the top of our newly allocated stack. ○ To modify the jmp_buf directly, we have to first understand that it is a very operating system and processor family-specific data structure that is typically not modified directly. On bandit, we can see the definition of the jmp_buf h ere in this header file: /usr/include/bits/setjmp.h (run cat /usr/include/bits/setjmp.h). Moreover, libc defines the following constants as the six integer elements of this structure: #define JB_BX 0 #define JB SI 1 #define JB DI 2 #define JB BP 3 #define JB SP 4 #define JB PC 5 ○ We can see that the stack pointer has index 4 and the program counter has index 5 into the jmp_buf. This allows us to easily write the new values for ESP and EIP into a jmp_buf. Unfortunately, there is a small complication on the Linux systems in the lab. These machines are equipped with a libc that includes a security feature to protect the addresses stored in jump buffers. This security feature “mangles” (i.e., encrypts) a pointer before saving it in a jmp_buf. Thus, we also have to mangle our new stack pointer and program counter before we can write it into a jump buffer, otherwise decryption (and subsequent uses) will fail. To mangle a pointer before writing it into the jump buffer, make use of the following function: ○ static int ptr_mangle(int p) { unsigned int ret; asm(“ movl %1, %%eax;n” “ xorl %%gs:0x18, %%eax;” “ roll $0x9, %%eax;” “ movl %%eax, %0;” : “=r”(ret) : “r”(p) : “%eax” ); return ret; } ○ Remember that the start routine of every new thread expects a single argument (a void pointer called arg). More precisely, the calling function would first put the argument onto the stack and the perform the call operation (which implicitly pushes the return address onto the stack). We have to basically “simulate” such a function call to the start routine. To this end, we just have to initialize the new stack for a thread by putting the argument on top of the stack, followed by the return address (which is the address of pthread_exit). Make sure to adjust the stack pointer (ESP) appropriately. ○ Once your stack is initialized and the (mangled) stack pointer (ESP) and program counter (EIP) are written to the jmp_buf, your new thread is all ready to go, and when the next SIGALRM arrives, it is ready to be scheduled! ○ In your solution you will have to use ptr_mangle to store the mangled stack and instruction pointers into the jmp_buf structure. If, during development, you want to inspect these values (e.g., to check you actually got a true jmp_buf) you can use the following counterpart to the above ptr_mangle function (not necessary just for convenience) static int ptr_demangle(int p) { unsigned int ret; asm( “ movl %1, %%eax;n” “ rorl $0x9, %%eax;” “ xorl %%gs:0x18, %%eax;” “ movl %%eax, %0;” : “=r”(ret) : “r”(p) : “%eax” ); return ret; }

留学生作业代写,cs作业代写,cs代写,作业代写,北美cs作业代写,澳洲cs作业代写,加拿大cs作业代写,cs作业代写价格,靠谱cs作业代写,程序代写
WeChat