/Teaching/System Level Programming/Assignments/A4


Pull from upstream before solving this task.


Task: Interprocess Communication (IPC)

This assignment aims to teach you the basics of interprocess communication (IPC), its use and how you can realize it.

Main Idea

Everybody of us has used interprocess communication already. Mostly unintentionally at this point in your studies. This is why we wanted to take this specific topic into this semester’s course.

To understand the concept of IPC, some major concepts must be learned and understood beforehand.

  • Virtual Memory
  • Process vs. Thread
  • Shared Resources
  • Locking

Some of those terms are already familiar to you, others not. We will not entirely go into details for this assignment, but it’s always useful looking up information based on those keywords.

Implementation details: Airport Communication

For this assignment you will have to implement a simple airport communication system, which consists of one tower process and multiple concurrent service/plane processes communicating with each other using shared memory.

The tower process is the initial process, creating and owning the shared memory objects of the airport; it also spawns all service and plane processes. For initializing the shared memory, CAREFULLY take a look at the defines in common.h. It not only contains the expected shared memory object names, but also flags for permissions, all the pre-defined structs, and the shm_locks_t struct where you must put all your shared locks.

This assignment focuses on your understanding of shared memory, shared synchronization, processes, implementing the radio transceiver, IPC, and cleaning up everything properly again. Other aspects of this assignment, this especially includes the airport logic, are already fully implemented for you, except for potential missing synchronization for shared memory accesses.

You MUST NOT change predefined function signatures, sequences of checks or similar. Exploits will automatically result in deductions! Do not remove or add any usleep or assert statements!

Airport Structure

The airport within this assignment is a very watered-down version of a real-life airport. We have modelled three key participants of an airport:

  • Tower: It is the controller of the live action at an airport and always has the final say regarding all movements around the airport. Within this assignment, there ever only exists one tower.
  • Plane: Transports passengers from/to the airport. Each plane is a separate process, and is either inbound (wants to land and de-board), or outbound (wants to board and take off).
  • Service: Small utility vehicles providing various services (fuel, baggage, food, etc.) to planes. Each plane is serviced once.

In order for all participants to be able to communicate with each other, there is a tower frequency installed within shared memory (shm_tower_freq_t struct). It is the key struct over which messages are sent. The shm_locks_t holds the shared synchronization primitives added by you.

To use the tower frequency, each participant needs to be able to both transmit and receive messages, which is accomplished by the radio transceiver in transceiver.h. Each participant process has a local instance of this transceiver, as well as a local message send queue. Your task will be to implement this in transceiver.c, which is key for the IPC aspect of this assignment.

There are no bad actors on the frequency nor valid participants sending junk messages. Not locally, not on the test system. You thus don’t need to worry about cases where a message is sent but the receiver participant does not exist, this will not happen, assuming your implementation of shared memory and the transceiver is correct.

Your Tasks (in no particular order)

  • Set up shared synchronization primitives
    • Add all necessary mutexes, semaphores and/or condition variables to the struct declaration of shm_locks_t in common.h
    • Initialize them in the function initLocks() in tower.c
  • Set up shared memory in tower.c, plane.c, and service.c
    • Create shared memory objects in the functions initAirportSHM(), initPlaneSHM() and initServiceSHM()
    • Map the shared objects into the process’ memory in the functions initAirportMappings(), initPlaneMappings() and initServiceMappings()
    • Initialize the mapping in initPlaneState()
  • Spawn the service vehicle and plane processes in tower.c
    • Start the correct executable with the needed arguments for each service vehicle in spawnServiceVehicles()
    • Start the correct executable with the needed arguments for a plane process in spawnPlane()
  • Implement clean up in tower.c, plane.c, and service.c
    • Wait for all spawned processes to terminate in the function waitForSpawnedProcesses()
    • Clean up shared mutexes, condition variables and/or semaphores in the function cleanupAirportResources()
    • Remove all mappings and shared memory objects in the functions cleanupAirportResources(), cleanupPlaneResources() and cleanupServiceResources(). Set closed file descriptors to -1 and pointers to unmapped memory regions to NULL.
  • Ensure sufficient synchronization between the participants of the airport in tower.c, plane.c and service.c
    • Go through the main communication loops in the functions starttower(), startplane() and startservice()
    • Add synchronization wherever it is needed
  • Implement all functions of the radio transceiver
    • Look at the docstrings of the functions in transceiver.h and implement them accordingly in transceiver.c
    • Make sure of proper synchronization. Add mutexes, semaphores and/or condition variables to the struct RadioTransceiver in transceiver.h
    • Optionally declare/implement custom transceiver utility functions in transceiver.h/transceiver.c
  • Optional: Create custom utility functions in common.h and common.c

Communication Protocol Reference

This section serves as a reference for the communication protocol. As it is already implemented for you, it simply lists the commands and who sends what to whom, without going into details. You can use it to compare your airport’s behavior to the reference behavior.

Inbound Plane Communication Protocol

This protocol applies to inbound planes only.

plane -> tower: HELLO
tower -> plane: HELLO

plane -> tower: REQUEST_LANDING
  (optional)
tower -> plane: EXPECT_RUNWAY { runway_idx }
plane -> tower: ACKNOWLEDGE
  (/optional)
tower -> plane: CLEAR_TO_LAND { runway_idx }
plane -> tower: ACKNOWLEDGE
  plane: doLanding()
plane -> tower: LANDING_COMPLETE { runway_idx }
tower -> plane: ACKNOWLEDGE

plane -> tower: REQUEST_GATE
  tower: possibly do later
tower -> plane: TAXI_TO_GATE { gate_idx }
plane -> tower: ACKNOWLEDGE
  plane: doTaxiToGate()

plane -> tower: REQUEST_SERVICE { gate_idx }
tower -> plane: ACKNOWLEDGE
  tower: possibly do later
tower -> service: SERVICE_BEGIN { plane_idx }
service -> tower: ACKNOWLEDGE
  service: doServiceActions()
service -> tower: SERVICE_COMPLETE
tower -> service: ACKNOWLEDGE
service -> plane: SERVICE_COMPLETE
plane -> service: ACKNOWLEDGE

plane -> tower: DEBOARDING_COMPLETE
tower -> plane: ACKNOWLEDGE

plane -> tower: BYE
tower -> plane: BYE
Plane: exits process after this message

Outbound Plane Communication Protocol

This protocol applies to outbound planes only.

plane -> tower: HELLO
tower -> plane: HELLO

plane -> tower: REQUEST_GATE
  tower: possibly do later
tower -> plane: TAXI_TO_GATE { gate_idx }
plane -> tower: ACKNOWLEDGE
  plane: doTaxiToGate()

plane -> tower: REQUEST_SERVICE { gate_idx }
tower -> plane: ACKNOWLEDGE
  tower: possibly do later
tower -> service: SERVICE_BEGIN { plane_idx }
service -> tower: ACKNOWLEDGE
  service: doServiceActions()
service -> tower: SERVICE_COMPLETE
tower -> service: ACKNOWLEDGE
service -> plane: SERVICE_COMPLETE
plane -> service: ACKNOWLEDGE

plane -> tower: BOARDING_COMPLETE
tower -> plane: ACKNOWLEDGE

plane -> tower: REQUEST_TAKEOFF
  (optional)
tower -> plane: EXPECT_RUNWAY { runway_idx }
plane -> tower: ACKNOWLEDGE
  (/optional)
tower -> plane: CLEAR_TO_TAKEOFF { runway_idx }
plane -> tower: ACKNOWLEDGE
  plane: doTakeOffApproach()
plane -> tower: TAKEOFF_COMPLETE { runway_idx }
tower -> plane: ACKNOWLEDGE

plane -> tower: BYE
tower -> plane: BYE
Plane: exits process after this message

Service Termination Protocol

This protocol applies to service vehicle termination only.

tower -> service: SERVICE_TERMINATE
service -> tower: ACKNOWLEDGE
Service: exits process after this message

Some further hints

What to do before you start?

  • Pull from upstream!
  • Try to understand the program structure.
  • Carefully read the Manpages and/or the POSIX standard on the needed functions.
  • Only begin, if you understand the basic concept of processes, virtual memory, shared resources as well as mapping them. Bruteforcing will lead to a severe amount of wasted time.
  • tower.h, plane.h, service.h, and the Makefile MUST NOT be changed and will cause a deduction of 100% on the test system.
  • You are not allowed to alter existing code or add new code, except within the TODO STUDENT blocks. If you need to add custom utility functions, there are areas marked for this in common.h and common.c as well as in transceiver.h and transceiver.c.
  • Do not push binary files or any other junk files.
  • Carefully read the TODOs and header docstrings. Some contain crucial information!

Build and run your airport with the provided Makefile on your local machine with make all (compiles all sources) and make run (starts the airport). Solely relying on the test system will not get you far.

Play around with the NUM_{RUNWAYS|GATES|SERVICES|PLANES_TO_SPAWN} constants in common.h and check how your airport behaves under heady load. Note that your airport communication will be stress-tested on the test system, so it is heavily advised to also stress-test your airport on your local machine.

What is this anti-ghost thing?

It is a convenience tool ensuring any processes you spawned (services and planes) are terminated when the tower exists. When you implement waitForSpawnedProcesses() properly, anti-ghost does nothing, else it will send a termination signal to processes not cleaned up by you. This tool is there because you else have to deal with ghost processes when your tower crashes, which then act on the still open shared memory segment and cause funny bugs. Anti-ghost only runs on your local machine, and does not exist on the test system. Relying on the anti-ghost is thus not a valid approach for proper cleanup of all processes.

The tool’s output is only informative. The test system is authoritative in determining if you implemented the wait logic correctly.

Submission

Modify the files in your git repository. You can find this assignment’s files in the A4 directory.

Tag the submission with A4 and push it to the server. You may remove and re-push the A4 tag any time you like before the deadline.

Assignment Tutors

If you have any questions regarding this assignment, go to Discord and read through the SLP channels. The probability that your question was already answered or some discussions will lead you in the right direction is quite high. If not so, just create a new thread with the A4 flair applied.

If you have a more direct question regarding your specific solution, you can also ask the tutors who organize this assignment:

Patrick Goldinger, patrick.goldinger@tugraz.at
Simon Schiller, simon.schiller@student.tugraz.at