Working with processes

Return to homepage

Working with shared memory

The following command line utilities will be usefull for this seminar:
ipcs -m
ipcrm -m id
ipcs -m lists all shared memory segments active on the system and ipcrm -m can be used to remove shared memory segments.
  1. Initialize and get file descriptor shared memory segment identified by current directory using ftok() and shmget(), the segment should have the size of 100 bytes.
  2. Get the file descriptor of the same shared memory segment using ftok() and shmget()
  3. Print and compare both file descriptors.
  4. Attach the memory segment to an address and change the type to char.
  5. Print both addresses, the one from shmat() system call and the one where the type has changed. What did you learn?
  6. Change values in the shared memory so that it contains a short string terminated with a terminator and print it.
  7. Detach the shared memory segment.
  8. Using shmctl(), get information about shared memory segment: key, user id of creator, and owner. Then print the information.
  9. Using shmctl(), remove the shared memory segment.

Shared memory synchronization issues

Consider the following code:
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/wait.h>

#define BODY_SIZE 1024

struct message
{
    char status;
    char body[BODY_SIZE];
};

void child(int shm_id_ret)
{
    sleep(1);
    void *shared_memory_startP = shmat(shm_id_ret, (void *)NULL, 0);
    struct message *testP = (struct message *)shared_memory_startP;
    do
    {
        while(testP->status != 0)
        {
            ;
        }
        char t;
        printf("Child %d: %s\n", getpid(), testP->body);
        sleep(1);
        testP->status = 1;
    } while(1);
    shmdt(shared_memory_startP);
}
int main(int argc, char **argv)
{
    int shm_id_ret = shmget(ftok(".", 1), sizeof(struct message), IPC_CREAT | 0666);
    switch(fork())
    {
        case -1:
        {
            perror("fork");
            break;
        }
        case 0:
        {
            child(shm_id_ret);
            break;
        }
        default:
        {
            void *shared_memory_start = shmat(shm_id_ret, (void *)NULL, 0);
            struct message *test = (struct message *)shared_memory_start;
            test->status = 1;
            for(int i = 0; i < 5; i++)
            {
                while(test->status != 1)
                {
                    ;
                }
                unsigned int max = rand() % 5 + 1;
                for(unsigned int i = 0; i < max; i++)
                {
                    test->body[i] = 'a' + (rand() % 26);
                }
                test->body[max] = '\0';
                printf("Parent %d: %s\n", getpid(), test->body);
                test->status = 0;
            }
            wait(NULL);
            shmdt(shared_memory_start);
            shmctl(shm_id_ret, IPC_RMID, NULL);
            break;	
        }
    }
}
  1. Try to compile and run the code and try to understand it.
  2. Try to solve the deadlock situation when parent is waiting for childs status change using signals.
  3. Revert the code to the original state and try to add another fork() in the parent process after the first was created. Make sure that the child() function is called in both child processes. Does the program work as expected?
  4. Try to solve synchronization problems that have arisen after adding another child process using pipes.

Homework 6

There are two variants of this homework, you can either do variant A or B, both are worth the same amout of points and are exlusive. That means that if you do both you will not get more points, you can choose which one you do and they will give you same amount of points.

Variant A:

Use the files in this archive to create two programs. First (hwa_unsolved) will use pipes as a form of inter-process communication and second (hwb_unsolved) will use shared memory. Further instructions are in the source files.

Variant B:

To complete this variant use the files in this archive.

First task (program hw_a_new__unresloved.c) is to work with signals and communication pipes. The parent process is consuming data produced by the child process. So, the parent process is referred to as a consumer and the child process is referred to as a producer. In the final solution, the synchronization should be based on the mechanism provided by pipes and signals. When the consumer attempts to read from the pipe it should be blocked until the data are available. The producer is blocked by waiting for SIGUSR1 signal until it arrives. Than the producer should write data into the writing end of the pipe. The very first message is written immediately without synchronization. The consumer start by reading this first message and then continues by repeatedly sending SIGUSR1 signal to the producer until the message count limit is reached.

The second task (program hw_b_new__unresloved.c) is to work with signals and shared memory. The program is similar to the previous one, but shared memory is used. The shared memory does not provide any synchronization mechanism. Thus, the entire synchronization in the second program is handled by sending signals and blocking process until required signal arrives.

The programs also allows modes for exclusive execution of the producer's or the consumer's code. There you can provide any file (it will be overwritten), but the good example of this separated execution of the producer and the consumer is to use named pipes. If you are not familiar with the named pipes we can discuss it briefly during the next lecture. The main goal is to make the programs run correctly without any additional parameters. The modes with the additional parameters are intended mainly for demonstration during the lesson or lab. I will likely use some parts of this program during the following lesson.

The programs might seem quite long, but there are many comments and your tasks are usually to add an appropriate parameter or system call function.

All of the tasks are marked by the "TODO" keyword.

Summary of tasks to do in the first program (hw_a_new.c): Associate function sig_handler__producer_write_message() as a handler to SIGUSR1 signal Write message into a file identified by file descriptor number stored in a variable g_write_fd. Send SIGUSR1 signal to the producer process. Read the message from the input file identified by a file descriptor stored in variable read_fd . Use the writing end of the communication pipe as a parameter of producer_process() function. Use the reading end of the communication pipe as a parameter of consumer_process() function.

Summary of tasks to do in the second program (hw_b_new.c)

Associate function sig_handler__producer_write_message() as a handler to SIGUSR1 signal Remove SIGUSR1 form the set of signals stored in variable sigsuspend_mask Use sprintf function to write formatted string into shared memory. Send SIGUSR1 signal to the consumer process indentified by PID stored in variable g_consumer_pid Print out a message inside the shared memory segment. Send SIGUSR1 signal to the producer process indentified by PID stored in variable g_producer_pid Detach the shared memory segment located at the address referenced Remove the shared memory segment referened by identifier stored in variable g_shmid . Attach the shared memory segment identified by an appropriate variable to the address space of the calling process.

You can compile the first program in the following way:

gcc --std=gnu11 --pedantic hw_a_new.c  -o hw_a_new
(of course, change the file names appropriately)