Shared memory in C and missing MAP_ANONYMOUS? - c

I am writing a C program where I am dealing out cards to n players, represented by n forked processes. I wish for them to all share the same deck of cards, so I am attempting to use mmap() to keep track of the deck size, however the machine I have to compile this program for does not allow MAP_ANONYMOUS or MAP_ANON. Is there another way that I can store a global variable in shared memory that would still be C89/pre Linux 2.4 compliant?
My program for context:
static int *deck_size;
int pop(int *arr, int *size, int loc)
{
int i;
int val = arr[loc];
for(i = loc; i < (*size - 1); i++)
{
arr[i] = arr[(i+1)];
arr[*size] = '\0';
}
*size = *size-1;
return val;
}
int main(int argc, char* argv[])
{
pid_t pid, wpid;
int status, index, players, rdm_card;
char outbuf[100];
int deck[] =
{1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,8,8,8,8,9,9,9,9,10,10,10,10,11,11,11,11,12,12,12,12,13,13,13,13};
deck_size = mmap(NULL, sizeof *deck_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
*deck_size = 52;
/* reject an execution with no arguments */
if(argv[1] == NULL)
{
write(STDERR_FILENO, "Usage: dealer <n>\n", 18);
exit(EXIT_FAILURE);
}
else
{
if( (players = atoi(argv[1])) < 1)
{
write(STDERR_FILENO, "n cannot be less than 1\n", 24);
exit(EXIT_FAILURE);
}
}
srand ( time(NULL) );
rdm_card = rand() % *deck_size;
for(index = 0; index < players; index++)
{
pid = fork();
if (pid == 0) {
sprintf(outbuf, "random card: %d\n", pop(deck, deck_size, rdm_card));
write(STDOUT_FILENO, outbuf, 17);
printf("size of deck %d!\n", *deck_size);
exit(EXIT_SUCCESS);
} else if (pid < 0) {
write(STDERR_FILENO, "fork error\n", 11);
exit(EXIT_FAILURE);
} else {
do {
wpid = waitpid(pid, &status, WUNTRACED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
}
return 0;
}

When you clearly read the man page MMAP(2) it clearly state that MAP_ANONYMOUS is supported since Linux kernel 2.4.
The use of
MAP_ANONYMOUS in conjunction with MAP_SHARED is supported on
Linux only since kernel 2.4.
Make sure you define _BSD_SOURCE or _SVID_SOURCE to get MAP_ANONYMOUS
#define _BSD_SOURCE

Related

How many processes will be created

I have this piece of code and was wondering how many processes will be created. I am uncertain as I think because of the loop it will be 12 processes but also could be 8.
#include <unistd.h>
#include <sys/types.h>
int main() {
pid_t childpid;
int i;
childpid = fork();
for (i = 0; i < 3 && childpid == 0; i++) {
if (childpid == -1) {
perror("Failed to fork.");
return 1;
}
fprintf(stderr, "A\n");
childpid = fork();
if (childpid == 0) {
fprintf(stderr, "B\n");
childpid = fork();
fprintf(stderr, "C\n");
}
}
return 0;
}
Yes, you can use aptitude to evaluate how many processes are created, but I let the program decide it for me.
The child and parent are synchronized using semaphore, and a pcount variable is used to keep track of the number of created processes. pcount is incremented whenever childpid is evaluated to zero. Below are the additions to your program.
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
int main(void)
{
/* Initialize and setup a semaphore */
sem_t* sema = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (sema == MAP_FAILED)
exit(1);
if (sem_init(sema, 1, 1) != 0)
exit(1);
/* Initialize pcount */
int* pcount = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (pcount == MAP_FAILED)
exit(1);
*pcount = 1;
printf("pcount = %d\n", *pcount);
pid_t childpid;
int i;
childpid = fork();
if (childpid == 0) {
sem_wait(sema);
*pcount = *pcount + 1;
printf("pcount = %d\n", *pcount);
sem_post(sema);
}
for (i = 0; i < 3 && childpid == 0; i++) {
if (childpid == -1) {
perror("Failed to fork.");
return 1;
}
fprintf(stderr, "A\n");
childpid = fork();
if (childpid == 0) {
sem_wait(sema);
*pcount = *pcount + 1;
printf("pcount = %d\n", *pcount);
sem_post(sema);
fprintf(stderr, "B\n");
childpid = fork();
if (childpid == 0) {
sem_wait(sema);
*pcount = *pcount + 1;
printf("pcount = %d\n", *pcount);
sem_post(sema);
}
fprintf(stderr, "C\n");
}
}
return 0;
}
Terminal Session:
$ gcc SO.c -lpthread
$ ./a.out
pcount = 1
pcount = 2
A
pcount = 3
B
C
pcount = 4
C
A
pcount = 5
B
C
pcount = 6
C
A
pcount = 7
B
C
pcount = 8
C
So yes, 8 is the answer. Easy-Peasy :)
8 processes will be created.
Here is how it looks like after the first loop:

shm_open segmentation fault and permission failed

I am new to Linux and i am trying to create a shared memory object which stores the collatz conjecture calculated in child process and prints it in parent process. I have already read the man pages for the commands.
When I create the object it prints permission denied and segmentation fault(core dumped). Only one time it somehow passed that step then I got the mapping failed error.I am using ubuntu 18.04 on a virtual machine
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<sys/wait.h>
#include<sys/stat.h>
int main(int argc,char** argv)
{
pid_t pid;
pid = fork();
int page_size = 4096;
char obj[] = "name";
int num = atoi(argv[1]);
if(num < 1)
{
printf("Please input a greater number\n");
return 0;
}
if(pid < 0)
{
fprintf(stdout,"Fork failed\n");
}
else if(pid == 0)
{
int fd1 = 0;
void *ptr1 = NULL;
fd1 = shm_open(obj,O_CREAT|O_TRUNC,S_IRWXU);
if (fd1 == -1)
{
perror("error:");
exit(0);
}
ftruncate(fd1,page_size);
ptr1 = mmap(0,page_size,PROT_WRITE,MAP_SHARED,fd1,0);
if(ptr1 == MAP_FAILED)
{
fprintf(stdout,"Mapping failed");
exit(0);
}
else
{
while(num != 1)
{
if(num%2 == 0)
num = num / 2;
else
num = (num * 3) + 1;
sprintf(ptr1,"%d, ",num);
}
}
}
else
{
wait(NULL);
int fd = 0;
void *ptr = NULL;
shm_open(obj,O_RDONLY,S_IRWXU);
ptr = mmap(0,page_size,PROT_READ,MAP_SHARED,fd,0);
char *pr = (char *)ptr;
fprintf(stdout,pr);
shm_unlink(obj);
}
return 0;
}
Your code needs a couple of changes, some for better programming practices and some for fixing the actual functionality:
#include <all.h>
int main(int argc, char **argv) {
pid_t pid;
pid = fork();
int page_size = 4096;
char obj[] = "name";
// check argc otherwise you'll segfault
if (argc < 2) {
printf("Please provide a second argument.\n");
// if the program errors it should not return 0
return 1;
}
int num = atoi(argv[1]);
if (num < 1) {
printf("Please input a greater number\n");
return 1;
}
if (pid < 0) {
// no need for fprintf(stdout
printf("Fork failed\n");
} else if (pid == 0) {
int fd1 = 0;
char *ptr1 = NULL;
// we need to open with O_RDWR otherwise mmap will fail
fd1 = shm_open(obj, O_CREAT | O_TRUNC | O_RDWR, 0666);
if (fd1 == -1) {
perror("shm_open error");
return 1;
}
ftruncate(fd1, page_size);
// cast it to char* directly, no need for void pointer stuff
ptr1 = (char *) mmap(0, page_size, PROT_WRITE, MAP_SHARED, fd1, 0);
if (ptr1 == MAP_FAILED) {
// perror for debugging
perror("mmap error");
printf("Mapping failed.\n");
return 1;
} else {
while (num != 1) {
if (num % 2 == 0)
num = num / 2;
else
num = (num * 3) + 1;
// we need to shift the pointer so it stops truncating
ptr1 += sprintf(ptr1, "%d, ", num);
}
}
} else {
wait(NULL);
// move these to one like and cast to char* directly
int fd = shm_open(obj, O_RDONLY, S_IRWXU);
char *ptr = (char*) mmap(0, page_size, PROT_READ, MAP_SHARED, fd, 0);
// no need for fprintf(stdout, and also use %s\n as a format to add a newline
printf("%s\n", ptr);
shm_unlink(obj);
}
return 0;
}

inter process communication using shared memory(mmap) and semaphores

I am trying to improvise a program I had written for single producer multiple consumer multi threading using counting semaphores. I want to implement inter process communication using shared memory (mmap() system call). I want to use anonymous mapping with no backing file.
This is the structure I want to share between the parent and its multiple child processes.
typedef struct Buffer{
char **Tuples;
sem_t buf_mutex,empty_count,fill_count;
} Buffer;
Buffer buffer[100];
The parent process is the mapper() function which does produces something and puts it in buffer[i], based on some inputs. The child processes go to reducer() function which consumes what is put in its buffer[j]. Each reducer or child process should have access to its buffer. The child processes are forked() in the main function and then the parent process control goes to mapper(). I have initialized the synchronization primitives to be process shared.
Is my main() method the correct way of doing it ? I am also getting type casting errors for return value of mmap(), which is a pointer, but I am not sure how to handle it and then use it. I also think malloc() should not be used in line 47 for allocating space to tuples but instead mmap() itself should be used. Can anyone please help ?
This is my program -
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/mman.h>
typedef struct Buffer{
char **Tuples;
// int count;
sem_t buf_mutex,empty_count,fill_count;
} Buffer;
Buffer buffer[100];
int numOfSlots;
int numOfReducers;
void mapper(){
//Synchronization primitives (counting semaphores) used for synchronization
//Produce something and put it in buffer[i]
}
void reducer(long b){
//Synchronization primitives (counting semaphores) for synchronization
//Consume from buffer[b]
}
int main (int argc, char *argv[]){
if(argc != 3) {
if(argc < 3)
printf("Insufficient arguments passed\n");
else
printf("Too many arguments passed\n");
return 1;
}
int i;
long r;
numOfSlots = atoi(argv[1]);
numOfReducers = atoi(argv[2]);
for(i=0; i<numOfReducers; i++){
buffer[i] = (struct Buffer *) mmap(NULL, sizeof(buffer[i]), PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (buffer[i] == MAP_FAILED)
errExit("mmap");
buffer[i].Tuples = malloc(numOfSlots * sizeof(char *));
sem_init(&buffer[i].buf_mutex, 1, 1);
sem_init(&buffer[i].fill_count, 1, 0);
sem_init(&buffer[i].empty_count, 1, numOfSlots);
}
for(r=0;r<numOfReducers;r++){ // loop will run n times (n=5)
if(fork() == 0){
printf("[son] pid %d from [parent] pid %d\n",getpid(),getppid());
Reducer(r);
exit(0);
}
}
mapper();
for(r=0;r<numOfReducers;r++) // loop will run n times (n=5)
wait(NULL);
}
These are the links I am trying to follow -
https://computing.llnl.gov/tutorials/pthreads/man/pthread_mutexattr_init.txt
https://github.com/bradfa/tlpi-dist/blob/master/mmap/anon_mmap.c
Thanks,
Harrish
After researching on the net. I came up with this solution which works.
One more thing I learnt is char *var="hello" is stored in read only memory in the text segment, which means the child processes can also access it. So strcpy() would be a better option for anything else.
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <string.h>
typedef struct Buffer{
char **Tuples;
// int count;
sem_t buf_mutex,empty_count,fill_count;
int inSlotIndex;
int outSlotIndex;
} Buffer;
Buffer *buffer;
int numOfSlots;
int numOfReducers;
int *done;
void mapper(){
//Synchronization primitives (counting semaphores) used for synchronization
//read continuously from a file, produce something and put it in buffer[i]
//Here is an example
char *temp = "trail";
sem_wait(&buffer[1].empty_count);
sem_wait(&buffer[1].buf_mutex);
// Use strcpy() for anything other than string literal
buffer[1].Tuples[0] = temp;
buffer[1].inSlotIndex = 3;
buffer[1].outSlotIndex = 4;
sem_post(&buffer[1].buf_mutex);
sem_post(&buffer[1].fill_count);
*done = 1;
}
void reducer(long tid, Buffer *buffer, int *done){
//Synchronization primitives (counting semaphores) used for synchronization
//Consume from buffer[b]
sem_wait(&buffer[tid].fill_count);
sem_wait(&buffer[tid].buf_mutex);
printf("%s\n", buffer[tid].Tuples[0]);
printf("%d\n", buffer[tid].inSlotIndex);
printf("%d\n", buffer[tid].outSlotIndex);
sem_post(&buffer[tid].buf_mutex);
sem_post(&buffer[tid].empty_count);
if(*done == 1)
printf("DONE\n");
}
int main (int argc, char *argv[])
{
if(argc != 3) {
if(argc < 3)
printf("Insufficient arguments passed\n");
else
printf("Too many arguments passed\n");
return 1;
}
srand(time(NULL));
int i, j;
long r;
char *temp;
numOfSlots = atoi(argv[1]);
numOfReducers = atoi(argv[2]);
buffer = (struct Buffer *)mmap(NULL, numOfReducers * sizeof(struct Buffer), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if(buffer == MAP_FAILED){
printf("EXITING");
exit(EXIT_FAILURE);
}
done = (int *)mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (done == MAP_FAILED){
printf("exiting\n");
}
*done = 0;
for(i=0; i<numOfReducers; i++){
buffer[i].Tuples = mmap(NULL, numOfSlots * sizeof(char *), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if(buffer[i].Tuples == MAP_FAILED){
printf("EXITING");
exit(EXIT_FAILURE);
}
for(j=0; j<numOfSlots; j++){
temp = (char *)mmap(NULL, 30 * sizeof(char), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
temp = strcpy(temp, "");
buffer[i].Tuples[j] = temp;
}
sem_init(&buffer[i].buf_mutex, 1, 1);
sem_init(&buffer[i].fill_count, 1, 0);
sem_init(&buffer[i].empty_count, 1, numOfSlots);
}
for(r=0;r<numOfReducers;r++){ // loop will run n times (n=5)
if(fork() == 0){
printf("[son] pid %d from [parent] pid %d\n",getpid(),getppid());
reducer(r, buffer, done);
exit(0);
}
}
mapper();
for(r=0;r<numOfReducers;r++) // loop will run n times (n=5)
wait(NULL);
}

Forking 3 processes, using shared memory

I have an assignment, and im beating my head against the wall. It is in C. I have a feeling im close to the solution, however I cant get the program to do whats required. I am changing the numbers and some small details, because most of the class is as stumped as I.
Requirements: Create 3 processes, the first one will increment a shared memory variable "total->value" from 1 to 10000, the second from 10000 to 12000, the third from 12000 to 14000
The process functions are labeled such (process1(), process2(), process3())
and the internals of those functions are as follows
process1()
{
int k = 0;
while (k < 10000)
{
k++;
total->value = total->value + 1;
}
printf("From process 1 = %d/n", total->value);
}
The second would be k < 2000 (because it only needs to increment the shared value 2000 more) and etc.
The main portion of the program is:
main()
{
int shmid;
int pid1;
int pid2;
int pid3;
int ID;
int status;
char *shmadd = (char *)0;
/* Create and connect to a shared memory segmentt */
if ((shmid = shmget(SHMKEY, sizeof (int), IPC_CREAT | 0666)) < 0)
{
perror("shmget");
exit(1);
}
if ((total = (shared_mem *)shmat(shmid, shmadd, 0)) == (shared_mem *)-1)
{
perror("shmat");
exit(0);
}
total->value = 0;
if ((pid1 = fork()) == 0)
process1();
if ((pid1 != 0) && (pid2 = fork()) == 0)
process2();
if ((pid1 != 0) && (pid2 != 0) && (pid3 = fork()) == 0)
process3();
if ((pid1 != 0) && (pid2 != 0) && (pid3 != 0))
{
if ((shmctl(shmid, IPC_RMID, (struct shmid_ds *)0)) == -1)
{
perror("shmctl");
exit(-1);
}
printf("\t\t End of Program.\n");
}
}
What I need is for the first process to finish, before the 2nd starts. I tried inserting a wait(&status) after the process1() (or 2 or 3) calls and am at a loss. Any pointers? (no pun intended) =) there is more to implement, but I believe once I have this part I can handle the rest on my own. I have been intentionally vague in some regards, but I would like to finish this project and more importantly understand it and there are others who want a free lunch. I will provide anything else in the code that is required. Thank you in advance for your help
The output should appear
From process 1 = 10000
From process 2 = 12000
From process 3 = 14000
I believe that Celada's comment/guess on the requirements is correct. However, barring that, and at the risk of doing too much work, the following code fulfills your spec. The use of the gcc built-in __sync_fetch_and_add() is perhaps unnecessary.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/wait.h>
static struct {
int value;
} *total;
static void process1(void) {
int k = 0;
while (k < 10000) {
k++;
__sync_fetch_and_add(&total->value, 1);
}
printf("From process 1 = %d\n", total->value); //<-- not quite right: could be >10000
}
static void process2(void) {
int k = 0;
while (__sync_fetch_and_add(&total->value, 0) != 10000)
;
while (k < 2000) {
k++;
__sync_fetch_and_add(&total->value, 1);
}
printf("From process 2 = %d\n", total->value);
}
static void process3(void) {
int k = 0;
while (__sync_fetch_and_add(&total->value, 0) != 12000)
;
while (k < 2000) {
k++;
__sync_fetch_and_add(&total->value, 1);
}
printf("From process 3 = %d\n", total->value);
}
int main(void) {
int shmid;
int pid1;
int pid2;
int pid3;
int status;
/* Create and connect to a shared memory segment */
if ((shmid = shmget(1234, sizeof *total, IPC_CREAT|0666)) < 0) {
perror ("shmget");
exit (1);
}
if ((total = shmat(shmid, 0, 0)) == (void *)-1) {
perror("shmat");
exit (0);
}
total->value = 0; // not necessary in Linux if IPC_CREAT invoked
if (!(pid1 = fork()))
process1();
else if (!(pid2 = fork()))
process2();
else if (!(pid3 = fork()))
process3();
else {
wait(&status);
wait(&status);
wait(&status);
if ((shmctl(shmid, IPC_RMID, (struct shmid_ds *) 0)) == -1) {
perror("shmctl");
exit (-1);
}
printf("\t\t End of Program.\n");
}
return 0;
}

working with named pipes and semaphores in linux

I've been trying to get my program to work for several hours now and I just can't fgure out what's wrong with my code. It's about passing a variable between processess using pipes. Each process increments it M times. The program works perfectly when I use shared memory, but when I change it to using pipes it's a disaster. Creating or using named pipes doesn't seem to work at all, or I guess I'm just doing it the wrong way. Here's the source code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <unistd.h>
#include <memory.h>
#include <fcntl.h>
#include <sys/stat.h>
#define PIPE_NAME "MY_PIPE"
#define N 5
#define M 10
struct sembuf operations;
int semid;
key_t key;
int marker;
void semWait(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = -1;
operations.sem_flg = 0;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop wait\n");
exit(-1);
}
}
void semPost(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = 1;
operations.sem_flg = IPC_NOWAIT;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop post\n");
exit(-1);
}
}
void worker(int id) {
int j, nmarker;
int fd = open(PIPE_NAME, O_RDWR);
read(fd, &nmarker, sizeof(int));
for (j = 0 ; j < M; j++) {
semWait(semid, id);
nmarker = nmarker + 1 ;
printf("%d ", marker);
semPost(semid, N);
}
write(fd, &nmarker, sizeof(nmarker));
close(fd);
}
main() {
int i, tempPID;
int sarray[N+1] = {0};
key = 23;
marker = 0;
if ((semid = semget(key , N+1, 0666 | IPC_CREAT)) == -1) {
perror("ERROR: semget\n");
exit(-1);
}
if ((semctl(semid, N+1, SETALL, sarray)) < 0) {
perror("ERROR: semctl - val\n");
exit(-1);
}
if(mkfifo(PIPE_NAME, S_IFIFO | 0666) < 0) {
perror("ERROR:pipe\n");
exit(-1);
}
int fd;
if( fd = open(PIPE_NAME, O_WRONLY) < 0 ){
perror("ERROR:open\n");
exit(-1);
}
write(fd, &marker, sizeof(marker));
close(fd);
for(i = 0; i < N; i++) {
tempPID = fork();
if (tempPID < 0) {
perror("ERROR: fork\n");
exit(-1);
}
else if (tempPID == 0) { // if child
worker(i);
exit(0);
}
}
for (i = 0 ; i < (M*N); i++) {
semPost(semid, i%N);
semWait(semid, N);
}
printf("Marker = %d\n", marker);
if (semctl( semid, 1, IPC_RMID ) == -1) {
perror("ERROR: semctl free\n");
exit(-1);
}
unlinc(PIPE_NAME);
}
I create N worker processes and each one has to increment the marker value M times. I have to create a pool of 'sleeping' processes and waken them one by one using semaphores but it's all a blur so the current source code is all I came up with... :\
This is a version of the same program but with shared memory instead of pipes:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/mman.h>
#define N 5
#define M 10
struct sembuf operations;
int semid;
key_t key;
int *sharedmem;
void semWait(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = -1;
operations.sem_flg = 0;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop wait\n");
exit(-1);
}
}
void semPost(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = 1;
operations.sem_flg = IPC_NOWAIT;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop post\n");
exit(-1);
}
}
void worker(int id) {
int j;
for (j = 0 ; j < M; j++) {
semWait(semid, id);
(*sharedmem)++;
semPost(semid, N);
}
}
main() {
int i, tempPID;
int sarray[N+1] = {0};
int protect = PROT_READ | PROT_WRITE;
int flags = MAP_SHARED | MAP_ANONYMOUS;
if ((key = ftok("/dev/null", 4343)) == -1) {
perror("ERROR: ftok\n");
exit(-1);
}
if ((semid = semget(key , N+1, 0666 | IPC_CREAT)) == -1) {
perror("ERROR: semget\n");
exit(-1);
}
if ((semctl(semid, N+1, SETALL, sarray)) < 0) {
perror("ERROR: semctl - val\n");
exit(-1);
}
sharedmem = (int*)mmap(NULL, sizeof(int), protect, flags, 0, 0);
*(sharedmem) = 0;
for(i = 0; i < N; i++) {
tempPID = fork();
if (tempPID < 0) {
perror("ERROR: fork\n");
exit(-1);
}
else if (tempPID == 0) { // if child
worker(i);
exit(0);
}
}
for (i = 0 ; i < (M*N); i++) {
semPost(semid, i%N);
semWait(semid, N);
}
printf("Marker = %d\n", *sharedmem);
if (semctl( semid, 1, IPC_RMID ) == -1) {
perror("ERROR: semctl free\n");
exit(-1);
}
munmap(sharedmem, sizeof(int));
}
Some of your problems are in the worker code - these two lines:
int fd = open(PIPE_NAME, O_RDWR);
read(fd, &nmarker, sizeof(int));
If you open the pipe for reading and writing, you are asking for trouble (IMNSHO). Open it for reading only, read it, close it. Then open it for writing only, write to it, close it. Now you have to consider where the semaphore operation should occur. You actually need to wake the next process before you try to open the pipe for writing, because the open for writing will block until there is a process available to read from it. Similarly, the process that opens for reading will block until there is a process available to write to it. So, the kernel will coordinate the processes.
You don't check the return value from open(), so you've no idea whether you got a valid file descriptor. Always check the return status of open().
You don't check the return value from read(), so you've no idea whether you got anything valid off the pipe. Always check the return status of read().
(You can decide to ignore the return status of write() if there is no meaningful error recovery possible for a failed write, but it is not a bad idea to check that it did work. You can decide to ignore the return status of close() for similar reasons, though you might not get to know about problems until you do the close().)
Continuing in the worker code:
for (j = 0 ; j < M; j++) {
semWait(semid, id);
nmarker = nmarker + 1 ;
printf("%d ", marker);
semPost(semid, N);
}
It is surprising to see you printing marker rather than nmarker; and surely, basic diagnostic technique prints the value of nmarker when it is read. You might or might not print j and nmarker on each iteration. Note that since nothing in this code increments marker, the value printed won't change.
The logic sequence here is interesting...it combines with the loop in main() most oddly. The parent process writes one value to the FIFO. Only one child gets to read that value - the rest get EOF immediately, or hang indefinitely (depending on whether you use O_RDONLY or O_RDWR in the children). Each child gets signalled to increment its value, does so, and then goes back to sleep until woken again. There is nothing that sends the incremented value to the next child. So each child is independently incrementing whatever value it chooses - which is probably garbage. With shared memory, if you had a pointer to the shared value, then the increments were seen by all processes at once - that's why it is called shared memory. But here there is no shared memory, so you have to communicate explicitly to get it to work. (I wonder if your FIFO plus shared memory implementation worked because the communication was via shared memory - by accident, in other words?)
So, if the child is to increment the variable it reads each time, it must both read the current value and write the new value each time around the loop. This would be an error-checked read, of course. You might be OK with O_RDWR because of the semaphores, but I'd personally be happier with the separate opens for read and write - on each iteration if need so be. But I haven't implemented this to check that it really does run into problems; it is simply aconventional to use O_RDWR on a FIFO.
After your child has incremented its value N times, it writes the result to the pipe.
write(fd, &nmarker, sizeof(nmarker));
close(fd);
The main program then does:
printf("Marker = %d\n", marker);
if (semctl( semid, 1, IPC_RMID ) == -1) {
perror("ERROR: semctl free\n");
exit(-1);
}
unlinc(PIPE_NAME);
Since it has not modified marker, the value printed will be 0. You should be having the main process read the replies from each of the children.
The correct function for unlinking a FIFO is unlink() or remove().
Discussion
As noted in a comment, one problem was that opening the FIFO was blocking - no readers. However, that was far from the only problem.
The code below runs. I haven't verified that the number is being incremented as it should (but it is being incremented). I've not checked that every process is getting its turn. I've revised the error handling (one line per call instead of 3 or 4), and added a printing function that includes the PID in the output. I've error checked every system call (but none of the printing statements). I fixed a problem if (fd = open(...) < 0). As far as I could tell, closing the FIFO in the master process discarded the content written to it - so the parent no longer closes the FIFO immediately. But mainly I moved the read and write of the FIFO into the worker loop - leaving open and close outside. The code is also laced with diagnostic printing so I can see where it is going wrong when it is going wrong. I haven't done header minimization or any of a number of other cleanups that should occur. However, everything except main() is static so it doesn't have to be pre-declared. It compiles clean under:
/usr/bin/gcc -O3 -g -std=c99 -Wall -Wextra fifocircle.c -o fifocircle
Code
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <unistd.h>
#include <memory.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
static const char *arg0 = "undefined";
static void err_error(const char *fmt, ...)
{
int errnum = errno;
va_list args;
fflush(0);
fprintf(stderr, "%s: pid %d:", arg0, (int)getpid());
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "(%d: %s)", errnum, strerror(errnum));
fputc('\n', stderr);
exit(1);
}
static void print(const char *fmt, ...)
{
va_list args;
printf("pid %d: ", (int)getpid());
va_start(args, fmt);
vfprintf(stdout, fmt, args);
va_end(args);
fflush(0);
}
#define PIPE_NAME "MY_PIPE"
#define N 5
#define M 10
static struct sembuf operations;
static int semid;
static key_t key;
static int marker;
static void semWait(int semid, int sempos)
{
operations.sem_num = sempos;
operations.sem_op = -1;
operations.sem_flg = 0;
if (semop(semid, &operations, 1) < 0)
err_error("semop wait");
}
static void semPost(int semid, int sempos)
{
operations.sem_num = sempos;
operations.sem_op = 1;
operations.sem_flg = IPC_NOWAIT;
if (semop(semid, &operations, 1) < 0)
err_error("semop post");
}
static void worker(int id)
{
int j;
int fd = open(PIPE_NAME, O_RDWR);
if (fd < 0)
err_error("failed to open FIFO %s for read & write", PIPE_NAME);
print("Worker %d: fd %d\n", id, fd);
for (j = 0 ; j < M; j++)
{
int nmarker;
print("waiting for %d\n", id);
semWait(semid, id);
if (read(fd, &nmarker, sizeof(int)) != sizeof(int))
err_error("short read from FIFO");
print("Got %d from FIFO\n", nmarker);
nmarker = nmarker + 1 ;
if (write(fd, &nmarker, sizeof(nmarker)) != sizeof(nmarker))
err_error("short write to FIFO");
print("Wrote %d to FIFO\n", nmarker);
print("posting %d\n", id);
semPost(semid, N);
}
if (close(fd) != 0)
err_error("failed to close FIFO");
print("done\n");
}
int main(int argc, char **argv)
{
int i;
int sarray[N+1] = {0};
key = 23;
marker = 0;
arg0 = argv[0];
if (argc != 1)
err_error("Usage: %s\n", arg0);
if ((semid = semget(key , N+1, 0666 | IPC_CREAT)) == -1)
err_error("semget");
if ((semctl(semid, N+1, SETALL, sarray)) < 0)
{
perror("ERROR: semctl - val\n");
exit(-1);
}
if (mkfifo(PIPE_NAME, S_IFIFO | 0666) < 0)
err_error("failed to create FIFO %s\n", PIPE_NAME);
print("FIFO created\n");
int fd;
if ((fd = open(PIPE_NAME, O_RDWR)) < 0 )
err_error("failed to open FIFO %s\n", PIPE_NAME);
print("FIFO opened\n");
if (write(fd, &marker, sizeof(marker)) != sizeof(marker))
err_error("short write to FIFO");
print("FIFO loaded\n");
print("Master: about to fork\n");
for (i = 0; i < N; i++)
{
pid_t pid = fork();
if (pid < 0)
err_error("failed to fork");
else if (pid == 0)
{
worker(i);
exit(0);
}
}
print("Master: about to loop\n");
for (i = 0 ; i < (M*N); i++)
{
print("posting to %d\n", i%N);
semPost(semid, i%N);
print("waiting for %d\n", N);
semWait(semid, N);
}
if (close(fd) != 0)
err_error("failed to close FIFO");
print("Marker = %d\n", marker);
if (semctl( semid, 1, IPC_RMID ) == -1)
err_error("semctl remove");
if (unlink(PIPE_NAME) != 0)
err_error("failed to remove FIFO %s", PIPE_NAME);
return(0);
}

Resources