Lab Five (5) Shared Memory and Semaphores
Shared memory is system supplied resource which enables
two or more processes or threads to access the same
memory space. It is a valuable method of enabling
Inter Process Communication.
NAME
shmget - allocates a shared memory segment
SYNOPSIS
#include
#include
int shmget(key_t key, int size, int shmflg);
DESCRIPTION
shmget() returns the identifier of the shared memory seg-
ment associated to the value of the argument key. A new
shared memory segment, with size equal to the round up of
size to a multiple of PAGE_SIZE, is created if key has
value IPC_PRIVATE.
NAME
shmop - shared memory operations
SYNOPSIS
#include
#include
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
DESCRIPTION
The function shmat attaches the shared memory segment identified by
shmid to the address space of the calling process. The attaching
address is specified by shmaddr with one of the following criteria:
If shmaddr is NULL, the system chooses a suitable (unused) address at
which to attach the segment.
A pointer to the memory is returned. A pointer of -1 indicated failure.
NAME
shmctl - shared memory control
SYNOPSIS
#include
#include
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
DESCRIPTION
shmctl() allows the user to receive information on a shared memory seg-
ment, set the owner, group, and permissions of a shared memory segment,
or destroy a segment. The information about the segment identified by
shmid is returned in a shmid_ds structure:
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
time_t shm_atime; /* last attach time */
time_t shm_dtime; /* last detach time */
time_t shm_ctime; /* last change time */
unsigned short shm_cpid; /* pid of creator */
unsigned short shm_lpid; /* pid of last operator */
short shm_nattch; /* no. of current attaches */
}
IPC_RMID is used to mark the segment as destroyed. It will actually
be destroyed after the last detach. (I.e., when the
shm_nattch member of the associated structure shmid_ds is
zero.) The user must be the owner, creator, or the super-
user.
A program to illustrate the creation of a shared memory block of
128000 bytes.
//By Nachum Danzig
#include
#include
#include
#define SHM_SIZE 128000
#define SHM_MODE ( SHM_R | SHM_W )
int
main(void)
{
int shm_id;
char * shm_ptr;
// allocates a shared memory segment & check
if(0 > (shm_id=shmget(IPC_PRIVATE , SHM_SIZE , SHM_MODE)))
{
perror("Can't create a shared memory.");
exit(1);
}
//attaches the shared memory segment & check
if((void *)-1 == (shm_ptr = shmat(shm_id,0,0)) )
{
perror("can't do the mapping.");
exit(1);
}
printf("shared memory attached from %x to %x\n", shm_ptr, shm_ptr+SHM_SIZE);
if (0 > shmctl(shm_id, IPC_RMID, 0) )
perror("Could not remove the shared memory segment from the system.");
exit(0); //finished the main
}
A program to illustrate the creation and accessing of a shared memory block.
Note that changes made by the child process will be seen by the father when he
reads the shared memory.
//by Nachum Danzig
#include
#include
#include
#include
#include
void cleanup();
char str[]="Goodbye";
char *sharestr;
int shmid;
main()
{
int s,i;
shmid=shmget ( IPC_PRIVATE , strlen ( str ) , 0600 );//make shrmem
sharestr=(char *)shmat ( shmid , 0 , 0600 );//attach shrmem to this process
for ( i=1 ; i < 19 ; i++ ) //trap all signals from 1->19
switch (i){
case 1: case 2: case 3: case 4: case 6: case 8:
case 9: case 11: case 13: case 14: case 15: case 18://17 is son died
if((void *)-1==(signal ( i , &cleanup ))); //to make sure shrmem will be deleted
perror("can't catch %d", i);
}
strcpy ( sharestr , str ); //copy our string into the shrmem location
if ( fork()==0 )//child
{ strcpy ( sharestr , "hello" ); // child copies "hello" into share memory
printf ( "debug: %s\n" , sharestr );
shmdt ( sharestr ); //detach shared memory
shmctl (shmid , IPC_RMID , 0); //remove shared memory segment
}
else
{
printf ( "debug: father\n");
wait ( &s ); //parent waits for child to finish
printf ( "debug: father later\n");
printf ( "%s\n" , sharestr ); //print the shared memory
shmdt ( sharestr ); //detach shared memory
shmctl (shmid , IPC_RMID , 0); //remove shared memory segment
}
// printf ( "%s\n" , sharestr ); //print the shared memory (father and son)
}
void cleanup()
{
perror ( "killed: cleanup\n");
shmdt ( sharestr ); //detach
shmctl ( shmid , IPC_RMID , 0 ); //remove shared memory segment
exit ( 1 ); //die
}
Semaphores are a method for locking resources by one process.
This allows for peer level control of system resource passing.
While semaphores are essentially just a global value or flag which is
checked to see if the resource is free (mutexes), in UNIX system 5
they are far more complex and allow a wider range of uses. For example,
sets of semaphores can be created.
NAME
semget - get a semaphore set identifier
SYNOPSIS
#include
#include
#include
int semget(key_t key, int nsems, int semflg);
DESCRIPTION
This function returns the semaphore set identifier associated with the
argument key. A new set of nsems semaphores is created if key has the
value IPC_PRIVATE.
The nsems field represents the number of semaphores desired in the set.
NAME
semop - semaphore operations
SYNOPSIS
#include
#include
#include
int semop(int semid, struct sembuf *sops, unsigned nsops);
DESCRIPTION
A semaphore is represented by an anonymous structure including the
following members:
unsigned short semval; /* semaphore value */
unsigned short semzcnt; /* # waiting for zero */
unsigned short semncnt; /* # waiting for increase */
pid_t sempid; /* process that did last op */
The function semop performs operations on selected members of the
semaphore set indicated by semid. Each of the nsops elements in the
array pointed to by sops specifies an operation to be performed on a
semaphore by a struct sembuf including the following members:
sembuf -> unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
Flags recognized in sem_flg are IPC_NOWAIT and SEM_UNDO. If an opera-
tion asserts SEM_UNDO, it will be undone when the process exits.
The set of operations contained in sops is performed atomically, that
is, the operations are performed at the same time, and only if they can
all be simultaneously performed. The behaviour of the system call if
not all operations can be performed immediately depends on the presence
of the IPC_NOWAIT flag in the individual sem_flg fields, as noted
below.
Each operation is performed on the sem_num-th semaphore of the
semaphore set, where the first semaphore of the set is semaphore 0.
There are three types of operation, distinguished by the value of
sem_op.
--> If sem_op is a positive integer, the operation adds this value to the
semaphore value (semval). The calling process must have alter permission
on the semaphore set.
--> If sem_op is zero, the process must have read access permission on the
semaphore set. This is a "wait-for-zero" operation: if semval is zero,
the operation can immediately proceed.
--> If sem_op is less than zero, the process must have alter permission on
the semaphore set. If semval is greater than or equal to the absolute
value of sem_op, the operation can proceed immediately: the absolute
value of sem_op is subtracted from semval, and, if SEM_UNDO is asserted
for this operation, the system updates the process undo count (semadj)
for this semaphore. If the absolute value of sem_op is greater than
semval, and IPC_NOWAIT is asserted in sem_flg, the system call fails,
with errno set to EAGAIN (and none of the operations in sops is per-
formed). Otherwise semncnt (the counter of processes waiting for this
semaphore's value to increase) is incremented by one and the process
sleeps until one of the following occurs:
1. semval becomes greater than or equal to the absolute value of
sem_op, at which time the value of semncnt is decremented, the
absolute value of sem_op is subtracted from semval and, if
SEM_UNDO is asserted for this operation, the system updates the
process undo count (semadj) for this semaphore.
2. The semaphore set is removed from the system: the system call
fails with errno set to EIDRM.
3. The calling process catches a signal: the value of semncnt is
decremented and the system call fails with errno set to EINTR.
On successful completion, the sempid value for each semaphore specified
in the array pointed to by sops is set to the process ID of the calling
process. In addition, the sem_otime is set to the current time.
RETURN VALUE
If successful the system call returns 0, otherwise it returns -1 with
errno indicating the error.
NAME
semctl - semaphore control operations
SYNOPSIS
#include
#include
#include
int semctl(int semid, int semnum, int cmd, ...);
DESCRIPTION
The function semctl performs the control operation specified by cmd on
the semnum-th semaphore of the semaphore set identified by semid.
(Semaphores are numbered starting at 0.)
This function has three or four arguments. When there are four, the
call is semctl(semid,semnum,cmd,arg); where the fourth argument arg has
a type union semun defined as follows:
union semun {
int val; /* value for SETVAL */
struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* array for GETALL, SETALL */
/* Linux specific part: */
struct seminfo *__buf; /* buffer for IPC_INFO */
};
#endif
IPC_STAT Copy info from the semaphore set data structure into the
structure pointed to by arg.buf. The argument semnum is ignored.
The calling process must have read access privileges on the
semaphore set.
IPC_SET Write the values of some members of the semid_ds structure
pointed to by arg.buf to the semaphore set data structure,
updating also its sem_ctime member. Considered members
from the user supplied struct semid_ds pointed to by
arg.buf are
sem_perm.uid
sem_perm.gidbits */
The effective userbID of the calling process must be that
of the superbuser, or match the creator or owner of the
semaphore set. The argument semnum is ignored.
IPC_RMID Immediately remove the semaphore set and its data structure
awakening all waiting processes (with an error return
and errno set to EIDRM). The effective userbID of the
calling process must be that of the superbuser, or match
the creator or owner of the semaphore set. The argument
semnum is ignored.
SETALL Set semval for all semaphores of the set using arg.array,
updating also the sem_ctime member of the semid_ds struc-
ture associated to the set. Undo entries are cleared for
altered semaphores in all processes. Processes sleeping on
the wait queue are awakened if some semval becomes 0 or
increases. The argument semnum is ignored. The calling
process must have alter access privileges on the semaphore
set.
SETVAL Set the value of semval to arg.val for the semnum-th
semaphore of the set, updating also the sem_ctime member of
the semid_ds structure associated to the set. Undo entries
cesses sleeping on the wait queue are awakened if semval
becomes 0 or increases. The calling process must have
alter access privileges on the semaphore set.
Example to illustrate the use of a single semaphore to stop one
process until the other process releases the semaphore.
//by Nachum Danzig
#include
#include
#include
#define SEM_MODE 0600 //( SEM_R | SEM_A )
union semun {
int val; /* value for SETVAL */
struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* array for GETALL, SETALL */
/* Linux specific part: */
struct seminfo *__buf; /* buffer for IPC_INFO */
};
int
main(void)
{
int sem_id1;
struct sembuf sops[1];
union semun semarg;
//get a semaphore identifier
if((sem_id1=semget ( IPC_PRIVATE , 1 , SEM_MODE ))==-1)
{
perror("can't create sem_id\n");
exit(1);
}
//=========================================================
//semaphore control operations give an initial value to begin with
semarg.val=1;
semctl(sem_id1,0,SETVAL,semarg);
sops[0].sem_num=0;
sops[0].sem_flg=0;
//=============================================================
if (fork()==0){//i am child
sops[0].sem_op=-1; //set semaphore, "occupied"
semop(sem_id1,sops,1);//I lock semaphore 1
//do action like write to shared memory
printf("Now you are locked out.\n");
sleep(5);
printf("Now I will release it.\n");
sops[0].sem_op=1;
semop(sem_id1,sops,1);//release semaphore
printf("Child finished.\n");
}
else{
//parent
sleep(2);
sops[0].sem_op=-1;
printf("Waiting for child process to release semaphore.\n");
semop(sem_id1,sops,1);//try to "occupy" semaphore
//do action
printf("I got the semaphore. Do some action. Now finish.\n");
//Remove immediately the semaphore set
semctl(sem_id1,0,IPC_RMID,semarg);
}
exit(0); //finished the main
}
Example to illustrate the use of a semaphore set to pass
control back and forth between two processes.
//by Nachum Danzig
#include
#include
#include
#include
#include
#define SEM_MODE 0600 //( SEM_R | SEM_A )
union semun {
int val; /* value for SETVAL */
struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* array for GETALL, SETALL */
/* Linux specific part: */
struct seminfo *__buf; /* buffer for IPC_INFO */
}semarg;
int sem_id1;
pid_t parent, child1;
static void sig_int(int signo) ;
int
main(void)
{
int sem_id1;
struct sembuf sops[1];
if (signal(SIGINT, sig_int) == SIG_ERR)
perror("can't catch SIGINT");
parent= getpid();
//get a semaphore identifier for a set of 2
if((sem_id1=semget ( IPC_PRIVATE , 2 , SEM_MODE ))==-1)
{
perror("can't create sem_id\n");
exit(1);
}
//declare an array to control the 2 semaphores
semarg.array=(ushort*)malloc(2*sizeof(ushort));
//semaphore control operations give an initial value to begin with
semarg.array[0]=1;
semarg.array[1]=0;
semctl(sem_id1,0,SETALL,semarg);
sops[0].sem_flg=0;
//=============================================================
if ((child1=fork())==0)//i am child i go first
while(1)
{
printf("Child1: Waiting to get semaphore.\n");
sops[0].sem_op=-1; //set semaphore, "occupied"
sops[0].sem_num=0;//that's me
semop(sem_id1,sops,1);//I lock semaphore 1
//do action like write to shared memory
printf("Child1: Now you are locked out. I will sleep.\n");
sleep(2);
printf("Child1: Now loop and wait for my semaphore to free up.\n");
sops[0].sem_op=1; //increment father by 1
sops[0].sem_num=1;//that's dad
semop(sem_id1,sops,1);//increment him too
}
else
//parent
while(1)
{
printf("Parent: Waiting to get semaphore.\n");
sops[0].sem_op=-1; //set semaphore, "occupied"
sops[0].sem_num=1;//that's me
semop(sem_id1,sops,1);//I lock semaphore 1
//do action like write to shared memory
printf("Parent: Now you are locked out. I will sleep.\n");
sleep(2);
printf("Parent: Now loop and wait for my semaphore to free up.\n");
sops[0].sem_op=1; //increment child1 by 1
sops[0].sem_num=0; //that's child1
semop(sem_id1,sops,1);
}
}
static void sig_int(int signo)
{
//Remove immediately the semaphore set
semctl(sem_id1,0,IPC_RMID,semarg);
kill(child1,9);
kill(parent,9);
exit(0); //finished the main
}
Exercise:
A. Repeat the father and sons assignment from lab 3 on pipes. Except, this time
have the sons send messages to the father. To prevent the father from
getting two different sons' messages at the same time, use a semaphore
to control the sons' writing to the pipe.
Also, use some kind of random waiting method so that the sons will write
at random (and potentially conflicting) times. The strings that the sons write
should be hard-coded in and can be of your choosing. However, they should
indicate which son is writing and which time he is writing (e.g. first time,
second time, ....)
B. Air Traffic Controllers use software to determine if
two planes are on a collision course. If they are, the operator
in the tower will radio the pilots and tell them to change course.
The software that calculates this gets input from a RADAR
machine (mak"am) which tells the location of the planes
at various times. This information is stored in shared memory.
A semaphore is needed in such cases in order to make sure
that the calculation of one plane's trajectory is based on
data generated at the same time as the other plane's data.
Only once the RADAR recording process has finished with
shared memory for both planes can the collision calculating
software access the memory. Furthermore, as long the collision
calculating process is calculating, the RADAR should not be
allowed to change the shared memory.
Write a program to simulate this. Have two processes simulating
planes. They will each run 2 for loops with x_counter and y_counter
for the x,y coordinates. The counters of the 2 planes will run at
different rates to simulate different speeds. Plane_1 will write its
x,y coordinates to shared_memory_1 and plane_2 will write to
shared_memory_2.
RADAR will read data from shared_memory_1 and shared_memory_2 when
it is passed the semaphore and write it to shared_memory_3 then will
release semaphore. Then Collision software will read shared_memory_3
when he gets the semaphore. He will then release the semaphore to
RADAR.
If there is a collision, print "Collision! Radio planes."
This should be run in an infinite loop.
Handle Ctrl-C interrupt.
We can eliminate any reference to time, since our semaphore will
guarantee that the times at which plane 1 and plane 2 are observed are the
same.
So, collisions can be calculated as follows:
(Y2(plane1)-Y2(plane2)) (X2(plane1)-X2(plane2))
--------------------------- =?= ---------------------------
plane1(Y2-Y1)- plane2(Y2-Y1) plane1(X2-X1)-plane2(X2-X1)
Where Y2(plane1) is plane1's Y coordinate at the second observation
and plane1(Y2-Y1) is the difference between plane1's second and first observed
Y coordinate. The other symbols are similarly understood.
Note: You will need to store 2 sets of x,y coordinates for each plane.
C. Write a simulation of a barber shop. Assume there is one barber and two
seats for people to wait their turn. Use a semaphore to simulate controlling the queue.
New customers should come in at random intervals.
If all the waiting chairs are taken they will not be able to take a seat until one frees up.
Use child processes to simulate the clients and the parent process for the barber.
Take this solution and add to it a semephore to simulate the barber chair.
Clients shoule check if the barber's chair itself is available before waiting in the queue.
Use sem_trywait.
Partial solution to Barbershop exercise
Another explanation on Semaphores with an exercise, By Avi Treistman
Nachum Danzig©