CECS Home | ANU Home | Search ANU
The Australian National University
ANU College of Engineering and Computer Science
Research School of Computer Science
Printer Friendly Version of this Document

UniSAFE

Operating Systems Implementation

Laboratory 3

This is the longest of the labs. This laboratory should take you less than 4 hours to complete. Make sure you ask your tutor for help if you get stuck. You are expected to print out and read through these instruction before the lab time. Also your are expected to do about 1 hour of preparation.

Learning Objectives

This lab has two main parts. The first involving memory management and the second involving the very very simple file system. The learning objectives for this lab are to:
  • understand a bit more about memory management in linux,
  • be able to use the mmap routine.
  • to learn a bit about the workings of the vfs, and
  • to understand the basic structure of the vvsfs.

Preparation

Read through the manual pages for mmap, mprotect, signal. Read through the documentation on the virtual file system(
/usr/src/linux/Documentation/filesystems/vfs.txt
) Also draft your code for the programs your are required to write during the lab.

During the Lab

Step 1 - File copying using mmap

See the description of the mmap() system call at the end of these notes and in the mmap man page.

An interesting way to copy a file is to memory map the source and destination files then to use memcpy() to copy the data between them. This allows you to copy a file without a read() or write() system call. Write a mmap_copy program that copies a file. It should take 3 parameters on the command line, the source file name, the destination file name and the size of the source file in bytes. Your program will need to perform the following steps:

  1. Open the source file for reading
  2. Open the destination file for reading and writing
  3. Use the ftruncate() call to set the size of the destination file
  4. Use mmap() on the source file descriptor to map in the file read-only. You will need to use PROT_READ.
  5. Use mmap() on the destination file descriptor to map in the file with write permission. You will need to use PROT_READ|PROT_WRITE and MAP_SHARED.
  6. memcpy() the data from one file to the other
  7. close both files
Test your program by copying files of different sizes and seeing if they are the same after copying using cmp.

mmap()

The mmap() system call provides a way for a user process to ask the kernel to map some pages from a file or device into the memory space of the process. The return value from mmap() is a pointer that points to an area of memory that, when accessed, will cause a page fault and a load of the corresponding page of data from the device or file. By using mmap() a process can treat data in a file or device as memory, which provides a very convenient programming interface for some tasks. The mmap() call takes the following parameters:
 void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
  • The start parameter is usually NULL an indicates the preferred address that the process would like the kernel to return. By specifying NULL the process is is telling the kernel to choose an address.
  • The length parameter specifies how much memory to map. This is often set to a multiple of the page size (4096 on the i386).
  • The prot parameters specifies the required protections on the pages of memory. These can be any combination of PROT_READ, PROT_WRITE and PROT_EXEC. Not all combinations are supported by the hardware (for example, on the i386 a page cannot be marked as write-only).
  • The flags parameter specifies whether the pages should be shared or private. If shared then changes made to the mapped memory will be propogated to the underlying file.
  • The fd parameters specifies a file descriptor open on the file you want to map
  • The offset parameters specifies the location in the file you want to map. You would set this to zero to start the mapping at the start of the file.

memalign

memalign() is a library call that allocates aligned memory. It is very similar to malloc() except that it takes an additional parameter that determines the minimum memory alignment required for the returned memory. In this lab you will use an alignment of 4096 so that the returned memory is guaranteed to be on a page boundary. This needs to be done because the memory management hardware works in units of one page and cannot set permissions on sub-page units.

The prototype for memalign is:

 void *memalign(size_t alignment, size_t size);

Note that, you should include "malloc.h".

Step 2 - Setting memory protections

The mprotect() system call allows you to set the protections on a set of pages in your processes address space. This allows you to set pages as read-only or to allow the pages to be executed. Have a look at the mprotect() man page. In this exercise you will allocate some memory then set the memory non-writeable and install a signal handler to catch a segmentation fault. You will then attempt to write to the memory and your signal handler will be called as the memory management hardware detects a fault. Inside your signal handler you will use mprotect to make the memory writeable again then return. The write to the memory will then continue. Your program should perform the following steps:
  1. Allocate a page of memory using memalign(). See the description of memalign() below.
  2. Set the memory as non-writeable using mprotect()
  3. Install a signal handler for the SIGSEGV signal using the signal() library call.
  4. Attempt to write to the write-protected memory
  5. Your signal handler should print out a message then set the page of memory as writeable using mprotect() and return.
  6. Write out a message saying that your program has completed the write

Step 3 - Direct Graphics

The kernel provides a special memory device /dev/mem that can be memory mapped to give direct access to the physical memory in your machine. In this exercise you will use mmap() to map in the physical memory of the graphics card in your machine and you will then perform some simple graphics operations by writing directly to that memory.

You will need to be very careful with this program. If you write to the wrong memory location then you will crash the machine or corrupt files.

Use "lspci -vv" will also help find this address of the graphics memory. The address is 0xe0000000 and is 256M on the machines currently in the lab (this will save you looking around for it).

Each pixel is an 'unsigned int' and the screen starts at offset 0x1000000 (in bytes from the start of video memory) into this 256M region of memory. Also each line is 2048 ints before it wraps around (although you only see the first part of this).

Your program should perform the following steps:

  1. Open the device /dev/mem with read-write access.
  2. Use mmap with an offset equal to the address of the frame buffer and map in two (or more) megabytes of video memory
  3. Draw a rectangle on the display by writing to the mapped memory

Each 32-bit unsigned int of mapped memory corresponds to one pixel on the display. The first 2048 ints will be the top line of the display(this includes some padding on right side) and the next 2048 shorts will be the next line, etc. To draw a rectangle you will need to work out the locations of the pixels in the rectangle and set them in the mapped memory(this can be done with two for loops). The value stored will determine the color of the pixel.

Why don't all programs use this method to display graphics?

Optional - Down-loading and mounting the vvsfs

Obtain a copy of the vvsfs code and Makefile. To mount and check the file system do the following:
  • Make a copy of vvsfs.c(into say vvsfs.unmodified.c). This will enable you(and your tutor) to see what changes you have made using the 'diff' command.
  • Kill rsyslogd process (sudo service rsyslog stop) and then create a xterm for the printk messages (cat /proc/kmsg).
  • Use make to compile the module.
  • Load the module. (cat /proc/filesystems to check that the file system is registered)
  • Make an empty directory to mount the vvsfs. (the standard place to put this is /mnt)
  • Mount the vvsfs. Either use loopback, /dev/ram1, or a usb memory stick (you will need to format the device first). Some instructions can be found in a comment at the top of vvsfs.c file. Use cat /proc/mounts to check that it was mounted okay. Take care not to format or mount your main harddisk. You should be able to use the dk command to see the name of your hard disk device.
  • 'cd' into the directory and try a few different file operations.
  • Create two files, say "file1" and "file2", write some data into them.
  • What is the difference between "ls" and "ls file1" in terms of the routines that are executed?
  • Examine the code and attempt to work out which routines are executed when you do the following actions: read from a file, writing to a file, listing a directory, creating a new file.
  • What happens when you attempt to remove a file ?
  • 'cd' to the directory you mounted the vvsfs on and determine the number of bytes this direcory takes up. What is odd about this result?
  • Unmount the filesystem. (use the umount command)
  • Remove the vvsfs module.

Optional - Your first bug to debug

As the number of entries in the sramfs directory increases the size of this file (directories are just special files in UNIX) also should increase. However, a mounted vvsfs shows that the number of bytes taken up by the directory is zero. This is a bug. It is basically due to the fact that the vfs caches a copy of the inode information and the sramfs also maintains a copy of the inode for this directory. When a new file is added to the directory the vvsfs inode's size is updated, but the size in the vfs inode is not updated, hence, when you do a "ls -als /mnt" the size of the vvsfs directory is zero.

Fix this bug. (Hint, you need to add one line to the end of the "vvsfs_add_entry" routine.)

Check that this modification worked.

Optional - Adding the ablity to "rm" a file

To be able to delete files you need to add an extra routine called "vvsfs_unlink" You need to add the pointer to this routine in the sturcture "vvsfs_dir_inode_operations". What are the parameters of this routine? (look in /usr/include/linux/fs.h or have a look at how another fs (eg minix, ext2) does this.)

The unlink routine is most like the "ramfs_lookup" use this routine as a starting point for your code.

Note that, you have:

  • the directory in which the file must be deleted from (this is given as an inode), and
  • the file name that must be deleted (this is given in the dentry structure).

Check your solution.

Attempt to change user id and permission with the vvsfs. What happens? Why is this the case?

Is there anypoint in storing this information in the vvsfs?