I'm reviewing my class staff :).
There are 3 types of semaphore use.
1. Signal Semaphore
2. Mutex Semaphore
3. Counting Semaphore
Signal Semaphore is used to order the sequence of processing between processes or threads.Semantics is following:
1.init semaphoe value <- 0
2.P() in shoud-be-later part
3.V() in should-be-first part
next is the sample code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include "semlib.h"
int main(void)
{
int sem_signal; // signal semaphoe
semop_init();
/* get semaphoe */
sem_signal = sem_get();
/* set the value of the semaphoe to 0 */
sem_init_value(sem_signal, 0);
if(fork() == 0){
/* Child */
sem_lock(sem_signal);
/* ------- this part is after the parent -----------*/
printf("I'm a child, shoule be after my parent.\n");
/* -------------------------------------------------*/
exit(0);
}
/* ------- from this part is before the child ----------- */
printf("I'm a parent, should be the first\n");
/* ------- to this part---------------------------------- */
sem_unlock(sem_signal);
/* remove the semaphoe */
sem_remove(sem_signal);
return 0;
}
Mutex Semaphoe if used to make a critical section:
1. init semaphore value <-1
2. P() at the start of C.S in each code segment
3. V() at the end of C.S in each code segment
next is the sample code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include "semlib.h"
#define NUM_CHILDREN 10
#define REPEAT_TIME 3
int main(int argc, char *argv[])
{
int sem_mutex;
int shmid;
int *mysm;
char mysm_l[256];
int ret_val, i, j, shm_val=0;
semop_init();
sem_mutex = sem_get();
sem_init_value(sem_mutex, 1);
fprintf(stderr, "Starting the main routine\n");
if ((shmid = shmget(IPC_PRIVATE,1,0777)) < 0) {
perror("Could not create shared memory");
exit(-1);
}else{
fprintf(stderr, "Shared memory segment created\n");
}
/* attach shmemory and initialize it */
if(( mysm = (int *)shmat(shmid,NULL,0)) < 0) {
perror("Cannot attach shared memory in parent");
exit(-1);
}
*mysm = 0;
/* generate chileren and operate against shared memory */
for(i=0;i<NUM_CHILDREN;i++){
if (fork() == 0){
fprintf(stderr,"Starting Child %d\n", i);
if(( mysm = (int *)shmat(shmid,NULL,0)) < 0) {
perror("Cannot attach shared memory in child");
exit(-1);
}
fprintf(stderr, "Child[%d] attached shared memory\n", i);
/* read shmem,
* sleep 1 sec,
* increment local variable by 1,
* display it,
* write back it to shmem
* sleep 1 sec
*/
for(j=0;j<REPEAT_TIME;j++){
sleep(1);
/* lock the critical section*/
sem_lock(sem_mutex);
(*mysm)++;
sem_unlock(sem_mutex);
sprintf(mysm_l, "child[%d] value[%d]\n", i, *mysm);
fputs(mysm_l, stderr);
sleep(1);
}
fprintf(stderr, "This is Child %d exiting\n", i);
exit(0);
}
}
/* main, wait all children */
fprintf(stderr, "This is the main program again\n");
i=NUM_CHILDREN;
while(i--){
wait(0);
}
/* display the value of share memory */
sprintf(mysm_l, "parent[%d] value[%d]\n", getpid(), *mysm);
fputs(mysm_l, stderr);
/* detatch shared memory */
if((ret_val = shmctl(shmid, IPC_RMID,0)) < 0 ) {
perror("Cannot destroy shared memory segment--do manually!\n");
exit(-1);
}
fprintf(stderr, "This is the main program exiting\n");
/* remove the semaphoe */
sem_remove(sem_mutex);
return 0;
}
Counting Semaphore is used to allocate a certain number of resources:
1. init semaphoe value <- # of resource
2. P() to allocate the available resource
3. V() to deallocate the available resource
so, in order to manage queue, we need 3 semaphore, 1st is mutex, 2nd is to maintain upper bound number of the queue, 3rd is to maintain lower bound number of the queue...
next is the sample code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <pthread.h>
#include "semlib.h"
#define REPEAT_TIME 100
int sem_up_limit; /* counting semaphe */
int sem_low_limit; /* counting semaphe */
int sem_mutex; /* mutex semaphe */
int queue[10]; /* queue */
int start_pos = 0; /* start position of the queue */
int end_pos =0; /* end position of the queue */
void th_enqueue(void *arg)
{
int i;
for(i=0; i<REPEAT_TIME ;i++){
sem_lock(sem_up_limit); /* block if queue if full */
sem_lock(sem_mutex);
queue[start_pos] = i;
start_pos++;
start_pos = start_pos%(sizeof(queue)/sizeof(queue[0]));
sem_unlock(sem_mutex);
sem_unlock(sem_low_limit); /* increment the # of available value */
}
}
void th_dequeue(void *arg)
{
int i;
for(i=0; i<REPEAT_TIME ;i++){
sem_lock(sem_low_limit); /* block if no value */
sem_lock(sem_mutex);
printf("dequeued:%d\n", queue[end_pos]);
queue[end_pos] = 0; /* when dequeue, just clear it */
end_pos++;
end_pos = end_pos% (sizeof(queue)/sizeof(queue[0]));
sem_unlock(sem_mutex);
sem_unlock(sem_up_limit); /* increment the # of available space */
}
}
int main(void)
{
pthread_t tid_enq, tid_deq;
int arg_enq_th, arg_deq_th;
int retvalue;
semop_init();
/* get semaphore */
sem_mutex = sem_get();
sleep(1);
sem_up_limit = sem_get();
sleep(1);
sem_low_limit = sem_get();
/* for mutex, first come, first served */
sem_init_value(sem_mutex, 1);
/* for lower bound check( if queue is empty, block )
* put to the queue : V
* get from the queue: P
*/
sem_init_value(sem_low_limit, 0);
/*
* for upper bound check( if queue is full, block )
* put to the queue : P
* get from the queue: V
*/
sem_init_value(sem_up_limit, (sizeof(queue)/sizeof(queue[0])));
/* create enqueue/dequeue threads */
if ((retvalue=pthread_create(&tid_enq,NULL, (void *)th_enqueue, (void *)&arg_enq_th)) < 0){
perror("Can't create enqueue thread");
exit(-1);
}
if ((retvalue=pthread_create(&tid_deq,NULL, (void *)th_dequeue, (void *)&arg_deq_th)) < 0){
perror("Can't create dequeue thread");
exit(-1);
}
/* wait threads here */
pthread_join(tid_enq, NULL);
pthread_join(tid_deq, NULL);
/* remove the semaphore */
sem_remove(sem_mutex);
sem_remove(sem_low_limit);
sem_remove(sem_up_limit);
return 0;
}
and, next is my semaphore functions used in sample:
struct sembuf sem_p;
struct sembuf sem_v;
union {
int val;
struct semid_ds *buf;
ushort *array;
}sem_arg;
void semop_init()
{
/* lock operation */
sem_p.sem_num = 0;
sem_p.sem_op = -1;
sem_p.sem_flg = 0;
/* unlock operation */
sem_v.sem_num = 0;
sem_v.sem_op = 1;
sem_v.sem_flg = 0;
}
int sem_get(void)
{
int sem_id;
int key = time(0);
/* semaphoe create and initialize */
if ((sem_id = semget(key,1,0777|IPC_CREAT)) < 0) {
perror("Could not create semaphore");
exit(-1);
}
fprintf(stderr, "Semaphores created\n");
return sem_id;
}
void sem_remove(int sem_id)
{
/* remove semaphoe */
if(semctl(sem_id, IPC_RMID,0) < 0 ) {
perror("Cannot destroy semaphoe --do manually!\n");
exit(-1);
}
}
void sem_init_value(int sem_id, int init_value)
{
int semval;
/* set the initial value of the semaphe to 1 */
sem_arg.val = init_value;
if(semctl(sem_id, 0, SETVAL, sem_arg) < 0){
perror("Could not initialize semaphore");
exit(-1);
}
semval = semctl(sem_id,0,GETVAL,sem_arg);
printf("Semaphoe value is :%d\n", semval);
}
int sem_get_value(int sem_id)
{
int semval;
if( (semval = semctl(sem_id,0,GETVAL,sem_arg)) == -1 ){
perror("Could not get semaphore value");
exit(-1);
}
return semval;
}
void sem_unlock(int sem_id)
{
if(semop(sem_id,&sem_v,1) < 0) {
perror("Can't do a V call in main\n");
exit(-1);
}
}
void sem_lock(int sem_id)
{
if(semop(sem_id,&sem_p,1) < 0) {
perror("Can't do a P call in main\n");
exit(-1);
}
}