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 exercise in the following way:
- Convert your program to a Real-Time Process. Simply create a new project of that type and copy the source code to it. Then rename the CreateTasks function to main. Do not forget that the return from main implies termination of the whole process.
- 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 the company registers – see below. You can set command line arguments in the Arguments field in the Run/Debug Real Time Process dialog box.
- 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.
- 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.
- Every upper digger will update his company's counter of dug out shovels.
- When the company finishes (the “E” key), the application unregisters itself from the shared memory and terminates.
- 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 company process and the monitor will be started in a specific order.
1.1 Upload system guidelines
Your solution should follow these guidelines:
Use the following header files. Implement all functions declared in them.
- company.h to replace previously used config.h,
- monitor.h, and
- shm.h that is used by both company and monitor.
Note: In total you should have at least six files (three header files and three source files).
Project should contain 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.
Each company is created in accordance with the previous exercise (if not stated otherwise, e.g. behaviour of “E” key). Note: Different companies can use the same names of workers.
Monitor application should behave as follows:
- Upon startup, monitor reports that it is running with some message (e.g., Monitor started).
- Each update of the monitor is finished by a delimiter composed of multiple dashes (e.g., --------) to make it obvious which lines belong to which update.
- For each company, one line is printed containing company’s name and the number of dug out shovels, 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.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 the “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 *registry; 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 */ registry = (struct company_registry *)mmap(0, sizeof(struct company_registry), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (registry == (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(registry, 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; }