CS372: Solutions for Homework 13

Problem 1:

A system uses multi-partition allocation with swapping. The size of main memory is 128K, and there are 4 programs running with sizes 64K, 64K, 32K, and 16K respectively. Each program runs for 50msec, and then requests an I/O operation that requires 20msec. A program can be swapped at the rate of 8Kbyte/20msec. Compute the CPU utilization under round-robin scheduling. Devise a better scheduling strategy and show it has a better utilization.

Solution:
Under round-robin scheduling the utilization increases with the time quantum, so let us assume that the time quantum is larger than 50msec. After loading (and running) the first two processes, running the third and fourth require swapping out the first process to make room available in memory. After the first round, we need to bring in the 1st process from the swap space. Because process 4 is still performing an I/O, it cannot be swapped out, and therefore it is the second process that has enough space to allow the first process to get in, thus the second process gets swapped out. But then, it will get swapped in again soon, instead of processes 3 and 4 (process 1 cannot be swapped out because its address space has to remain in memory while the I/O operation takes place). So, in steady state a time window will look like:

Process 2 swapped out, process 1 swapped in, process 1 runs, processes 3 & 4 swapped out, process 2 swapped in, process 2 runs, process 1 swapped out, process 3 swapped in, process 3 runs, process 4 swapped in (note: we do not need to swap anybody out to get process 4 in), process 4 runs, and loop. The memory will look like:

Process 1: red, Process 2: blue, Process 3: yellow, Process 4: Green

Now, to compute the utilization under this model: In each iteration, we have each process running for 50msec. The time wasted for swapping
is: 64 * 20/8 (2 out) + 64 * 20/8 (1 in) + (32 + 16) * 20/8 (3 & 4 out) + 64 * 20/8 (2 in) + 64 * 20/8 (1 out) + 32 * 20/8 (3 in) + 16 * 20/8
(4 in) = 880 msec. Utilization is 200 / ( 200 + 880) = 18.5%

A better solution is to run the available processes in memory for a few time slices, then swap them out and swap in other processes, run them for a while, and so on.
For example: Micro-level scheduling: round-robin. Macro-level scheduling: each process is swapped out after 10 time slices. In this case, the scenario would be, in steady state Processes 3 & 4 swapped out, processes 1 & 2 swapped in, processes 1 & 2 run for 10 time slices each, get swapped out, processes 3 & 4 swapped in, processes 3 & 4 run for 10 time slices, and so on...

Time wasted for swapping: 48 * 20/8 + 128 * 20/8 + 128 * 20/8 + 48 * 20/8 = 880, while the utilization would be:
10 * 100 / (10 * 100 + 880) = 53.2%

This scheme could be optimized further by swapping out a process as soon as it finishes its 10th time slice, but the difference in utilization is not substantial.

Problem 2:

In problem one, compute the CPU utilization if we:
  1. replace the CPU by one that runs 10 times faster
  2. double the size of main memory

Solution:

  1. Under round-robin, each process will run 5 msec before asking for I/O. Therefore, the resulting utilization is 20/(20+880) = 2.2%
  2. If we double the size of main memory, we can accommodate all processes, which will result in 100% utilization assuming the time quantum is still at 50msec.

Problem 3

Alice P. Hacker is a Head Guru in MacroHard, a software company known for operating systems with very sorry quality. She suggested a trick to reduce the pressure on the swap space. Instead of swapping out pages that belong to code texts into the swap area, the operating system could just take away the memory frames. If the code page is needed later, it could be paged directly from its binary file. Her argument is that the operating system will save time by not storing the code page into the swap space, and will save space in the swap area. The code text exists on disk anyway, and could be fetched from there. Would you approve of this design? Why or why not?

Solution:
This is a nice trick, but it ignores a small problem. What happens if the binary file changes while the program is running in main memory? If the operating system evicts the code pages (during swapping out) but does not save them on the swap space, and just brings them from the binary file (during swapping in), then we are loading a different program into the process. We can have the situation then that pages belonging to two different programs be in the process's text segment. The results of such a disastrous mix are undefined at best. Operating systems that wish to play this trick lock the binary file so that it cannot be modified while there is a process that runs the code. A user who is trying to recompile a program that is currently running in a process will get a message like "text file busy" or some such.

Problem 4:

You are designing a program to automate the publication of stock data in the Greedy Stock Exchange system. To simplify the design, the structure of the application is such that the stock data file will be shared in read-only mode among the processes that are used by brokers, while the exchange manager will run a different process that can update the stock data. All processes will run on the MegaGiga mainframe system running the Solaris operating system. Using the system calls mmap(), mprotect(), and any other call you deem necessary, explain how the processes can share the stock data file as required. Show how the system calls will be used and explain the arguments that will be used to establish the solution.

Solution:
mmap() is used to remap physical memory and files into a process's virtual address space. It is widely used when linking shared libraries, since they don't have to be loaded into process' memory. mmap can also be used to map a file on the disk to the virtual address space of several processes. When a file is accessed by one of the processes, it will be loaded into the physical memory and it will not be loaded twice when other processes need it, since those processes can access the files through their virtual memory mappings.

mprotect controls access permissions to a section of memory.

Here is the detailed information about these functions copied from the man pages.

SYNOPSIS

#include <sys/mman.h>
void * mmap(void * start , size_t length, int prot , int flags , int fd , off_t offset );
int munmap(void * start , size_t length);

DESCRIPTION

The mmap function asks to map length bytes starting at offset offset from the file (or other object) specified by fd into memory, preferably at address start . This latter address is a hint only, and is usually specified as 0. The actual place where the object is mapped is returned by mmap.

The prot argument describes the desired memory protection. It has bits:

PROT_EXEC
Pages may be executed.
PROT_READ
Pages may be read.
PROT_WRITE
Pages may be written.
PROT_NONE
Pages may not be accessed.
The flags parameter specifies the type of the mapped object, mapping options and whether modifications made to the mapped copy of the page are private to the process or are to be shared with other references. It has bits
MAP_FIXED
Do not select a different address than the one specified. If the specified address cannot be used, mmap will fail. If MAP_FIXED is specified, start must be a multiple of the pagesize. Use of this option is discouraged.
MAP_SHARED
Share this mapping with all other processes that map this object.
MAP_PRIVATE
Create a private copy-on-write mapping.
You must specify exactly one of MAP_SHARED and MAP_PRIVATE.
The above three flags are described in POSIX.1b (formerly POSIX.4). Linux also knows about MAP_DENYWRITE, MAP_EXECUTABLE and MAP_ANON(YMOUS).

The munmap system call deletes the mappings for the specified address range, and causes further references to addresses within the range to generate invalid memory references.

RETURN VALUE

On success, mmap returns a pointer to the mapped area. On error, MAP_FAILED (-1) is returned, and errno is set appropriately. On success, munmap returns 0, on failure -1, and errno is set (probably to EINVAL).

SYNOPSIS

#include <sys/mman.h>
int mprotect(const void *addr, size_t len, int prot);

DESCRIPTION

mprotect controls how a section of memory may be accessed. If an access is disallowed by the protection given it, the program receives a SIGSEGV.

prot is a bitwise-or of the following values:

PROT_NONE
The memory cannot be accessed at all.
PROT_READ
The memory can be read.
PROT_WRITE
The memory can be written to.
PROT_EXEC
The memory can contain executing code.
The new protection replaces any existing protection. For example, if the memory had previously been marked PROT_READ, and mprotect is then called with PROT_WRITE, it will no longer be readable.

RETURN VALUE

On success, mprotect returns zero. On error, -1 is returned, and errno is set appropriately.

As seen from the man page descriptions, to be able to use mmap and mprotect we need to know a file descriptor and the length of file section we want to map starting from the offset. For our purposes, the offset from the file is going to be 0 and the length of the section we want to access is as large as the file itself. In order to get the file descriptor, we can use open() system call and, for finding out the length of the file we can use fstat(). You can find the detailed descriptions of these system calls in man pages. I'll give a brief summary of these system calls here:

#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>
int open(const char * pathname , int flags );
open attempts to open a file and return a file descriptor (a small, non-negative integer for use in read , write , etc.)
flags is one of O_RDONLY , O_WRONLY or O_RDWR which request opening the file read-only, write-only or read/write, respectively.

#include <sys/stat.h>
#include <unistd.h>

fstat(int filedes , struct stat * buf );
stats the file pointed to by filedes and fills in buf

struct stat
{
    dev_t     st_dev;           /* device */
    ino_t     st_ino;           /* inode */
    mode_t    st_mode;          /* protection */
    nlink_t   st_nlink;         /* number of hard links */
    uid_t     st_uid;           /* user ID of owner */
    gid_t     st_gid;           /* group ID of owner */
    dev_t     st_rdev;          /* device type (if inode device) */
    off_t     st_size;          /* total size, in bytes */
    unsigned  long st_blksize;  /* blocksize for filesystem IO */
    unsigned  long st_blocks;   /* number of blocks allocated */
    time_t    st_atime;         /* time of last access */
    time_t    st_mtime;         /* time of last modification */
    time_t    st_ctime;         /* time of last change */
};
Now we can write pseudo code for the brokers and the manager assuming that mentioned file is Stock.data.

Broker() {
//open the file in read only mode and get the file descriptor
int fd = open( "Stock.data", O_RDONLY );
//get the information about the file
struct stat* this_file = new struct stat;
fstat( fd, this_file);
//now let's map the file into the process' virtual memory space
void* addr = (void*)mmap( Null, this_file->off_t,PROT_READ,MAP_SHARED, fd, 0);
    /* DO SOMETHING*/
munmap(addr, this_file->off_t );
return;
}

Manager() {
//open the file in read only mode and get the file descriptor
int fd = open( "Stock.data", O_RDWR
//get the information about the file
struct stat* this_file = new struct stat;
stat( fd, this_file);
//now let's map the file into the process' virtual memory space
void* addr = mmap( Null, this_file->off_t,PROT_READ | PROT_WRITE,MAP_SHARED, fd, 0);
/* DO SOMETHING*/
    /* WRITE*/
    /* DO SOMETHING*/

munmap(addr, this_file->off_t );
return;
}

Problem 5

In some operating systems, IO from/to disk is done directly to/from a buffer in the user program's memory. The user program does a system call specifying the address and length of the buffer (the length must be a multiple of the disk record size).

The disk controller needs a physical memory address, not a virtual address. Ben Bitdiddle proposes that when the user does a write system call, the operating system should check that the user's virtual address is valid, translate it into a physical address, and pass that address and the length (also checked for validity) to the disk hardware.

This won't quite work. In no more than two sentences, what did Ben forget?

Solution:
Need solution