/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


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!

For this assignment you will have to implement a simple airport communication system, which consists of one
airport process and multiple concurrent airplane processes communicating with each other using shared memory.
The airport process is the initial process, creating and owning the shared memory objects; 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 synchronization
struct where you must put all your locks.

airport.c:

  • ”initAirportSHM()”
    • Initialize all shared memory objects.
    • Make sure to also adjust the shared memory object size.
  • ”initAirportMappings()”
    • Map all shared objects to virtual memory.
    • The permissions need to match the ones you used for the SHM objects!
    • Make sure not to use this function to do anything else (like lock initializations).
  • ”initLocks()”
    • Initialize all locks or other synchronization primitives you might need here.
  • ”spawnServiceVehicles()”
    • Launch all service vehicle processes here.
    • Ensure you launch a new process.
    • Ensure you execute the right executable (hint: look inside the auto-generated build directory).
    • Ensure you pass the index as the first argument to the service vehicle process.
  • ”spawnPlane()”
    • Find a free slot in the plane_pids array.
    • Launch a plane plane process here.
    • Ensure you launch a new process.
    • Ensure you execute the right executable (hint: look inside the auto-generated build directory).
    • Ensure you pass the index as the first argument to the plane process.
  • ”cleanupAirportResources()”
    • Clean up all opened resources of the airport here.
    • Ensure to destroy all your locks/synchronization primitives.
    • Ensure to also have a look at the existing code and identify what you additionally need to clean
      up.
    • Order matters, so think it through thoroughly!
  • has to be synchronized properly

After initializing, the airport (“tower”) always initially waits for a mesaage to be sent to it. After receiving
a message, depending on the message and the communication protocol, the tower will send a response, and then
either wait itself for an further response or a new message. Make sure that your communication synchronization
can handle this.

DO NOT remove or relocate code for checking your approach, otherwise, we will deduct points.

plane.c:

  • Handles the communication logic from the pilot side
  • ”initPlaneSHM()”: Initialize all shm objects you need here. Only get the ones you
    actually need, set others to -1.
  • ”initPlaneMappings()”: Initialize all mappins to virtual memory you need here. Only
    get the ones you actually need, set others to NULL.
  • ”cleanupPlaneResources()”: clean up all resources of the plane here.
  • ”planeCommsInbound()”: handles the inbound plane communication logic (see
    communication protocol for details)
  • ”planeCommsOutbound()”: handles the outbound plane communication logic (see
    communication protocol for details)
  • also has to be synchronized properly

service.c:

  • Handles the communication logic from the service side
  • ”initServiceSHM()”: Initialize all shm objects you need here. Only get the ones you
    actually need, set others to -1.
  • ”initServiceMappings()”: Initialize all mappins to virtual memory you need here. Only
    get the ones you actually need, set others to NULL.
  • ”cleanupServiceResources()”: clean up all resources of the plane here.
  • ”startservice()”: handles the service vehicle communication logic loop (see
    communication protocol for details)
  • also has to be synchronized properly

Communication protocol:

  • As already mentioned, there are 2 different communication sequences, one for incoming planes and one for
    outbound ones. Within plane you can fully differentiate them, but ensure that tower and service can
    handle both (there is only one communication loop which must hanle both types of planes).
  • For incoming planes:
    • plane -> tower: HELLO
    • tower -> plane: HELLO
    • plane -> tower: REQUEST_LANDING
    • tower -> plane: EXPECT_RUNWAY (optional – if runway is occupied)
    • plane -> tower: ACKNOWLEDGE (optional – if runway is occupied)
    • tower -> plane: CLEAR_TO_LAND
    • plane -> tower: ACKNOWLEDGE
    • plane: doLandingApproach()
    • plane -> tower: LANDING_COMPLETE
    • tower -> plane: ACKNOWLEDGE
    • plane -> tower: REQUEST_GATE
    • tower -> plane: TAXI_TO_GATE
    • plane -> tower: ACKNOWLEDGE
    • plane: doTaxiToGate()
    • plane -> tower: REQUEST_SERVICE
    • tower -> plane: ACKNOWLEDGE
    • tower -> service: SERVICE_BEGIN
    • service -> tower: ACKNOWLEDGE
    • service: doServiceActions()
    • service -> plane: SERVICE_COMPLETE
    • plane -> service: ACKNOWLEDGE
    • service -> tower: SERVICE_COMPLETE
    • tower -> service: ACKNOWLEDGE
    • plane -> tower: DEBOARDING_COMPLETE
    • tower -> plane: ACKNOWLEDGE
    • plane -> tower: BYE
    • tower -> plane: BYE
  • For outbound planes:
    • plane -> tower: HELLO
    • tower -> plane: HELLO
    • plane -> tower: REQUEST_GATE
    • tower -> plane: TAXI_TO_GATE
    • plane -> tower: ACKNOWLEDGE
    • plane: doTaxiToGate()
    • plane -> tower: REQUEST_SERVICE
    • tower -> plane: ACKNOWLEDGE
    • tower -> service: SERVICE_BEGIN
    • service -> tower: ACKNOWLEDGE
    • service: doServiceActions()
    • service -> plane: SERVICE_COMPLETE
    • plane -> service: ACKNOWLEDGE
    • service -> tower: SERVICE_COMPLETE
    • tower -> service: ACKNOWLEDGE
    • plane -> tower: BOARDING_COMPLETE
    • tower -> plane: ACKNOWLEDGE
    • plane -> tower: REQUEST_TAKEOFF
    • tower -> plane: EXPECT_RUNWAY (optional – if runway is occupied)
    • plane -> tower: ACKNOWLEDGE (optional – if runway is occupied)
    • tower -> plane: CLEAR_TO_TAKEOFF
    • plane -> tower: ACKNOWLEDGE
    • plane: doTakeOffApproach()
    • plane -> tower: TAKEOFF_COMPLETE
    • tower -> plane: ACKNOWLEDGE
    • plane -> tower: BYE
    • tower -> plane: BYE
  • Make sure you use the shm_tower_freq_t
    struct for the message transmission and that you properly synchronize it!

DO NOT remove or relocate code for checking your approach. Otherwise, we will deduce points.

common.h:

  • The only allowed changes in this file are adding synchronization primitives to the
    shm_locks_t struct and adding your own util function declarations at the end. All other
    changes made in this file will make your program not testable. Do not change anything but carefully look
    at the extern variables, defines, and structs, you will need to use them.

common.c:

  • The only allowed change in this file is implementing the custom util functions you
    defined at the end.

util.h / util.c:

  • You can extend the existing check functions if you want, however please note that any changes you do to
    these files will be ignored on the test system. If you are unsure, just leave these
    files alone.

IPC in a (way too oversimplified) nutshell

Those simplified explanations should not replace your attendance and attention in the lecture nor serve you the
detailed solution to this assignment. It should help you to understand the central concept briefly to make
research more accessible.

Virtual Memory Virtual memory is part of the concept of modern operating systems. As you may
suspect, there is a difference between physical and virtual memory. The physical memory provided by, e.g. (SO-)DIMMs has to be managed by the operating
system that uses virtual addresses for locating data. So there has to be a sort of translation between the
physical and virtual memory addresses. If you are interested in this topic, you can look up this and related
articles [1].

Process vs. Threads As many of you already know from previous assignments, processes are
treated differently in contrast to threads – well, kind of. As you discovered, a thread can operate on the whole
memory the program has mapped. This means, e.g., the heap-allocated by one thread can be used with any other
thread the program launches. It is possible since they share the same virtual address space, as they are all
used in one program launched before. So we can deduce that each process has its own virtual memory space. Those
virtual memory spaces are strictly separated from each other.
Thus, sharing resources ”between” two processes are realized differently from sharing resources ”in” a
process. So we have to share memory between two processes. This memory is surprisingly called shared memory, and
it is a central part of IPC [2].

Shared resources Shared resources are “saved” in files, as the file system (FS) is accessible
in both processes – well, kind of again. But besides some exceptions, all files saved on the FS can be accessed
depending on the rights a user has. But some of you may wonder, how to access this data? Do we have to use
”read()” and ”write()” for sharing information? Well, this is possible with one big limitation. This
limitation is that we cannot use virtual addresses to access data easily and fast. Well, there is a solution
called ”mmap()”, which takes a file descriptor (fd) that maps the file (full size) to our virtual memory space
of the program. That’s it, well, kind of. How this mapping works in the kernel will be taught in the following
course called operating systems.

Locking When we use IPC, mostly, some locking mechanisms have to be used. In our case, semaphores and mutexes are the way to go [3].


Holding a lock while going to sleep
will lead to point deductions.

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.
  • airport.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 is an area
    marked for this in common.h and
    common.c
  • Do not push binary files or any other junk files.
  • Carefully read the TODOs. Some contain crucial information!

Submission

Modify the files in your git repository. You can find this file in directory A4. Tag the submission with A4 and push it to the server.

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 ask in the corresponding channel.
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@student.tugraz.at
or
Lorenz Pretterhofer, lorenz.pretterhofer@student.tugraz.at