Update for October 2012:

Overview

Synchronization constructs allow for concurrent processes to operate correctly. The kernel provides semaphores as a synchronization primitive. Some programming languages have support for a more usable form of synchronization called monitors. This project will use kernel level semaphores to implement user level monitors.

To give a head-start, I provide an example of a new kernel system call. It provides access to a single (globally defined) semaphore. The system call has two arguments. The first is an operation, the second is a mid, a monitor ID. In this example, the mid isn't used, and the operation codes are for semaphore operations, not monitor operations.

Your code will implement mid's, and monitor operations. First we experiment with semaphores.

Experiment

A semaphore system call was implemented. This code is in subversion. Find it in the class account, under the proj5 subdirectory.

================================================
=== /usr/src/linux-2.6.26.2/kernel/sched.c =====
================================================

/* static DECLARE_MUTEX(my_monitor) ; */
static DEFINE_SEMAPHORE(my_monitor) ; /* see http://lwn.net/Articles/304725/ */

#define MONITOR_DOWN 0
#define MONITOR_UP 1
#define MONITOR_REACQUIRE 2

SYSCALL_DEFINE2(monitor, int, action, int, mid)
{
        int result ;
        printk( KERN_DEBUG "sys_monitor: entered") ;
        switch(action){
        case MONITOR_DOWN:
                printk( KERN_DEBUG "sys_monitor: action 0") ;
                result = down_interruptible(&my_monitor) ;
                break ;
        case MONITOR_UP:
                printk( KERN_DEBUG "sys_monitor: action 1") ;
                up(&my_monitor) ;
                break ;
        case MONITOR_REACQUIRE: 
                printk( KERN_DEBUG "sys_monitor: action 2") ;
                up(&my_monitor) ;
                /*yield() ;*/ /* unnneeded, Linux implements fairness */
                result = down_interruptible(&my_monitor) ;
                break ;
        default:
                printk( KERN_DEBUG "sys_monitor: unknown action") ;
        }
        return 0 ;
}

The experiments use the following user codes:

================================================
============== monitortest-up.c ================
================================================

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

#define __NR_monitor 31

#define MONITOR_DOWN 0
#define MONITOR_UP 1

int main(int argc, char * argv[]) {
   printf("calling up on monitor ...") ;
   syscall(__NR_monitor, MONITOR_UP) ;
   printf("done.\n") ;
   return 0 ;
}


================================================
============ monitortest-down.c ================
================================================

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

#define __NR_monitor 31

#define MONITOR_DOWN 0
#define MONITOR_UP 1

int main(int argc, char * argv[]) {
   printf("calling down on monitor ...") ;
   syscall(__NR_monitor, MONITOR_DOWN) ;
   printf("done.\n") ;
   return 0 ;
}


================================================
============= monitortest-reacq.c ==============
================================================

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

#define __NR_monitor 31

#define MONITOR_DOWN 0
#define MONITOR_UP 1
#define MONITOR_REACQUIRE 2

int main(int argc, char * argv[]) {
   int i ;
   printf("calling up on monitor ...") ;
   syscall(__NR_monitor, MONITOR_DOWN) ;
   printf("in monitor\n") ;
   // in monitor
   for (i=0;i<10;i++) {
      // leave and reenter monitor
      syscall(__NR_monitor, MONITOR_REACQUIRE) ;
      printf("in monitor i=%d\n",i) ;
      sleep(3) ;
   }
   syscall(__NR_monitor, MONITOR_UP) ;
   // leave monitor
   printf("done.\n") ;
   return 0 ;
}

These experiments use monitortest-down and monitortest-up in two separate shells to experiment with the semaphore.

In the program monitortest-reacq, the lock is released and immediately the process attempts to reacquire it. If there are currently waiting processes on the semaphore, the process will not reacquire, instead it will wait, letting the already-blocked processes awaken first.

Project details

The project is to use semaphores to implement a monitor as a kernel API. You will add two system calls, monitor_create which takes a single argument and monitor_op which takes two arguments.

In designing your monitor, consider:

To get this project up and running, here is a sample solution to creating a monitor with semaphores. It uses two semaphores: one to lock the critical sections of the code, and another to implement the wait queue.

// solution to monitor project
// burt rosenberg, oct 2010
// updated: oct 2011

// REMEMBER THIS IS PSEUDO-CODE!

semaphore lock_sema
semaphore wait_sema
integer wait_count

init:
	lock_sema unlocked (1)
	wait_sema locked (0)
	wait_count = 0 

enter: 
	assume: not holding lock_sema
	down on lock_sema

leave: 
	assume: holding lock_sema
	up on lock_sema

wait:
	assume: holding lock_sema
	// increment must be atomic
	wait_count++
	up on lock_sema
	// no critical race because wait_count 
	//    registers interest in waiting
	down on wait_sema
	// contend to reenter
	down on lock_sema
	
notify:
	assume: holding lock_sema
	// if and decrement must be atomic to be SMP safe
	if wait_count>0 
		wait_count--
		up on wait_sema
       	//*let waiting thread run
	//*up on lock_sema 
	//*down on lock_sema
	
notify_all:
	assume: holding lock_sema
	while wait_count>0
		wait_count--
		up on wait_sema
	//*up on lock_sema
	//*down on lock_sema
	
	

The wait_count++ and -- have to be done in a critical section 
to make the increment/decrement atomic.

Also, the check of the wait_count and the decrement have to be in 
the critical section to prevent two thread both finding wait_count positive
(but say equal to one) both doing the decrement (leaving wait_count negative).

I do place the wait_count++ under the lock so that there is not a race
between the decision to wait and the test by notify of wait_count of process
intending to wait

The lines marked //* were commented out to make this a Mesa style monitor

Once you get this up and running, use your monitor. Implement a solution to the Producer-Consumer problem; implement a solution to the Dining-Philosophers problem.

Helpful hints: Given the discussion in class today, let me elucidate a bit.

As Paul Erdos says: If there is a problem you can't solve, then there is an even simpler problem that you also can't solve. Find that problem.

Getting a simplified monitor to work, with a single global monitor is a simpler problem than the full monitor, with several monitors, and initialization routines that automatically dole out monitors. That is the first problem to solve.

A number of people are having trouble with the macros. Mostly this is just struggling with C. I think a review of static, of structures, and so on, by reading H&S would be very helpful. There are a lot of misconceptions about structures, static, arrays, and so forth.

Forget you ever knew them and read H&S.