Assignment updates

02/11/23 Binaries are created using Build Targets instead of Build Specs.

13/10/22 Added missing #endif to the shm header file.

1   Assignment

This task is an extension of the previous exercise. The hole to be dug needs to be very large and one digging company would spend long time digging it. It was decided that multiple companies will dig the hole simultaneously. Each company will be represented by a separate process and your task is to create monitoring software to monitor the work done by individual companies.

You need to modify your program from the previous exercises in the following way:

  1. Convert your program to Real-Time Process. Simply create a new project of that type and copy the source to it. Then rename CreateTasks function to main. Do not forget that the return from main implies termination of the whole process.
  2. The first "command line" argument (i.e. argv[1]) of the company process will be the name of the company. This name will be used when company registers – see below. You can set command line arguments in the Arguments field in the Run/Debug Real Time Process dialog box.
  3. All "company" processes will share a common piece of memory, where companies will update their statistics. This memory will contain a fixed size array of structures describing each company. The structure will contain at least two fields: name of the company and the amount of shoveled off soil.
  4. After starting, each company will register itself in the shared memory by finding an empty slot in the array and putting its name to that slot.
  5. When the company finishes (the "E" key), the application unregisters itself from the shared memory and terminates. Every upper digger will update his company's counter of dug out shovels.
  6. Create a new monitoring application that will periodically display the list of currently working companies with the number of dug shovels. The update period will be 1 second.

Note: Do not expect that applications and a monitor will be started in a specific order.

1.1   Upload system guidelines

Your solution should follow these guidelines:

  1. Use following header files. Implement all functions declared in them.

    Note: In total you should have at least six files (three header files and three source files).

  2. Project contains exactly two Build Targets:

    • company (composed of company.c, shm.c), which produces binary file company.vxe,
    • and monitor (composed monitor.c, shm.c), which produces binary file monitor.vxe.
  3. Each company is created in accordance with previous exercise (if not stated otherwise, e.g. behaviour of "E" key). Note: Name of a worker can repeat between multiple companies.

  4. Monitor application should behave as follows:

    • Upon startup, monitor reports that it is running (e.g., Monitor started).
    • Each update of monitor is finished by a dashed delimiter (e.g., --------) to make it obvious when an update occurs.
    • Each company is reported with its name and a number of dug out shovels, both on one line. Optionally, monitor can also report other information.
    • Output should look like this (with added comments):
    --- Monitor started ---   // Report that company monitor is active
    -----------------------   // End of 1st update cycle
    -----------------------
    DigCompany:      0        // New company 'DigCompany' arrived, printed with 0 dug out shovels
    -----------------------
    DigCompany:      5
    -----------------------   // End of 4th update cycle
    DigCompany:      12
    WeWantShovels:   0        // New company arrived
    -----------------------   // End of 5th update cycle
    WeWantShovels:   0
    -----------------------   // End of 6th update cycle, 'DigCompany' company was terminated
    

2   Hints

2.1   Shared memory documentation

Shared memory is documented in Guides → Operating System → VxWorks Application Programmer's Guide → POSIX Facilities → POSIX Memory Management. Look at functions shm_open(), mmap(), etc.

The Wind API documentation calls shared memory as "Shared Data Regions" in the documentation of the sdLib library.

The example below uses the POSIX API.

2.2   Named semaphores

Semaphores created in the shared memory and accessed by several processes concurrently must be created as named objects i.e. created by semOpen() (Wind API) or sem_open() (POSIX API).

2.3   Sharing source code between applications

You may want to share the code that initializes the shared memory between the companies and monitor processes. This can be accomplished by having multiple build targets in one project and sharing some source files between them. Click on Build Targets → New Build Target… to add a new target. Then select the source files that should make up the target binary by unticking Use default content or later by using Edit Content… on each target.

2.4   Example code

You can use the example below as a starting point for your program. It defines the data structure for "company registry" in the shared memory and a skeleton of a function initializing the shared memory region. Note that the init_shm() function below needs to be modified in order to work as required.

#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <semLib.h>
#include <fcntl.h>

struct company {
    char name[20];
    int work_done;
};

struct company_registry {
    struct company companies[50];
};

struct company_registry *ptr;
SEM_ID lock;

void init_shm(void)
{
    int fd;

    /* Lock to protect manipulations with shared memory - accessible from multiple processes */
    lock = semOpen("/complock", SEM_TYPE_MUTEX, SEM_FULL, SEM_Q_FIFO, OM_CREATE, NULL);
    if (lock == NULL) {
        perror("semOpen");
        exit (1);
    }
    /* use semTake() and semGive() to protect the relevant code below */

    fd = shm_open("/company", O_RDWR | O_CREAT, S_IRUSR|S_IWUSR);
    /* or consider using O_EXCL flag to find whether the memory
     * needs to be initialized (see memset below) or not */
    fd = shm_open("/company", O_RDWR | O_CREAT | O_EXCL, S_IRUSR|S_IWUSR);

    /* set the size of shared memory block */
    if (ftruncate (fd, sizeof(struct company_registry)) == -1) {
        perror("ftruncate");
        exit (1);
    }

    /* Map shared memory object in the process address space */
    ptr = (struct company_registry *)mmap(0, sizeof(struct company_registry),
                          PROT_READ | PROT_WRITE,
                          MAP_SHARED, fd, 0);

    if (ptr == (struct company_registry *)MAP_FAILED)
        exit (1);

    /* close the file descriptor; the mapping is not impacted by this */
    close (fd);

    /* ... */

    /* the first company should zero the memory this way: */
    memset(ptr, 0, sizeof(struct company_registry));

    /* ... register this company to the memory ... */
}

2.5   Handling arguments in the main function

Within the application, you can access the arguments in the following way:

int main (int argc,         // Number of the arguments
          char** argv)      // Array of the arguments
{
    if (argc > 0) {
        printf("Name of the application is: %s\n", argv[0]);
        if (argc > 1) {
            printf("Name of the company is: %s\n", argv[1]);
        }
    }

    return 0;
}