22C:116, Lecture 24, Spring 2002

Douglas W. Jones
University of Iowa Department of Computer Science

  1. Multiprocessor Operating Systems

    Porting an operating system to a symmetrical multiprocessor can be easy, if the operating system was constructed using "textbook methods". The textbook methods that matter are:

    Violations of these rules are common in uniprocessor operating systems. If all kernel code is privileged, and if some critical section is very short, the system programmer on a uniprocessor will naturally use disable-interrupts to enter a critical section and enable-interrupts to exit that critical section. Code written this way will port very poorly to a multiprocessor.

    The reason for this is: If there are multiple CPUs, disable interrupts does nothing to halt other CPU's, and if we care about performance, we don't want to halt other CPUs! When one CPU needs to update a shared data structure, it must only block those other CPUs that are trying to update the same data structure!

  2. Basic Rules

    When porting uniprocessor code to a multiprocessor, wherever the uniprocessor code had:

    	disable interrupts
    	.. critical section ..
    	enable interrupts
    
    The corresponding code on the multiprocessor will have this code:
    	disable interrupts     -- prevent other activity on this CPU
    	wait on spin lock      -- block if other CPU in critical section
    	.. critical section ..
    	release spin lock      -- allow other CPUs into critical section
    	enable interrupts      -- allow other activity on this CPU
    
    In the worst case, any critical section guarded by disable-interrupts on the uniprocessor will wait on a global lock variable. The spin-lock can be implemented using any available locking primitive. In the worst case, somethign as crude as Dekker's solution might apply, but if the machine has instructions supporting better solutions, they should be used.

    When porting uniprocessor code to a multiprocessor, the interrupt mechanism of the multiprocessor must be inspected! There are two common possibilities:

    Trap service routines always run on the same processor that executed the instruciton that caused the trap. Therefore, all CPU's execute trap service routines, and it is most productive if they are treated as user code running under the authority of the scheduler as part of a user process (but executing in the kernel).

  3. User Code

    When user processes must deal with shared data, in a uniprocessor, they will usually use semaphores or some equivalent mechanism supported by the kernel. Assuming correct kernel implementation, this will generally work correctly on the multiprocessor, so it requires no changes, however, changes to user code may allow better multiprocessor performance.

    Inside trap-service routines, however, there may be places where the code running on behalf of a user process uses disable-interrupts to guard some critical section. Spin locks must be added to each such critical section!

    In general, both user and kernel code running on behalf of users has two options to guard each critical section: spin-locks and semaphores (or equivalent mechanisms that involve the process manager). The choice between these is should be made as follows: