in some exemples i did see that they use threading for that
but i want to do it using just fork and 1 private semaphore
i want to do something simple like this :
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<semaphore.h>
int main() {
pid_t pid;
sem_t sema;
sem_init(&sema,1,0);
pid=fork();
if(pid==0) {
printf("first"); // want this first
sem_post(&sema);
}else{
sem_wait(&sema);
printf("second -_-");// and that second
}
return 0;
}
///////////////////////////////////
gcc pgm.c -pthread
./a.out
///////////////////////////////////
output :
first
//////////////////////////////////
so whats the problem here ? i want it =>
first second -_-
The semaphore you created is not shared across processes unless you create it in shared memory (even though you passed 1 for pshared). To do what you want to do, you need to create shared memory first. Read about shmget and shmat.
Note that if you used threads instead of fork, your threads would share the same memory and then the code would work. But because you used fork, you created a new process that no longer shares the same memory with the parent process.
If you are using semaphore.h, then you must put your semaphores in the shared memory to use them in different processes. If you don't want to use shared memory you can always use the semaphores in sys/sem.h. I've implemented your example using sys/sem.h below:
#include <stdio.h>
#include <stdlib.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/shm.h>
/* Required for semctl() */
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
int main()
{
pid_t pid;
/* VARIABLES FOR SEMAPHORE */
int sem_id;
union semun sem_arg;
unsigned short sem_value[1] = {0}; /* initial value of semaphore will be 0 */
struct sembuf sem_buf;
/* END OF VARIABLES FOR SEMAPHORE */
/* CREATE SHARED SEMAPHORE */
sem_arg.array = sem_value;
sem_id = semget(IPC_PRIVATE, 1, SHM_R | SHM_W);
semctl(sem_id, 0, SETALL, sem_arg);
/* END OF CREATE */
pid=fork();
if(pid==0) {
printf("first"); // want this first
/* UNLOCK SEMAPHORE */
sem_buf.sem_num = 0;
sem_buf.sem_op = 1;
sem_buf.sem_flg = 0;
semop(sem_id, &sem_buf, 1);
/* END OF UNLOCK */
}else {
/* LOCK SEMAPHORE */
sem_buf.sem_num = 0;
sem_buf.sem_op = -1;
sem_buf.sem_flg = 0;
semop(sem_id, &sem_buf, 1);
/* END OF LOCK */
printf("second -_-"); // and that second
}
/* Remove semaphore */
semctl(sem_id, 0, IPC_RMID, NULL);
return 0;
}
It looks more complicated but you can define functions for LOCK and UNLOCK sections and call them instead of writing the same 4 lines of code each time you want to lock/unlock your processes.
Note: you don't need -lpthread flag to compile this code.
Related
I am relatively new to programming, especially in the C Language.
I got an assignment to write a simple program in C and executing it in Linux.
I've written the code already as below:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<stdio.h>
#include<stdlib.h>
#define BUFSZ 4096
int shmid;
int shmkey = 12222;
int main()
{
/* ----- Creating Shared memory ----- */
shmid=shmget(IPC_PRIVATE,BUFSZ,0666);
if(shmid<0)
{
perror("shmget");
exit(EXIT_FAILURE);
}
printf(" Segment Created: %d \n", shmid);
/* ----- Attaching Memory to Shared memory ----- */
char *shmbuf;
if((shmbuf=shmat(shmid,0,0))<0)
{
perror("shmat");
exit(EXIT_FAILURE);
}
printf("Segmet Attached at %p \n", shmbuf);
//system("ipcs -m");
/* ----- Creating Process Using Fork ----- */
int p,i; //i is counter
for (i=1; i<=20; i++) //loop for counter
{
p=fork(); //defining fork
if (p<0)
{
printf("Fork Failed"); //Fork Fails if P<0
exit(-1);
}
else if (p==0) //Child Process
{
printf("Counter: %d\t Process ID: %d\n" ,i,getpid());
//Prints Counter Value and Process ID.
}
else //Parent Process
{
wait(NULL); //Waits Child Process to Complete
exit(0);
}
}
/* ----- Deattaching Memory -----*/
if((shmdt(shmbuf))<0)
{
perror("shmat");
exit(EXIT_FAILURE);
}
printf("Segmet Deattached at %p \n", shmbuf);
//system("ipcs -m");
/* ----- Exiting Program ----- */
exit(0);
puts("\n End\n");
}
I now have to apply the concept of semaphore to this program, using semget(), semop(), and semctl(). Can I please get help on how to use the three functions in my program?
The advantage of semaphores over other synchronization mechanisms is that they can be used to synchronize two related or unrelated processes trying to access the same resource.
Related Process
The processes are said to be related if the new process is created from within an existing process, which ends up in duplicating the resources of the creating process. Such processes are called related processes. The following example shows how the related processes are synchronized.
file prog.c
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
int main(int argc, char **argv) { int fd, i,count=0,nloop=10,zero=0,*ptr; sem_t mutex;
//open a file and map it into memory
fd = open("log.txt",O_RDWR|O_CREAT,S_IRWXU); write(fd,&zero,sizeof(int)); ptr = mmap(NULL,sizeof(int),PROT_READ |PROT_WRITE,MAP_SHARED,fd,0); close(fd);
/* create, initialize semaphore */ if( sem_init(&mutex,1,1) < 0)
{
perror("semaphore initilization");
exit(0);
} if (fork() == 0) { /* child process*/
for (i = 0; i < nloop; i++) {
sem_wait(&mutex);
printf("child: %d\n", (*ptr)++);
sem_post(&mutex);
}
exit(0); } /* back to parent process */ for (i = 0; i < nloop; i++) {
sem_wait(&mutex);
printf("parent: %d\n", (*ptr)++);
sem_post(&mutex); } exit(0); }
gcc prog.c -lpthread
In this example, the related process access a common piece of memory, which is synchronized.
Unrelated Process
Processes are said to be unrelated if the two processes are unknown to each other and no relationship exists between them. For example, instances of two different programs are unrelated process. If such programs try to access a shared resource, a semaphore could be used to synchronize their access. The following source code demonstrates this:
file 1: server.c
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SHMSZ 27
char SEM_NAME[]= "vik";
int main()
{
char ch;
int shmid;
key_t key;
char *shm,*s;
sem_t *mutex;
//name the shared memory segment
key = 1000;
//create & initialize semaphore
mutex = sem_open(SEM_NAME,O_CREAT,0644,1);
if(mutex == SEM_FAILED)
{
perror("unable to create semaphore");
sem_unlink(SEM_NAME);
exit(-1);
}
//create the shared memory segment with this key
shmid = shmget(key,SHMSZ,IPC_CREAT|0666);
if(shmid<0)
{
perror("failure in shmget");
exit(-1);
}
//attach this segment to virtual memory
shm = shmat(shmid,NULL,0);
//start writing into memory
s = shm;
for(ch='A';ch<='Z';ch++)
{
sem_wait(mutex);
*s++ = ch;
sem_post(mutex);
}
//the below loop could be replaced by binary semaphore
while(*shm != '*')
{
sleep(1);
}
sem_close(mutex);
sem_unlink(SEM_NAME);
shmctl(shmid, IPC_RMID, 0);
_exit(0);
}
gcc server.c -lpthread -o server
File 2: client.c
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SHMSZ 27
char SEM_NAME[]= "vik";
int main()
{
char ch;
int shmid;
key_t key;
char *shm,*s;
sem_t *mutex;
//name the shared memory segment
key = 1000;
//create & initialize existing semaphore
mutex = sem_open(SEM_NAME,0,0644,0);
if(mutex == SEM_FAILED)
{
perror("reader:unable to execute semaphore");
sem_close(mutex);
exit(-1);
}
//create the shared memory segment with this key
shmid = shmget(key,SHMSZ,0666);
if(shmid<0)
{
perror("reader:failure in shmget");
exit(-1);
}
//attach this segment to virtual memory
shm = shmat(shmid,NULL,0);
//start reading
s = shm;
for(s=shm;*s!=NULL;s++)
{
sem_wait(mutex);
putchar(*s);
sem_post(mutex);
}
//once done signal exiting of reader:This can be replaced by another semaphore
*shm = '*';
sem_close(mutex);
shmctl(shmid, IPC_RMID, 0);
exit(0);
}
gcc client.c -lpthread -o client
The above executables (client and server) demonstrate how semaphore could be used between completely different processes.
In addition to the applications shown above, semaphores can be used cooperatively to access a resource. Please note that a semaphore is not a Mutex. A Mutex allows serial access to a resource, whereas semaphores, in addition to allowing serial access, could also be used to access resources in parallel. For example, consider resource R being accessed by n number of users. When using a Mutex, we would need a Mutex "m" to lock and unlock the resource, thus allowing only one user at a time to use the resource R. In contrast, semaphores can allow n number of users to synchronously access the resource R.
Hey so I'm doing a simple producer problem but for some reason my producer runs through and then my consumer runs, even though my producer should be stopped once it can't get the semaphore initialized to 15:
#define _REENTRANT
#define BUFFER_SIZE 15
#define SHMKEY ((key_t) 7890)
//The buffer should be treated as circular buffer
#define BUFFER_WRAP(x) x%BUFFER_SIZE
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <semaphore.h>
#include <stdbool.h>
//headers
void* producerFunc(void *);
void* consumerFunc(void *);
//Structures
typedef struct
{
char* value;
} shared_mem;
typedef struct
{
int value;
} shared_dat;
//You will require 3 semaphores;
sem_t empty;
sem_t full;
sem_t crit_sec;
int start = 0;
int end = 0;
shared_mem *buffer;
//while(fscanf(fp,"%c",&newChar) != EOF)
char newChar;
FILE* fp;
shared_dat *counter;
//To compile program use the command:
//gcc name_of_program.c -lpthread -lrt
int main(void){
fp = fopen("mytest.dat", "r");
/******************init everything: threads, sems, shmem**********/
int r=0;
int i;
int shmid; /* shared memory ID */
pthread_t producer[1]; /* process id for thread 1 */
pthread_t consumer[1]; /* process id for thread 2 */
pthread_attr_t attr; /* attribute pointer array */
int status;
char *shmadd;
shmadd = (char *) 0;
int value, value1;
//create shared memory seg, if return -1 then print error
if ((shmid = shmget (SHMKEY, sizeof(int), IPC_CREAT | 0666)) < 0){
perror ("shmget");
exit (1);
}
//connect process to shared memory segment. If return is -1 then print error
if ((buffer = (shared_mem *) shmat (shmid, shmadd, 0)) == (shared_mem *) -1){
perror ("shmat");
exit (0);
}
//set the
char buffer_array[15];
buffer->value = buffer_array;
counter = (shared_dat *) malloc(sizeof(shared_dat));
/* initialize shared memory to 0 */
counter->value = 0 ;
sem_init(&empty,0,BUFFER_SIZE);
sem_init(&full,0,0);
sem_init(&crit_sec,0,1);
printf("1 - I am here %d in pid %d\n",r,getpid());
fflush(stdout);
/* Required to schedule thread independently.
Otherwise use NULL in place of attr. */
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); /* system-wide contention */
/* end to schedule thread independently */
/* Create the threads */
pthread_create(&producer[0], &attr, producerFunc, NULL);
pthread_create(&consumer[0], &attr, consumerFunc, NULL);
/* Wait for the threads to finish */
pthread_join(producer[0], NULL);
pthread_join(consumer[0], NULL);
/***************once threads finished and * reached in buffer************/
printf("from parent counter = %d\n", counter->value);
printf("---------------------------------------------------------------------------\n");
printf("\t\t End of simulation\n");
sem_destroy(&empty);
sem_destroy(&full);
sem_destroy(&crit_sec);
//release shared mem with IPC_RMID and print "end"
if ((shmctl (shmid, IPC_RMID, (struct shmid_ds *) 0)) == -1){
perror ("shmctl");
exit (-1);
}
fclose(fp);
exit(0);
}
void* producerFunc(void *arg){
bool finished = false;
while(!finished){
sem_wait(&empty);
sem_wait(&crit_sec);
end++;
if(fscanf(fp,"%c",&newChar) != EOF){
buffer->value[BUFFER_WRAP(end)] = newChar;
printf("%c",newChar);
}else{buffer->value[BUFFER_WRAP(end)] = '*';
finished = true;
}
sem_post(&crit_sec);
sem_post(&full);
}
}
void* consumerFunc(void *arg){
bool finished = false;
char val;
while(!finished){
sem_wait(&full);
sem_wait(&crit_sec);
start++;
//sleep for 1 sec
sleep(1);
if((val = buffer->value[BUFFER_WRAP(start)]) != '*'){
printf("Consuming: %c\n",val);
counter->value++;
}else{finished = true;}
sem_post(&crit_sec);
sem_post(&empty);
}
}
For some reason the producer isn't blocked after 15 letters are put into the buffer.
With consumer calling
sem_post(&empty);
you are ensuring more steps than 15 to the producer.
Also note that in your shmget() call you are assuming that
sizeof(int) >= sizeof(struct shared_mem)
which is probably correct until you don't add fields to the struct
So my solution I can't exactly explain but I compiled the code on the machines at school running windows 7. I use a mac. I know the issue was with the options I used to compile.
I needed to use the command on the school computers:
gcc -o ass3 ass3.c -lpthread -lrt
This made the semaphores work. This same command doesn't work on my mac. Apparently that -lrt option doesn't work on mac. Does anyone know why or what the equivalent is so I can get this to work on my home computer?
I implemented the Reader/Writer problem in C using semaphores (http://en.wikipedia.org/wiki/Readers%E2%80%93writers_problem).
If I put the respective processes to sleep() for one second, the program acts as expected, however if I let it run without interrupting I get something like this:
writer wrote 5
writer wrote 10
reader reads the data:5
writer wrote 15
reader reads the data:15
reader reads the data:15
Semaphore with value of sid = 11599873 is killed
Semaphore with value of sid = 11632642 is killed
Semaphore with value of sid = 11665411 is killed
or
writer wrote 5
writer wrote 10
writer wrote 15
reader reads the data:5
reader reads the data:15
reader reads the data:15
Semaphore with value of sid = 11599873 is killed
Semaphore with value of sid = 11632642 is killed
Semaphore with value of sid = 11665411 is killed
Is this just because the standard out buffer prints these lines out of order, or is there a race condition in my implementation?
Here's the code:
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<stdlib.h>
#include "sem.h"
#define N 3
#define BUFSIZE 1
#define PERMS 0666 //0666 - To grant read and write permissions
int *buffer;
int mutex,write_allowed,rd_count; /* semaphore variables
* mutex, write_allowed - binary semaphore -- critical section
* rd_count - counting semaphore */
void reading()
{
//perform read
int g;
g=*buffer;
printf("reader reads the data:%d\n",g);
}
int main()
{
int shmid,no=1,i;
int pid,n;
if((shmid=shmget(1000,BUFSIZE,IPC_CREAT| PERMS )) < 0)
{
printf("\n unable to create shared memory");
return;
}
if((buffer=(int*)shmat(shmid,(char*)0,0)) == (int*)-1)
{
printf("\n Shared memory allocation error\n");
exit(1);
}
// semaphore creation
if((mutex=semget(IPC_PRIVATE,1,PERMS | IPC_CREAT)) == -1)
{
printf("\n can't create mutex semaphore");
exit(1);
}
if((write_allowed=semget(IPC_PRIVATE,1,PERMS | IPC_CREAT)) == -1)
{
printf("\n can't create empty semaphore");
exit(1);
}
if((rd_count=semget(IPC_PRIVATE,1,PERMS | IPC_CREAT)) == -1)
{
printf("\ncan't create full semaphore");
exit(1);
}
// initialze the semaphore
sem_create(mutex,1);
sem_create(write_allowed,1);
sem_create(rd_count,0);
//forking a child
if((pid=fork()) < 0)
{
printf("\n Error in process creation");
exit(1);
}
// write process
if(pid > 0)
{
for(i=0;i<N;i++)
{
P(write_allowed);
//perform write
*buffer=*buffer+5;
printf("write wrote %d\n", *buffer);
//sleep(1);
V(write_allowed);
}
}
//read process
if(pid == 0)
{
for(i=0;i<N;i++)
{
P(mutex);
rd_count++;
if(1 == rd_count)
P(write_allowed);
V(mutex);
reading();
//sleep(1);
P(mutex);
rd_count--;
if(0==rd_count)
V(write_allowed);
V(mutex);
}
shmdt(0);
shmctl(shmid, IPC_RMID, NULL);
semkill(mutex);
semkill(write_allowed);
semkill(rd_count);
}
}
The sem.h file:
/************************************************************************/
/* Operating Systems - Fall 2006
/* */
/* Semaphore library : sem.h */
/* */
/* Originally developed at KSU by a teaching assistant */
/* */
/* Description : The following library is a collection of */
/* routines for using binary semaphores in C: */
/* 1. seminit - to initialize a semaphore. */
/* 2. P - to perform a P(S) (wait) operation. */
/* 3. V - to perform a V(S) (signal) operation. */
/* 4. semkill - to remove a semaphore */
/* */
/* These routines call system routines: */
/* 1. semget - to get a semaphore */
/* 2. semctl - semaphore control operations */
/* 3. semop - semaphore operations */
/* */
/* Complete manual entries can be obtained by: */
/* man semctl | col -b | lpr */
/************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
union arg{ /* This structure is used to call semctl */
int val;
struct semid_ds *buf;
char *array;
};
/*
* Create semaphore based on "key" parameter to "initval"
*/
void sem_create(int semid, int initval)
{
int semval;
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
}s;
s.val=initval;
if((semval=semctl(semid,0,SETVAL,s))<0)
printf("\n Erroe in executing semctl");
}
/*
* Remove semaphore with semaphore id (sid) from the kernel
*/
static void semkill (sid)
int sid;
{
if (semctl(sid,0,IPC_RMID,0) == -1)
perror("semctl (kill)");
printf("Semaphore with value of sid = %d is killed \n",sid);
}
/*
* Perform the designated "op" operation on the semaphore. If "op" is -1,
* then this implements the "P" operation; it decrements the value of the
* semaphore (semval) if it was >0,
* and blocks the caller if it was zero (semval==0)
* If "op" is 1, then this is simply added to current value of
* the semaphore ("V" operation).
*/
static void semcall(sid, op)
int sid;
int op;
{
struct sembuf sb;
sb.sem_num = 0; /* semaphore number within sid */
sb.sem_op = op;
sb.sem_flg = 0; /* blocking call */
if (semop(sid, &sb, 1) == -1)
perror("semop");
}
/*
* P operation on semaphore "sid". Should be called upon entry to critical
* region.
*/
static void P(sid)
int sid;
{
semcall(sid, -1);
}
/*
* V operation on semaphore "sid". Should be called upon exit from critical
* region.
*/
static void V(sid)
int sid;
{
semcall(sid, 1);
}
You don't increment a semaphore by rd_count++;. rd_count is supposedly just a semaphore ID. You need to use some kind of function that operates using the semaphore ID to change the state of the semaphore, which is kept somewhere outside your two processes.
Also I am not familiar with sem_create(), but unless it is a C++ function taking a reference argument, I have my doubts it has any side-effect on the variables you pass it.
I want to fork multiple processes and then use a semaphore on them. Here is what I tried:
sem_init(&sem, 1, 1); /* semaphore*, pshared, value */
.
.
.
if(pid != 0){ /* parent process */
wait(NULL); /* wait all child processes */
printf("\nParent: All children have exited.\n");
.
.
/* cleanup semaphores */
sem_destroy(&sem);
exit(0);
}
else{ /* child process */
sem_wait(&sem); /* P operation */
printf(" Child(%d) is in critical section.\n",i);
sleep(1);
*p += i%3; /* increment *p by 0, 1 or 2 based on i */
printf(" Child(%d) new value of *p=%d.\n",i,*p);
sem_post(&sem); /* V operation */
exit(0);
}
And the output is:
child(0) forked
child(1) forked
Child(0) is in critical section.
Child(1) is in critical section.
child(2) forked
Child(2) is in critical section.
child(3) forked
Child(3) is in critical section.
child(4) forked
Child(4) is in critical section.
Child(0) new value of *p=0.
Child(1) new value of *p=1.
Child(2) new value of *p=3.
Child(3) new value of *p=3.
Child(4) new value of *p=4.
Parent: All children have exited.
This clearly means the semaphore didn't work as it was supposed to. Can you explain how I should use semaphores on forked processes?
The problem you are facing is the misunderstanding of sem_init() function. When you read the manual page
you will see this:
The pshared argument indicates whether this semaphore is to be shared
between the threads of a process, or between processes.
If you are done reading up to this point, you will think that the non-zero value of pshared will make the semaphore inter-process semaphore. However, this is wrong.
You should continue reading and you'll understand that you have to locate the semaphore in a shared memory region. To do that, several functions can be used as
you can see below:
If pshared is nonzero, then the semaphore is shared between processes,
and should be located in a region of shared memory (see shm_open(3),
mmap(2), and shmget(2)). (Since a child created by fork(2) inherits
its parent's memory mappings, it can also access the semaphore.) Any
process that can access the shared memory region can operate on the
semaphore using sem_post(3), sem_wait(3), etc.
I find this approach as a more complicated approach than others, therefore I want to encourage people to use sem_open() instead of sem_init().
Below you can see a complete program illustrates the following:
How to allocate shared memory and use shared variables between forked
processes.
How to initialize a semaphore in a shared memory region and is used
by multiple processes.
How to fork multiple processes and make the parent wait until all of
its children exit.
#include <stdio.h> /* printf() */
#include <stdlib.h> /* exit(), malloc(), free() */
#include <sys/types.h> /* key_t, sem_t, pid_t */
#include <sys/shm.h> /* shmat(), IPC_RMID */
#include <errno.h> /* errno, ECHILD */
#include <semaphore.h> /* sem_open(), sem_destroy(), sem_wait().. */
#include <fcntl.h> /* O_CREAT, O_EXEC */
int main (int argc, char **argv){
int i; /* loop variables */
key_t shmkey; /* shared memory key */
int shmid; /* shared memory id */
sem_t *sem; /* synch semaphore *//*shared */
pid_t pid; /* fork pid */
int *p; /* shared variable *//*shared */
unsigned int n; /* fork count */
unsigned int value; /* semaphore value */
/* initialize a shared variable in shared memory */
shmkey = ftok ("/dev/null", 5); /* valid directory name and a number */
printf ("shmkey for p = %d\n", shmkey);
shmid = shmget (shmkey, sizeof (int), 0644 | IPC_CREAT);
if (shmid < 0){ /* shared memory error check */
perror ("shmget\n");
exit (1);
}
p = (int *) shmat (shmid, NULL, 0); /* attach p to shared memory */
*p = 0;
printf ("p=%d is allocated in shared memory.\n\n", *p);
/********************************************************/
printf ("How many children do you want to fork?\n");
printf ("Fork count: ");
scanf ("%u", &n);
printf ("What do you want the semaphore value to be?\n");
printf ("Semaphore value: ");
scanf ("%u", &value);
/* initialize semaphores for shared processes */
sem = sem_open ("pSem", O_CREAT | O_EXCL, 0644, value);
/* name of semaphore is "pSem", semaphore is reached using this name */
printf ("semaphores initialized.\n\n");
/* fork child processes */
for (i = 0; i < n; i++){
pid = fork ();
if (pid < 0) {
/* check for error */
sem_unlink ("pSem");
sem_close(sem);
/* unlink prevents the semaphore existing forever */
/* if a crash occurs during the execution */
printf ("Fork error.\n");
}
else if (pid == 0)
break; /* child processes */
}
/******************************************************/
/****************** PARENT PROCESS ****************/
/******************************************************/
if (pid != 0){
/* wait for all children to exit */
while (pid = waitpid (-1, NULL, 0)){
if (errno == ECHILD)
break;
}
printf ("\nParent: All children have exited.\n");
/* shared memory detach */
shmdt (p);
shmctl (shmid, IPC_RMID, 0);
/* cleanup semaphores */
sem_unlink ("pSem");
sem_close(sem);
/* unlink prevents the semaphore existing forever */
/* if a crash occurs during the execution */
exit (0);
}
/******************************************************/
/****************** CHILD PROCESS *****************/
/******************************************************/
else{
sem_wait (sem); /* P operation */
printf (" Child(%d) is in critical section.\n", i);
sleep (1);
*p += i % 3; /* increment *p by 0, 1 or 2 based on i */
printf (" Child(%d) new value of *p=%d.\n", i, *p);
sem_post (sem); /* V operation */
exit (0);
}
}
OUTPUT
./a.out
shmkey for p = 84214791
p=0 is allocated in shared memory.
How many children do you want to fork?
Fork count: 6
What do you want the semaphore value to be?
Semaphore value: 2
semaphores initialized.
Child(0) is in critical section.
Child(1) is in critical section.
Child(0) new value of *p=0.
Child(1) new value of *p=1.
Child(2) is in critical section.
Child(3) is in critical section.
Child(2) new value of *p=3.
Child(3) new value of *p=3.
Child(4) is in critical section.
Child(5) is in critical section.
Child(4) new value of *p=4.
Child(5) new value of *p=6.
Parent: All children have exited.
It is not bad to check shmkey since when ftok() fails, it returns -1. However if you have multiple shared variables and
if the ftok() function fails multiple times, the shared variables that have a shmkey with value -1 will reside in the same
region of the shared memory resulting in a change in one affecting the other. Therefore the program execution will get messy. To avoid this, it is better checked if the ftok()
returns -1 or not (better to check in source code rather than printing to screen like I did, although I wanted to show you the key values in case there is a collision).
Pay attention to how the semaphore is declared and initialized. It's different than what you have done in the question (sem_t sem vs sem_t* sem). Moreover, you should use them as they appear in this example. You cannot define sem_t* and use it in sem_init().
Linux minimal anonymous sem_init + mmap MAP_ANONYMOUS example
I like this setup as it does not pollute any global namespace as sem_open does.
The only downside is that MAP_ANONYMOUS is not POSIX, and I don't know any replacement: Anonymous shared memory? shm_open for example takes a global identifier just like sem_open.
main.c:
#define _GNU_SOURCE
#include <assert.h>
#include <semaphore.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char **argv) {
pid_t pid;
typedef struct {
sem_t sem;
int i;
} Semint;
Semint *semint;
size_t size = sizeof(Semint);
semint = (Semint *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0);
assert(semint != MAP_FAILED);
/* 1: shared across processes
* 0: initial value, wait locked until one post happens (making it > 0)
*/
sem_init(&semint->sem, 1, 0);
semint->i = 0;
pid = fork();
assert(pid != -1);
if (pid == 0) {
sleep(1);
semint->i = 1;
msync(&semint->sem, size, MS_SYNC);
sem_post(&semint->sem);
exit(EXIT_SUCCESS);
}
if (argc == 1) {
sem_wait(&semint->sem);
}
/* Was modified on the other process. */
assert(semint->i == 1);
wait(NULL);
sem_destroy(&semint->sem);
assert(munmap(semint, size) != -1);
return EXIT_SUCCESS;
}
Compile:
gcc -g -std=c99 -Wall -Wextra -o main main.c -lpthread
Run with sem_wait:
./main
Run without sem_wait:
./main 1
Without this synchronization, the assert is very likely to fail, since the child sleeps for one whole second:
main: main.c:39: main: Assertion `semint->i == 1' failed.
Tested on Ubuntu 18.04. GitHub upstream.
I am getting error in the following program. I want to demonstrate how two processes can share a variable using semaphore. Can anyone guide me?
I am not able to debug the errors...
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<semaphore.h>
int main()
{
int pid,mutex=1;
int semid; /* semid of semaphore set */
key_t key = 1234; /* key to pass to semget() */
int nsems = 1; /* nsems to pass to semget() */
semid=semget(key,nsems,IPC_CREAT|0666);
if (semid<0)
{
perror("Semaphore creation failed ");
}
if ((pid = fork()) < 0)
{
perror("fork");
return 1;
}
else if(pid==0)
{
sem_wait(&semid);
printf("IN CHILD PROCESS :\n");
mutex++;
printf("value of shared variable =%d",mutex);
sem_post(&semid);
return 0;
}
sem_wait(&semid);
printf("IN PARENT PROCESS :\n");
mutex--;
printf("value of shared variable =%d",mutex);
sem_post(&semid);
return 0;
}
Your Fundamentals are wrong, the program won't work, so go through the basics and rewrite the program.
Some of the corrections you must make are:
1) You must make a variable of semaphore type
sem_t semvar;
2) The functions sem_wait(), sem_post() require the semaphore variable but you are passing the semaphore id, which makes no sense.
sem_wait(&semvar);
//your critical section code
sem_post(&semvar);
3) You are passing the semaphore to sem_wait() and sem_post() without initializing it. You must initialize it to 1 (in your case) before using it, or you will have a deadlock.
ret = semctl( semid, 1, SETVAL, sem);
if (ret == 1)
perror("Semaphore failed to initialize");
Study the semaphore API's from the man page and go through this example.
The fundamental issue with your code is that you mix two APIs. Unfortunately online resources are not great at pointing this out, but there are two semaphore APIs on UNIX-like systems:
POSIX IPC API, which is a standard API
System V API, which is coming from the old Unix world, but practically available almost all Unix systems
Looking at the code above you used semget() from the System V API and tried to post through sem_post() which comes from the POSIX API. It is not possible to mix them.
To decide which semaphore API you want you don't have so many great resources. The simple best is the "Unix Network Programming" by Stevens. The section that you probably interested in is in Vol #2.
These two APIs are surprisingly different. Both support the textbook style semaphores but there are a few good and bad points in the System V API worth mentioning:
it builds on semaphore sets, so once you created an object with semget() that is a set of semaphores rather then a single one
the System V API allows you to do atomic operations on these sets. so you can modify or wait for multiple semaphores in a set
the SysV API allows you to wait for a semaphore to reach a threshold rather than only being non-zero. waiting for a non-zero threshold is also supported, but my previous sentence implies that
the semaphore resources are pretty limited on every unixes. you can check these with the 'ipcs' command
there is an undo feature of the System V semaphores, so you can make sure that abnormal program termination doesn't leave your semaphores in an undesired state
Vary the consumer-rate and the producer-rate (using sleep), to better understand the operation of code. The code below is the consumer-producer simulation (over a max-limit on container).
Code for your reference:
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
sem_t semP, semC;
int stock_count = 0;
const int stock_max_limit=5;
void *producer(void *arg) {
int i, sum=0;
for (i = 0; i < 10; i++) {
while(stock_max_limit == stock_count){
printf("stock overflow, production on wait..\n");
sem_wait(&semC);
printf("production operation continues..\n");
}
sleep(1); //production decided here
stock_count++;
printf("P::stock-count : %d\n",stock_count);
sem_post(&semP);
printf("P::post signal..\n");
}
}
void *consumer(void *arg) {
int i, sum=0;
for (i = 0; i < 10; i++) {
while(0 == stock_count){
printf("stock empty, consumer on wait..\n");
sem_wait(&semP);
printf("consumer operation continues..\n");
}
sleep(2); //consumer rate decided here
stock_count--;
printf("C::stock-count : %d\n", stock_count);
sem_post(&semC);
printf("C::post signal..\n");
}
}
int main(void) {
pthread_t tid0,tid1;
sem_init(&semP, 0, 0);
sem_init(&semC, 0, 0);
pthread_create(&tid0, NULL, consumer, NULL);
pthread_create(&tid1, NULL, producer, NULL);
pthread_join(tid0, NULL);
pthread_join(tid1, NULL);
sem_destroy(&semC);
sem_destroy(&semP);
return 0;
}
Please check this out below sample code for semaphore implementation(Lock and unlock).
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include<string.h>
#include<malloc.h>
#include <sys/sem.h>
int main()
{
int key,share_id,num;
char *data;
int semid;
struct sembuf sb={0,-1,0};
key=ftok(".",'a');
if(key == -1 ) {
printf("\n\n Initialization Falied of shared memory \n\n");
return 1;
}
share_id=shmget(key,1024,IPC_CREAT|0744);
if(share_id == -1 ) {
printf("\n\n Error captured while share memory allocation\n\n");
return 1;
}
data=(char *)shmat(share_id,(void *)0,0);
strcpy(data,"Testing string\n");
if(!fork()) { //Child Porcess
sb.sem_op=-1; //Lock
semop(share_id,(struct sembuf *)&sb,1);
strncat(data,"feeding form child\n",20);
sb.sem_op=1;//Unlock
semop(share_id,(struct sembuf *)&sb,1);
_Exit(0);
} else { //Parent Process
sb.sem_op=-1; //Lock
semop(share_id,(struct sembuf *)&sb,1);
strncat(data,"feeding form parent\n",20);
sb.sem_op=1;//Unlock
semop(share_id,(struct sembuf *)&sb,1);
}
return 0;
}