You are expected to use the man pages! Remember that man pages with the same title may appear in multiple sections. If you want to search all sections use man -a.
A process may control the effects of signals by installing a signal handler function. In this sense, signals are similar to the traps that you used with PeANUt in COMP2300 (however, signals are not to be confused with traps or interrupts, as they are purely implemented by the software and OS).
In the following code we create two signal handlers. Using the function signal(), we assign these to be executed when our executing process receives either SIGUSR1 or SIGUSR2. We then put our program into an infinite number of 1 second sleeps.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void handler1(int sig);
void handler2(int sig);
int main(int argc, char* argv[]) {
struct sigaction new_action1, new_action2, old_action;
/* Print out process ID and signal values */
printf("Hello from Process ID %d \n", getpid());
printf("Value of SIGUSR1 %d \n", SIGUSR1);
printf("Value of SIGUSR2 %d \n", SIGUSR2);
/* Assign the signal handlers*/
new_action1.sa_handler = handler1;
sigaction(SIGUSR1, &new_action1, &old_action);
new_action2.sa_handler = handler2;
sigaction(SIGUSR2, &new_action2, &old_action);
while (1) sleep(1);
return 0;
}
void handler1(int sig) {
printf("Handler 1 caught signal %d\n", sig);
return;
}
void handler2(int sig) {
printf("Handler 2 caught signal %d\n", sig);
return;
}
ACTIONS
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
int main(int argc, char* argv[]) {
double value;
long int iseed;
struct timeval tv;
/* Use time of day to generate seed for random number generator*/
gettimeofday(&tv, NULL);
iseed = tv.tv_usec;
srand48(iseed);
value = drand48();
if (value < 0.8) {
/* 80% of time code terminates normally */
printf("Process %d will terminate \n", getpid());
sleep(1);
} else {
/* 20% of time code runs for ever */
printf("Process %d will run for ever \n", getpid());
while (1)
sleep(1);
}
return 0;
}
ACTIONSTo obtain a Unix semaphore we use the semget() function:
int semget(key_t key, int nsems, int semflg);
This returns an identifier that is used to manipulate the semaphore,
however rather than obtaining just a single semaphore this function
allows the user to allocate an array of nsems semaphores. For
this reason, the return value is referred to as the semaphore set
id, and therefore to identify a specific semaphore we need to specify
both the semaphore set id AND the number of the semaphore within this
semaphore set (You should also be aware that there are system dependent
limits on the number of semaphores in a semaphore array. E.g. on Linux
look in /proc/sys/kernel/sem or
/usr/include/linux/sem.h. On Solaris look in
/etc/system. You should also consider why such semaphores
are allocated in sets, rather than individually).
Of the other parameters of this system call, the first is a key that we will brush over for the moment, while the third is used to determine whether, for example, we want to create a new semaphore set and if so what access permissions to assign it.
Having created a semaphore set it can be manipulated using calls to semctl():
int semctl(int semid, int semnum, int cmd, ...);
We will use this function to initialize the value of each semaphore. (do
man semctl; for more info). While to do the traditional
"P" and "V" semaphore operations we use:
int semop(int semid, struct sembuf *sops, unsigned nsops);
...pretty easy really!!
The following code puts this all together. First it creates a semaphore set with 2 elements. It then initializes the first semaphore within this set a value of 1 while gives the second a value of 0. It then does a double check to verify that these values are set, before forking a second process. Since the semaphore array is created before the call to fork both the parent and the child have access to it. Each process then enters a loop that selectively decrements or increments the first semaphore (ignoring the second semaphore for the time being).
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/ipc.h>
union semun {
int val;
struct semid_ds *buf;
ushort *array;
};
int main(int argc, char* argv[]) {
key_t sem_key; int nsems, semflg; // used to set up semaphore array
int semid; // OS-assigned id of semaphore array
union semun semctl_arg; // used to set elements of array
struct sembuf sem0_P, sem0_V, sem1_P, sem1_V;// specify P & V ops for each
int irpt; pid_t fork_pid;
/* SEMAPHORE CREATION */
/* create a new semaphore with PRIVATE key */
sem_key = IPC_PRIVATE;
/* number of semaphores in the array */
nsems = 2;
/* set flag to creation with user read/write access permissions */
semflg = IPC_CREAT | 0600;
/* Now create the semaphore array */
semid = semget(sem_key, nsems, semflg);
if (semid < 0) {
perror("Semaphore creation error");
exit(-1);
}
printf("Semaphore created - ID = %d\n", semid);
/* SEMAPHORE INITIALISATION */
/* initialize element 0 of the semaphore array to 1 */
semctl_arg.val = 1;
if (semctl(semid, 0, SETVAL, semctl_arg) < 0) {
perror("Semaphore 0 initialisation error ");
exit(-1);
}
/* initialize element 1 of the semaphore array to 0 */
semctl_arg.val = 0;
if (semctl(semid, 1, SETVAL, semctl_arg) < 0) {
perror("Semaphore 1 initialisation error ");
exit(-1);
}
/*CHECK VALUE OF SEMAPHORES */
printf("Value of semaphore array 0 %d\n", semctl(semid, 0, GETVAL));
printf("Value of semaphore array 1 %d\n", semctl(semid, 1, GETVAL));
/* setup semaphore op buffers for P (decrement) and V (increment) */
sem0_P.sem_num = 0; sem0_P.sem_op = -1; sem0_P.sem_flg = 0;
sem0_V.sem_num = 0; sem0_V.sem_op = +1; sem0_V.sem_flg = 0;
/*FORK A SECOND PROCESS - THEY WILL SHARE THE SEMAPHORE ARRAY */
fork_pid = fork();
/* loop 10 times and manipulate the semaphore */
for (irpt=0; irpt<10; irpt++) {
/* SEMAPHORE GIVES MUTUAL EXCLUSION OF PARENT/CHILD IN FOLLOWING REGION*/
/* Semaphore op - waits until semaphore > 0 then proceed */
if (semop(semid, &sem0_P, 1)) {
perror("semaphore decrement error");
exit(-1);
}
printf("%s %d\n", fork_pid? "Parent": "Child", irpt);
fflush(stdout);
/* Semaphore op - increment semaphore - no waiting */
if (semop(semid, &sem0_V, 1)) {
perror("semaphore increment error");
exit(-1);
}
/* END MUTUAL EXCLUSION */
} // for(...)
/* Have parent wait for child to terminate for tidy output! */
if (fork_pid)
wait(NULL);
return 0;
}
ACTIONS------ Shared Memory Segments -------- key shmid owner perms bytes nattch status ------ Semaphore Arrays -------- key semid owner perms nsems 0x002fa327 0 root 666 2 0x00000000 294914 u8914893 600 2 ------ Message Queues -------- key msqid owner perms used-bytes messages
#!/bin/sh -f
us="$USER"
for sh in ` ipcs -s | grep $us | awk '{print $2}' ` ; do
ipcrm -s $sh
done
Save this code to a file (say remsem.sh),
make sure it has execute permissions (chmod +x remsem.sh, and then
execute this file. Check ipcs -a to verify the removal of the
semaphores from IPC shared memory.
Semaphore created - ID = 393219 Value of semaphore array 0 1 Value of semaphore array 1 99 Child 0 Child 1 Child 2 Child 3 Child 4 Child 5 Child 6 Child 7 Child 8 Child 9 Parent 0 Parent 1 Parent 2 Parent 3 Parent 4 Parent 5 Parent 6 Parent 7 Parent 8 Parent 9This is fine, but the intention was actually to use the semaphore to cause the Parent/Child to execute the region of mutual exclusion in an alternating fashion. So that we get output that looks like:
Semaphore created - ID = 98307 Value of semaphore array 0 1 Value of semaphore array 1 99 Parent 0 Child 0 Parent 1 Child 1 Parent 2 Child 2 Parent 3 Child 3 Parent 4 Child 4Your task is to modify the above code to achieve this. Do this by using both of the elements of the semaphore array. To ensure that you do this correctly augment the printout to include the current wall-clock time on the parent or child process (use gettimeofday() to obtain the wall-clock time). Hint: the operative word is alternating; another approach is to recognize that a semaphore can be used to implement a shared variable. Remember to ./remsem.sh when you re done.
If you do want to learn about shared memory, read the man pages for shmget, shmat, shmctl and shmdt, and/or take COMP4300 in the future. If you want to do more Ada take COMP4330, while if you want to learn more computer architecture, build a cluster and run some applications on it do COMP3320 and COMP4340.
PS. it is not just software engineers who write code that runs forever - just mainly software engineers!