Semaphores and the standard out buffer - c

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.

Related

Why does my solution to The Cigarette Smoker's Problem not terminate?

I wrote a solution to The Cigarette Smoker's Problem that does not terminate.
A full description of the problem and pseudo-code for the solution can be found here. Briefly, the task is to synchronize four processes using semaphores.
I used the pseudo-code as a guide for writing my solution. The following is my C implementation of the solution:
#include "sem.h" //Allows the use of semget(), sem_create(), and the P() and V() functions.
#include <stdlib.h> //Allows the use of srand() and rand().
#include <time.h> //Allows the use of time().
#include <stdbool.h> //Allows the use of the boolean variable that terminates smoker processes
#include <unistd.h> //Allows the use of fork().
#include <sys/wait.h> //Allows the use of waitpid().
int main()
{
/*Create and initialize semaphores*/
int smkr_matches = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT); // Smoker that has matches
int smkr_tobacco = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT); // Smoker that has tobacco
int smkr_paper = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT); // Smoker that has paper
int agent = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT);
int lock = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT); // The mutex variable
/*According to http://www.cs.umd.edu/~hollings/cs412/s96/synch/smokers.html,
all semaphores are initialized to zero except "lock", which is initialized to one.
*/
sem_create(smkr_matches, 0);
sem_create(smkr_tobacco, 0);
sem_create(smkr_paper, 0);
sem_create(agent, 0);
sem_create(lock, 1);
/*Seed the random number generator to generate random choice of ingredients.*/
srand(time(NULL));
// Delcare a boolean variable to terminate the smoker processes.
bool done = false;
//Declare variables to store PID after wait() calls and status.
int pid = 0;
int status = 0;
/*Declare variables to store PIDs.*/
int smkr_mat_PID = 0;
int smkr_tob_PID = 0;
int smkr_pap_PID = 0;
int agent_PID = 0;
/*Implement the agent process*/
agent_PID = fork();
if (agent_PID == -1)
{ // Forking the agent process failed
perror("Forking the agent process");
exit(EXIT_FAILURE);
}
else if (agent_PID == 0)
{
printf("AGENT process created (PID = %d).\n", getpid());
int rand_num = 0;
for (int i = 0; i < 4; i++)
{ // Agent will place items on table ten times.
P(lock);
// Randomly choose a pair of ingredients.
rand_num = (rand() % 3) + 1;
// Place the ingredients on the table and wake the appropriate smoker.
if (rand_num == 1)
{
printf("AGENT has placed TOBACCO and PAPER on the table.\n");
printf("AGENT wakes smoker with MATCHES.\n");
V(smkr_matches);
}
else if (rand_num == 2)
{
printf("AGENT has placed TOBACCO and MATCHES on the table.\n");
printf("AGENT wakes smoker with PAPER.\n");
V(smkr_paper);
}
else if (rand_num == 3)
{
printf("AGENT places MATCHES and PAPER on the table.\n");
printf("AGENT wakes smoker with TOBACCO.\n");
V(smkr_tobacco);
}
// Allow the smoker to work
V(lock);
printf("AGENT will now sleep.\n");
P(agent);
}
done = true;
}
/*Implement the smoker with tobacco*/
else
{
smkr_tob_PID = fork();
if (smkr_tob_PID == -1)
{
perror("Forking smoker with tobacco");
exit(EXIT_FAILURE);
}
else if (smkr_tob_PID == 0)
{
printf("SMOKER WITH TOBACCO created. (PID = %d)\n", getpid());
while(done == false)
{
P(smkr_tobacco);
P(lock);
printf("SMOKER WITH TOBACCO picks up matches and paper from the table.\n");
V(agent);
V(lock);
printf("SMOKER WITH TOBACCO begins smoking.\n");
}
}
/*Implement the smoker with paper*/
else
{
smkr_pap_PID = fork();
if (smkr_pap_PID == -1)
{
perror("Forking smoker with paper");
exit(EXIT_FAILURE);
}
else if (smkr_pap_PID == 0)
{
printf("SMOKER WITH PAPER created (PID = %d).\n", getpid());
while(done == false)
{
P(smkr_paper);
P(lock);
printf("SMOKER WITH PAPER picks up tobacoo and matches from the table.\n");
V(agent);
V(lock);
printf("SMOKER WITH PAPER begins smoking.\n");
}
}
/*Implement the smoker with matches*/
else
{
smkr_mat_PID = fork();
if (smkr_mat_PID == -1)
{
perror("Forking smoker with matches");
exit(EXIT_FAILURE);
}
else if (smkr_mat_PID == 0)
{
printf("SMOKER WITH MATCHES created (PID = %d).\n", getpid());
while(done == false)
{
P(smkr_matches);
P(lock);
printf("SMOKER WITH MATCHES picks up tobacco and paper from the table.\n");
V(agent);
V(lock);
printf("SMOKER WITH MATCHES begins smoking.\n");
}
}
else
{
pid = wait(&status);
printf("PID = %d terminated with status = %d\n.", pid, status);
pid = wait(&status);
printf("PID = %d terminated with status = %d\n.", pid, status);
pid = wait(&status);
printf("PID = %d terminated with status = %d\n.", pid, status);
pid = wait(&status);
printf("PID = %d terminated with status = %d\n.", pid, status);
}
exit(EXIT_SUCCESS);
}
exit(EXIT_SUCCESS);
}
exit(EXIT_SUCCESS);
}
exit(EXIT_SUCCESS);
}
I ran my solution through a debugger and noticed that the bug occurs when the second wait() call towards the end of the program is made. In executing that wait() call, it seems that my program runs forever. I suspected that an infinite loop was occurring in one of the smoker processes. After reviewing my code further, I noticed that, although the agent process seems to terminate normally in the first wait() call, the boolean variable done does not change its value to true as intended. This could explain why there seems to be an infinite loop.
In attempting to understand why the bug was happening, I tried several changes to the code, but all of them failed to correct the bug. The following is a list of what I tried and the rationale for trying it:
1. Changing the initial value of done from false to true.
My solution initializes done to false. I reasoned that, during execution, if one of the smoker processes started first, perhaps their while-loops would run forever. However, I realized the while-loops could not run forever because the P() operation would block the smoker process. Nevertheless, I changed the initial value of done to true to confirm my reasoning.
2. Having the agent process acquire the lock before assigning true to done.
As mentioned earlier, I noticed that the agent process terminated normally without changing the value of done to true. In an attempt to correct that, I changed the solution to have the agent acquire the lock one more time before changing the value of done and then releasing the lock again to allow the smoker processes to do work.
3. In the implementations of the smoker processes, releasing the lock before incrementing the agent semaphore.
In the while-loops of the smoker processes, after a smoker picks up the ingredients from the table, the agent semaphore is incremented before the lock is released. My instructor suggested that this order of operations could cause a deadlock and advised that I swap the order, releasing the lock first before incrementing the agent semaphore.
4. In the implementation of the agent process, releasing the lock before incrementing the semaphore of a smoker process.
Similar to what happens in the while-loop of the smoker processes, after the agent process randomly selects two ingredients and places them on the table, the agent increments the semaphore of the corresponding smoker process and then releases the lock. I wanted to see what would happen if I applied my instructor's suggestion to the agent process, so I changed the code so that the agent releases the lock before incrementing the semaphore of the smoker process.
The following additional information may be helpful in answering this problem:
The sem.h header file included at the beginning of the code works fine. However, if you would like to see it, here it is:
/************************************************************************/
/* Operating Systems - Fall
/* 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(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(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(int sid) {
semcall(sid, -1);
}
/*
* V operation on semaphore "sid". Should be called upon exit from critical
* region.
*/
static void V(int sid) {
semcall(sid, 1);
}
I wrote my code in Microsoft Visual Studio using Ubuntu within a Virtual Box VM.
Finally, my question is: why does my solution not terminate?
Thank you for your time.

fcntl F_GETLK always returns F_UNLCK

I am trying to understand POSIX file-region locks in C. The program below is really simple, sets the lock to F_WRLCK and then gets locks. There is no errors during opening/setting lock. Unfortunatelly it's always returning F_UNLCK. Where is the mistake ? Is it possible that it doesnt work on OSX correctly ?
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
void printLockType(int lock) {
if ( lock == F_RDLCK ) {
printf("readlock %i \n", lock);
}else if ( lock == F_WRLCK ) {
printf("writelock %i \n", lock);
}else if ( lock == F_UNLCK ) {
printf("unlock %i \n", lock);
} else {
printf("other %i\n", lock);
}
}
int main(int argc, char *argv[])
{
int fd;
struct flock fl ,fl2;
fl2.l_type = F_RDLCK; /* read/write lock */
fl2.l_whence = 0; /* beginning of file */
fl2.l_start = 0; /* offset from l_whence */
fl2.l_len = 100; /* length, 0 = to EOF */
fl2.l_pid = getpid();
fl.l_type = F_WRLCK; /* read/write lock */
fl.l_whence = 0; /* beginning of file */
fl.l_start = 0; /* offset from l_whence */
fl.l_len = 1000; /* length, 0 = to EOF */
fl.l_pid = getpid();
if ((fd = open("xxx", O_RDWR)) == -1) {
perror("open");
exit(1);
}
if (fcntl(fd, F_SETLK, &fl) == -1) {
perror("fcntl");
exit(1);
}
if(fcntl(fd, F_GETLK, &fl2) == -1) {
printf("%s \n", strerror(errno));
} else {
printLockType(fl2.l_type);
}
return 0;
}
You're misunderstanding the F_GETLK query. It returns F_UNLCK when nothing blocks the calling process from placing a lock of the given type at the given position.
Since the calling process is the one that created these existing locks, it can also create this new lock.
The Mac OS X manuals say
F_GETLK
Get the first lock that blocks the lock description pointed to by the third argument, arg,
taken as a pointer to a struct flock (see above). The information retrieved overwrites the
information passed to fcntl in the flock structure. If no lock is found that would prevent
this lock from being created, the structure is left unchanged by this function call except
for the lock type which is set to F_UNLCK.

Private Semaphore in c

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.

How to use POSIX semaphores on forked processes in C?

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.

clone(2) with CLONE_FILES leak fcntl locks?

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#define __USE_GNU
#include <sched.h>
void init_lock(struct flock *f)
{
f->l_type = F_WRLCK; /* write lock set */
f->l_whence = SEEK_SET;
f->l_start = 0;
f->l_len = 0;
f->l_pid = getpid();
}
int lock(int fd, struct flock *f)
{
init_lock(f);
if(fcntl(fd, F_SETLKW, f) == -1) {
fprintf(stderr,"fcntl() failed: %s\n", strerror(errno));
return -1;
}
return 0;
}
int unlock(int fd, struct flock *f)
{
f->l_type = F_UNLCK;
if(fcntl(fd, F_SETLK, f) == -1) {
fprintf(stderr, "fcntl() failed: %s\n", strerror(errno));
return -1;
}
return 0;
}
int file_op(void *arg)
{
char buff[256];
int fd = (int) arg, n;
struct flock my_lock;
printf("Trying to get lock\n");
if(lock(fd, &my_lock) == -1) { /* lock acquired by a thread */
return -1;
}
printf("Got lock: %d\n", getpid()); /* I am printing thread id after lock() */
printf("Enter string to write in file : ");
scanf("%s", buff);
if((n=write(fd, &buff, strlen(buff))) == -1) {
fprintf(stderr, "write() failed: %s\n", strerror(errno));
}
if(unlock(fd, &my_lock) == -1) {
return -1;
}
printf("Lock Released: %d\n", getpid());
return 0;
}
int main()
{
char *stack;
int fd, i=0, cid, stacksize;
if((fd = open("sample.txt", O_CREAT | O_WRONLY | O_APPEND, 0644)) == -1) {
printf("Error in file opening\n");
exit(1);
}
stacksize = 3*1024*1024;
for(i=0; i<5; i++) {
stack = malloc(stacksize);
if((cid = clone(&file_op, stack + stacksize, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD, (void *) fd)) == -1) {
fprintf(stderr,"clone() failed: %s\n", strerror(errno));
break;
}
}
sleep(30);
close(fd);
return 0;
}
I want that every clone() will wait for lock.
But Output of this code (something like this):
Trying to get lock
Trying to get lock
Trying to get lock
Got lock: Got lock: 10287
Got lock: Got lock: 10287
Enter string to write in file : Trying to get lock
Enter string to wriGot lock: 10287
Got lock: 10287
Got lock: 10287
Enter string to write in file : Trying to get lock
Got lock: 10287
Got lock: Enter string to write in file :
But when i am removing CLONE_FILES field set from clone(2), it goes all well. Other clone threads will wait for a lock().
Output of that:
Trying to get lock
Got lock: 10311
Trying to get lock
Trying to get lock
Trying to get lock
Trying to get lock
Any other alternatives (with CLONE_FILES)? And Why this kind of behavior?
Beginner in this field.
The locking provided by flock is per process, not per thread.
From http://linux.die.net/man/2/flock (emphasis mine):
A call to flock() may block if an incompatible lock is held by another process.
Subsequent flock() calls on an already locked file will convert an existing lock to the new lock mode.
Locks created by flock() are associated with an open file table entry.
Although threads are not explicitly mentioned multiple threads share a file table entry whereas multiple processes do not. Passing CLONE_FILES to clone causes your 'processes' to share file tables.
A solution might be to call dup to make more file descriptors. From the documentation:
If a process uses open(2) (or similar) to obtain more than one descriptor for the same
file, these descriptors are treated independently by flock(). An attempt to lock the file
using one of these file descriptors may be denied by a lock that the calling process has
already placed via another descriptor.

Resources