Docsity
Docsity

Prepare for your exams
Prepare for your exams

Study with the several resources on Docsity


Earn points to download
Earn points to download

Earn points by helping other students or get them with a premium plan


Guidelines and tips
Guidelines and tips

Multi-threading and Consistency in Systems Design: A Case Study, Exams of Programming Languages

A case study on multi-threading and consistency in systems design, focusing on the use of volatile variables and memory barriers in ensuring mutual exclusion and thread safety. The document also discusses the end-to-end argument and its relevance to systems design, as well as the importance of systems programming languages in handling fixed memory locations and side effects.

Typology: Exams

Pre 2010

Uploaded on 07/22/2009

koofers-user-tg6
koofers-user-tg6 🇺🇸

10 documents

1 / 8

Toggle sidebar

Related documents


Partial preview of the text

Download Multi-threading and Consistency in Systems Design: A Case Study and more Exams Programming Languages in PDF only on Docsity! cs591: Test 1 Rolf Riesen October 4, 2007 1 General • Don’t forget to put your name on each answer sheet! • Open book, open notes, open papers, but no laptops or other electronic devices with search or Internet capability. • Time: About 70 minutes. • Most likely, there are more questions on this exam than you can answer during this class period. Answer as many as you can, starting with the ones in areas you are most confident about. The goal is to earn as many points as possible. 2 Questions About Memory Models and Consistency 2.1 The C volatile Keyword and Memory Consistency 10 points Show how the C volatile keyword can be used to enforce consistency. Consider the code fragments in Listing 1 and 2. They are correct in a sequentially consistent system. Modern compilers and CPUs tend to reorder writes for performance reasons. Show and explain how to use the volatile keyword to prevent reordering of the write statements. Listing 1: ”Thread 1” 1 x= 12; 2 flag = 1; Listing 2: ”Thread 2” 1 while (flag == 0); 2 printf (”X is %d\n”, x); Solution: Both x and flag need to be declared volatile for this code to work on most systems (see Listing 3). The compiler must be told not to keep these variables in a register that is not seen by the other process. Declaring both x and flag volatile also guarantees that they will be written in order. Listing 3: ”Thread 1” 1 volatile int x, flag ; 2 3 x= 12; 4 flag = 1; 1 2.2 Consistency 20 points Assume you are writing a multi-threaded program for a machine whose compilers and CPUs have the following characteristics: writes can be reordered arbitrarily; i.e., writes from one thread can be seen in different orders on other threads. For example, the following execution is valid under this memory model: P1: W(x)1 W(x)2 W(x)3 P2: R(x)2 R(x)3 R(x)1 P3: R(x)1 R(x)3 R(x)2 The machine has a memory barrier instruction that guarantees that all writes from the thread issuing the memory barrier instruction have completed (in some order) before any writes in that thread that follows the memory barrier instruction. The following sequence shows the effect of this write barrier instruction: P1: W(x)1 W(x)2 mb W(x)3 W(x)4 P2: R(x)2 R(x)1 R(x)3 P3: R(x)1 R(x)2 R(x)4 In the above example, threads 2 and 3 can read the values 1 or 2 (in any order) or, later, the values 3 or 4 (again in any order). However, it is not possible to see values from after the memory barrier before values that were written before the memory barrier. The following example is not possible: P1: W(x)1 W(x)2 mb W(x)3 P2: R(x)1 R(x)3 R(x)2 The code in Listings 4 and 5 shows code fragments that are meant to guarantee mutual exclusion to the section labeled CS (critical section). The algorithm does not work under the memory model described above. Explain why it does not work under that memory model. Then, rewrite it, possibly with the help of the memory barrier instruction, so it is correct under the above memory model. Listing 4: ”Thread P1” 1 f1= 1; 2 turn= 2; 3 gotit = FALSE; 4 while (f2 && (turn != 1)) { 5 /∗ wait ∗/ 6 } 7 gotit = TRUE; 8 CS(); 9 gotit = FALSE 10 f1= 0; Listing 5: ”Thread P2” 1 f2= 1; 2 turn= 1; 3 gotit = FALSE; 4 while (f1 && (turn != 2)) { 5 /∗ wait ∗/ 6 } 7 gotit = TRUE; 8 CS(); 9 gotit = FALSE 10 f2= 0; Solution: 2 • The micro-kernel versus monolithic kernel debate can be seen as described by the end-to-end argument. Micro-kernels place less functionality between applications and the services they need. In return, at least one of the end points (the server) has to provide functionality that is provided inside the OS in a monolithic kernel. When I wrote the question, I should have asked the students to specify the end points in their examples, and tell me what should, or should not, happen between these points. 3.2 Programming Languages for Systems Design 10 points In order to write an operating system, firmware, or a device driver, it is necessary for a pro- gramming language to provide constructs that allow the programmer to place variables and data structures at fixed addresses. Explain why that is so. The Oberon language provides a facility for that through the system dependent module SYSTEM. Basically, it allows a variable or data structure to be placed at a specific address. For data structures that feature alone is not quite enough. Explain what else is needed. Solution: Many I/O devices are memory-mapped. That is, their control registers and sometimes memory local to the interface, appear as ordinary memory to the host system CPU. Writing and reading to these locations has side effects, such as sending data across a network, or providing status about a previous operation. These memory mapped registers appear at a fixed location in the memory space of the OS or an application. In order to access these locations, a programmer needs to be able to obtain those addresses and read and write to them. If the address of a given device is static (mapped by hardware to a specific address), then that address can be hard-coded into a program that accesses that device. Often, the OS assigns an address to a device and provides a function to retrieve that address. In that case, a program must be able to set a structure pointer to a specific address so he or she can access data at that location. A systems programming language must provide a facility to create pointers that point at arbitrary memory locations, or provide a way to map data structures to a specific memory address. In addition, the language must ensure that the order of structure elements is preserved and that no unwanted padding is inserted. This is not truly a requirement, since individual pointers to the structure elements could be used. However, that would lead to inefficiencies. 5 4 Questions About Distributed Systems 4.1 Lamport’s Clocks 10 points Leslie Lamport describes an distributed algorithm that allows us to order all observable events in a system (total order). Explain what is meant by a distributed algorithm. Provide a non- distributed algorithm that also provides a total ordering or events in a system. Just describe your algorithm; you do not need to provide actual code. Why is it better to use a distributed algorithm (if possible)? Solution: A distributed algorithm is an algorithm that gets executed in the individual processes of a distributed system where no single process has complete knowledge of the global state. Yet, taken together, all the processes solve a single problem. A simple way to provide total ordering is to elect a node that will serve as a central arbiter. All events are sent to that node and distributed by it to all others (that are affected by that event). That central node will have total knowledge of the whole system. Of course, it will also be a bottleneck and this algorithm will not scale well at all. 5 Questions About File Systems 5.1 Log-Structured File Systems 10 points What problem is a log-structured file system solving? Solution: Recovery of a crashed file system is faster: Recovery of a file system after a crash is a time-consuming process because the entire data structure that represent the file system on disk must be traversed and checked for inconsistencies. In a log-structured file system, updates are written to the log. At certain points along the log entries, the file system is in a known, consistent, state and marked by a checkpoint. The recovery mechanism only needs to look at the log since the last checkpoint. Performance of file system is better: In a log-structured file system, writes are larger than in a traditional system, and these writes usually go to consecutive blocks on disk. This avoids having many small writes and waiting for confirmation that they have been successfully written to the disk. The latter is necessary in conventional file systems to ensure integrity of the file system data on the disk. 6 5.2 File Systems 10 points What are soft updates? Solution: Soft-updates are an alternative to logging or journaling. The key idea is to sort out pointer dependencies a write block to disk in such an order that the information on disk is always in a consistent state. For example, the ordering makes sure that a new inode is written to the disk before any other block that points to it. 5.3 Consistency in a File System 20 points Assume you were employed in a team that is designing and writing a new disk file system. Your task is to write a subroutine that moves the contents of a file from the blocks it currently occupies to a set of new blocks that are contiguously arranged on the disk. Your subroutine is given how many blocks the file occupies (n), a list of blocks the file currently uses b[0]..b[n − 1], and the block number (x) of where the file is to be moved. After you are done, the file will be located on sectors x through x + n− 1. As part of moving the file, you will also need to update the free block map. I.e., the blocks the file originally occupied need to be marked as free, while the new blocks it occupied now, need to be marked as busy. Functions to set or clear a bit in the free block map are available to you. You can only clear or set one bit at a time. Describe the operations your subroutine needs to carry out. Pay special attention to the problem of the system crashing while your subroutine is executing. You have to make sure that during the whole operation, the file system is always in a consistent state. That is, the file is either in its old location, it has been moved to its new location and the free block map has been updated, or it is possible to generate one of those two states with the data available after a crash. You have to provide a second subroutine that gets called with the same information as your original routine. The task of the second routine is to check and complete, if necessary, the original move. Of course, the system may crash during recovery as well. Simply explain what the two subroutines need to do. You do not have to write pseudo code, but you need to be clear in what exact order your various operations need to execute. Solution: Issues: • Blocks allocated in free block map, but no longer needed • Blocks in use, but marked as free • Block reallocation between freeing and crash recovery The basic steps to be taken are: 1. Copy the file blocks to the new location 2. Update the free block map We assume the new blocks to be used have already been marked as allocated. If not, we can do that before we copy any data. If something goes wrong during the first step, the recovery routine can simply redo that step. The updating of the free block map is trickier 7
Docsity logo



Copyright © 2024 Ladybird Srl - Via Leonardo da Vinci 16, 10126, Torino, Italy - VAT 10816460017 - All rights reserved