Working with semaphores
The following command line utilities will be usefull for this seminar:ipcs -s
ipcrm -s id
ipcs -s
lists all semaphore sets active on the system and ipcrm -s
can be used to remove semaphore sets.
- Create a semaphore set with one semaphore and get its descriptor using the semget system call.
- Set the value of semaphore to 0 using the semctl system call.
- Attempt to decrement the value of the semaphore using semop system call. Observe what happens.
- Attempt to increment the value of the semaphore using semop system call and then attempt to decrement the value. Observe what happens.
- Delete the semaphore set using the semctl system call.
- Create functions
int set_semvalue(int sem_id, int value);
,int semaphore_dec(int sem_id, int semnum);
,int semaphore_inc(int sem_id, int semnum);
,void del_semaphore(int sem_id);
that will set value of specified semaphore, decrement value of semaphore by one, increment value of semaphore by one and delete the whole semaphore set respectively.
Solving synchronization issues using semaphores
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;
}
}
}
- Try to compile and run the code and try to understand it.
- Try to solve the deadlock situation when parent is waiting for childs status change using semaphores.
- 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?
- Try to solve synchronization problems that have arisen after adding another child process using semaphores.
Homework 7
Use this file to create a program that consists of 3 processes. The parent writes the message to shared memory. Two children then access the shared memory after the message is ready and read bytes from shared memory then write them to standard output one by one. They both take turns when accessing shared memory.
Example output is:
PID: 8799 1
PID: 8800 1
PID: 8799 2
PID: 8800 2
PID: 8799 3
PID: 8800 3
PID: 8799 4
PID: 8800 4
PID: 8799 5
PID: 8800 5
PID: 8799 6
PID: 8800 6
PID: 8799 7
PID: 8800 7
PID: 8799 8
PID: 8800 8
PID: 8799 9
PID: 8800 9
The message in shared memory was 123456789. In the output, we can see that every second printf was called from the same process and all bytes were written twice - once by each child. Also, all bytes were written in order. Further instructions are in the source files.