I'm trying to implement producer-consumer problem in C using processes and System V IPC and I'm stuck on one thing. This is early version of my code (without implementing queue operations or even producer and consumer executing in loop) that I was using to learn and test how semaphores work:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
#include <time.h>
#include "sem.h"
#include "shm.h"
#define BUFFER_SIZE 9
int full;
int empty;
int mutex;
struct buff {
int queue[BUFFER_SIZE];
} *buffer;
void producer();
void consumer();
int main() {
int parent_pid, pid, i;
parent_pid = getpid();
int shmid = allocate_shared_memory(sizeof(*buffer));
buffer = (struct buff *) attach_shared_memory(shmid);
for (i = 0; i < BUFFER_SIZE; i++) {
buffer->queue[i] = 0;
}
full = sem_allocate();
empty = sem_allocate();
mutex = sem_allocate();
printf("Full %d\n", full);
printf("Empty %d\n", empty);
printf("Mutex %d\n", mutex);
sem_init(full, 0);
sem_init(empty, BUFFER_SIZE);
sem_init(mutex, 1);
printf("Full value %d\n", sem_get_val(full));
printf("Empty value %d\n", sem_get_val(empty));
printf("Mutex value %d\n", sem_get_val(mutex));
srand(time(0));
pid = fork();
if (!pid) {
printf("Producer here: %d\n", getpid());
producer();
printf("Full value after prod() %d\n", sem_get_val(full));
return 0;
} else printf("Created new producent: %d\n", pid);
sleep(1);
pid = fork();
if (!pid) {
printf("Consumer here: %d\n", getpid());
printf("Full value before cons() %d\n", sem_get_val(full)); //here I always get 0
consumer();
return 0;
} else printf("Created new consumer: %d\n", pid);
while (1)
{
int status;
pid_t done = wait(&status);
if (done == -1)
{
if (errno == ECHILD) break; // no more child processes
}
else
{
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
exit(1);
}
}
}
if (getpid() == parent_pid) {
sem_deallocate(full);
sem_deallocate(empty);
sem_deallocate(mutex);
}
}
void producer() {
sem_wait(empty);
sem_wait(mutex);
printf("Producer is producing!\n");
buffer->queue[0]=0;
sem_post(mutex);
sem_post(full);
}
void consumer() {
sem_wait(full);
sem_wait(mutex);
printf("Consumer is consuming!\n");
sem_post(mutex);
sem_post(empty);
}
int sem_allocate() {
return semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
}
void sem_deallocate(int semid) {
if (semctl(semid, 0, IPC_RMID, NULL) == -1)
{
perror("Error releasing semaphore!\n");
exit(EXIT_FAILURE);
}
}
int sem_init(int semid, int value) {
union semun arg;
arg.val = value;
if (semctl(semid, 0, SETVAL, arg) == -1) {
perror("semctl");
return -1;
}else return 1;
}
int sem_wait(int semid) {
printf("Someone is waiting %d\n", semid);
struct sembuf sem = { 0, -1, SEM_UNDO };
return semop(semid, &sem, 1);
}
int sem_post(int semid) {
printf("Someone is posting %d\n", semid);
struct sembuf sem = { 0, 1, SEM_UNDO };
return semop(semid, &sem, 1);
}
int sem_get_val(int semid) {
return semctl(semid, 0, GETVAL, 0);
}
int allocate_shared_memory(int size) {
return shmget(IPC_PRIVATE, size, IPC_CREAT | SHM_W | SHM_R);
}
void deallocate_shared_memory(const void* addr, int shmid) {
shmctl(shmid, IPC_RMID, 0);
}
void* attach_shared_memory(int shmid) {
return shmat(shmid, NULL, 0);
}
sem.h:
#include <sys/types.h>
#include <errno.h>
union semun {
int val;
struct semid_ds *buf;
ushort* array;
};
int sem_post(int);
int sem_wait(int);
int sem_allocate();
void sem_deallocate(int);
int sem_init(int, int);
int sem_get_val(int);
shm.h:
#include <stdio.h>
#include <sys/shm.h>
#include <sys/stat.h>
int allocate_shared_memory(int size);
void deallocate_shared_memory(const void* addr, int shmid);
void* attach_shared_memory(int shmid);
Why before executing consumer function value of full semaphore is 0? Even if right after producer finishes his job the value is 1...
I'm new to this kind of topics so maybe there is an obvious explenation of the situation, but I have no idea what can I do and hope you can help me.
You initialize the "full" semaphore to zero. Your "child" producer, prior to exiting calls your sem_post() function, which calls semop() with a SEM_UNDO argument.
int sem_post(int semid) {
printf("Someone is posting %d\n", semid);
struct sembuf sem = { 0, 1, SEM_UNDO };
return semop(semid, &sem, 1);
}
The Ubuntu Linux man page for semop says the following about SEM_UNDO:
... If an operation specifies SEM_UNDO, it will be automatically
undone when the process terminates.
This means, "producer" increments "full" prior to exiting, then after it exits the system "undoes" the increment (i.e. it decrements "full") setting it back to zero.
So,for the purposes of the "full" semaphore, you should NOT specify SEM_UNDO.
Related
I'm reading Modern Operating Systems and there's a exercise that asks you to build a counting-semaphore with a binary-semaphore.
I was wondering if I could do it with semaphores & shared memory (C and POSIX IPC).
I'll leave a simplification of my program here.
typedef struct sem {
int semid; /* contains two semaphores with value set to 1, one for the critical region and the other for locking */
int shmid; /* shared counter */
}Sem;
/* the parent process spawns a few child processes and then waits for them */
void child() {
Sem *s;
s = get_sem();
down_sem(s);
printf("working...");
sleep(5);
printf("finished!");
up_sem(s);
}
void down_sem(Sem *s) {
int n;
enter_critical(s); // down to critical region sem
n = get_counter(s);
if (n == 0) {
exit_critical(s); // up to critical region sem
acquire_lock(s); // down to lock sem
} else {
n--;
exit_critical(s); // up to critical region sem
}
}
void up_sem(Sem *s) {
int n;
enter_critical(s);
n = get_counter(s);
n++;
if (n == 1) {
exit_critical(s);
release_lock(s); // up to lock sem
} else {
exit_critical(s);
}
}
But this doesn't work since some processes block in the lock semaphore forever. I'm just now learning these concepts so my design is probably totally off.
I can share the complete code if you wish.
Thanks in advance!
EDIT: requested mre
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/shm.h>
#define NPROC 5
#define SLOTS 2
#define PATH "."
#define SID 'S'
#define SEMN 2
#define SHMID 'M'
#define SHM_SIZE 32
#define CRIT 0
#define LOCK 1
#define UP 1
#define DOWN -1
typedef struct sem {
int shmid;
int semid;
}Sem;
typedef union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
}Args;
void err (char *msg) {
char error[50];
int n;
n = snprintf(error, 50, "[%d] %s", getpid(), msg);
error[n] = 0;
perror(error);
exit(1);
}
int* get_n(Sem *s) {
int *data = NULL;
data = shmat(s->shmid, (void *)0, 0);
if (data == (int *)(-1))
err("get_n shmat");
return data;
}
void dettach_n(int *data) {
if (shmdt(data) == -1)
err("dettach_n shmdt");
}
void enter_crit(Sem *s) {
struct sembuf sb;
sb.sem_num = CRIT;
sb.sem_op = DOWN;
printf("[%d] enter crit\n", getpid());
if (semop(s->semid, &sb, 1) == -1)
err("enter crit");
}
void exit_crit(Sem *s) {
struct sembuf sb;
sb.sem_num = CRIT;
sb.sem_op = UP;
printf("[%d] exit crit\n", getpid());
if (semop(s->semid, &sb, 1) == -1)
err("exit crit");
}
void acquire_lock(Sem *s) {
struct sembuf sb;
sb.sem_num = LOCK;
sb.sem_op = DOWN;
printf("[%d] acquire lock\n", getpid());
if (semop(s->semid, &sb, 1) == -1)
err("acquire lock");
}
void release_lock(Sem *s) {
struct sembuf sb;
sb.sem_num = LOCK;
sb.sem_op = UP;
printf("[%d] release lock\n", getpid());
if (semop(s->semid, &sb, 1) == -1)
err("release lock");
}
int sem_init(Sem *s, int n) {
key_t key;
Args arg;
int *data;
/* sem */
if ((key = ftok(PATH, SID)) == -1) {
return -1;
}
if ((s->semid = semget(key, SEMN, 0600 | IPC_CREAT)) == -1) {
return -1;
}
arg.val = 1;
if (semctl(s->semid, 0, SETVAL, arg) == -1) {
return -1;
}
if (semctl(s->semid, 1, SETVAL, arg) == -1) {
return -1;
}
/* mem */
if ((key = ftok(PATH, SHMID)) == -1) {
return -1;
}
if ((s->shmid = shmget(key, SHM_SIZE, 0664 | IPC_CREAT)) == -1) {
return -1;
}
data = shmat(s->shmid, (void *)0, 0);
if (data == (int *)(-1)) {
return -1;
}
*data = n;
if (shmdt(data) == -1) {
return -1;
}
return 0;
}
int sem_get(Sem *s) {
key_t key;
if ((key = ftok(PATH, SID)) == -1) {
return -1;
}
if ((s->semid = semget(key, SEMN, 0)) == -1) {
return -1;
}
if ((key = ftok(PATH, SHMID)) == -1) {
return -1;
}
if ((s->shmid = shmget(key, SHM_SIZE, 0)) == -1) {
return -1;
}
return 0;
}
int sem_up(Sem *s) {
int *data;
enter_crit(s);
data = get_n(s);
printf("[%d] read %d\n", getpid(), *data);
(*data)++;
if (*data == 1) {
printf("[%d] now is %d\n", getpid(), *data);
dettach_n(data);
exit_crit(s);
release_lock(s);
} else {
exit_crit(s);
}
return 0;
}
int sem_down(Sem *s) {
int *data;
enter_crit(s);
data = get_n(s);
printf("[%d] checked %d\n", getpid(), *data);
if (*data == 0) {
dettach_n(data);
exit_crit(s);
acquire_lock(s);
} else {
(*data)--;
dettach_n(data);
exit_crit(s);
}
return 0;
}
int sem_rm(Sem *s) {
if (semctl(s->semid, 0, IPC_RMID) == -1)
return -1;
if (shmctl(s->shmid, 0, IPC_RMID) == -1)
return -1;
return 0;
}
void child() {
pid_t pid;
Sem s;
pid = getpid();
printf("\x1b[31m[%d] hello!\033[0m\n", pid);
if (sem_get(&s) == -1) {
perror("sem_get");
exit(1);
}
sem_down(&s);
printf("\x1b[32m[%d] working...\033[0m\n", pid);
sleep(5);
printf("\x1b[32m[%d] finishing...\033[0m\n", pid);
sem_up(&s);
printf("\x1b[34m[%d] **child leaving**\033[0m\n", pid);
exit(0);
}
int main() {
Sem s;
int i;
if (sem_init(&s, SLOTS) == -1) {
perror("sem_init");
exit(1);
}
printf("[%d] parent\n", getpid());
for (i = 0; i < NPROC; i++) {
switch(fork()) {
case 0:
child();
case -1:
perror("fork");
exit(1);
}
}
printf("waiting for children...\n");
for (i = 0; i < NPROC; i++)
wait(NULL);
if (sem_rm(&s) == -1) {
perror("sem_rm");
exit(1);
}
printf("good bye!\n");
return 0;
}
The problem was in the up_sem function. It only does an up to the lock sem (release_lock func) if n was previously 0. This is incorrect since it might be several process waiting.
My solution was adding an extra shared memory counting the waiting processes and doing an up to the lock sem when that number is greater than 0.
So I've got this great code for a C example of a deadlock by Ali Hasan Ahmed Khan previously. I was just thinking about converting it to a parent-child process, so somehow making funcA the parent, then forking funcB. When I used the fork command, I was met with an error in main, saying funcB is non existent. Can this be converted? The full code is:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/sem.h>
#define PERMS 0660
int semId;
int initSem(int semId, int semNum, int initValue) {
return semctl(semId, semNum, SETVAL, initValue);
}
// Try to take a resource, wait if not available
int P(int semId, int semNum) {
struct sembuf operationList[1];
operationList[0].sem_num = semNum;
operationList[0].sem_op = -1;
operationList[0].sem_flg = 0;
return semop(semId, operationList, 1);
}
// Release a resource
int V(int semId, int semNum) {
struct sembuf operationList[1];
operationList[0].sem_num = semNum;
operationList[0].sem_op = 1;
operationList[0].sem_flg = 0;
return semop(semId, operationList, 1);
}
void* funcA(void* nothing) {
printf("Thread A try to lock 0...\n");
P(semId, 0); // Take resource/semaphore 0 of semID
printf("Thread A locked 0.\n");
usleep(50*1000); // Wait 50 ms
printf("Thread A try to lock 1...\n");
P(semId, 1); // Take resource/semaphore 1 of semID
printf("Thread A locked 1.\n");
V(semId, 0); // Release resource/semaphore 0 of semID
V(semId, 1); // Release resource/semaphore 1 of semID
return NULL;
}
void* funcB(void* nothing) {
printf("Thread B try to lock 1...\n");
P(semId, 1);
printf("Thread B locked 1.\n");
usleep(5*1000);
printf("Thread B try to lock 0...\n");
P(semId, 0);
printf("Thread B locked 0.\n");
V(semId, 0);
V(semId, 1);
return NULL;
}
int main(int argc, char* argv[]) {
int i;
// ftok generates a key based on the program name and a char value
semId = semget(ftok(argv[0], 'A'), 2, IPC_CREAT | PERMS);
initSem(semId, 0, 1);
initSem(semId, 1, 1);
pthread_t thread[2];
pthread_create(&thread[0], NULL, funcA, NULL);
pthread_create(&thread[1], NULL, funcB, NULL);
for (i = 0 ; i < 2 ; i++) {
pthread_join(thread[i], NULL);
}
printf("This is not printed in case of deadlock\n");
semctl(semId, 0, IPC_RMID, 0);
semctl(semId, 1, IPC_RMID, 0);
return 0;
}
The program should create 200000 integers and write 2000 to a shared memory. A forked process should read 2000 from shared memory and the parent should write the next 2000 to shared memory.
if i use the code below without sleep, the parent first creates all 200000 integers and then the child reads the same integers from shared memory.
With sleep everything looks good, but we have to use semaphore.
shm.c (parent):
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <sys/wait.h>
#define N_DATA 200000
#define N_SHARED 2000
#define LOCK -1
#define UNLOCK 1
static struct sembuf semaphore;
char shmidArg[32];
char semidArg[32];
int *shmData;
int i, j;
int status;
char *strsignal(int sig);
pid_t pid;
static int shmid;
static int semid;
char *strsignal(int sig);
/** Semaphore Operation */
static int semaphore_operation (int op) {
semaphore.sem_num = 1;
semaphore.sem_op = op;
semaphore.sem_flg = IPC_NOWAIT;
if( semop (semid, &semaphore, 1) == -1) {
perror(" semop ");
exit (EXIT_FAILURE);
}
return 1;
}
int main(int argc, char **argv) {
/* Ein Shared-Memory-Segment einrichten */
shmid = shmget(IPC_PRIVATE, N_SHARED*sizeof(int), IPC_CREAT | SHM_R | SHM_W);
if (shmid == -1) {
perror("shmid");
exit(1);
}
printf("Shared-Memory-ID: %d\n",shmid);
/* Pointer zu Shared-Memory-Segment erhalten */
shmData = (int *)shmat(shmid,0, 0);
if (shmData == (int *)(-1)) {
perror("shmat");
exit(1);
}
/* Semaphore anlegen */
semid = semget(IPC_PRIVATE, 1, IPC_CREAT | SHM_R | SHM_W);
if (semid < 0) {
perror("semid");
exit(1);
}
printf ("Semaphor-ID : %d\n", semid);
/* Semaphor mit 1 initialisieren */
if (semctl (semid, 0, SETVAL, (int) 1) == -1) {
perror("semctl");
}
snprintf(shmidArg,32, "%d", shmid);
snprintf(semidArg,32, "%d", semid);
/** erstellen des Kindprozesses */
pid = fork();
// Kindprozess
if (pid == 0) {
execlp("./shm_child",shmidArg,semidArg,NULL);
} else if (pid < 0) {
perror("Kindprozess konnte nicht erzeugt werden!");
return 1;
}
// Elternprozess
else {
/** ininitalisieren des Zufallsgenerator durch aktuellen Zeitstempel */
srand48(time(NULL));
for(i=0;i<N_DATA;i=i+N_SHARED) {
semaphore_operation(LOCK);
for (j=0; j<N_SHARED; j++) {
shmData[j] = lrand48();
//MSZ
//printf("SHM-->%d-->%d\n",i+1,shmData[i]);
}
// if(i == 0 || i == 2000) {
printf("Parent-->%d-->0-->%d\n",i,shmData[0]);
printf("Parent-->%d-->1999->%d\n",i,shmData[1999]);
// }
semaphore_operation(UNLOCK);
//sleep(1);
}
}
//MSZ
//sleep(2);
printf("PID: %d\n", pid);
if (waitpid(pid, &status, 0) == -1) {
perror("wait konnte nicht erzeugt werden!");
return 1;
}
if (WIFEXITED(status)) {
printf("Exitcode: %d\n", WEXITSTATUS(status));
semctl (semid, 0, IPC_RMID, 0);
shmctl (shmid, IPC_RMID, NULL);
//If process terminaded by a signal
} else if (WIFSIGNALED(status)) {
printf("Signal: %d %s\n", WTERMSIG(status), strsignal(WTERMSIG(status)));
semctl (semid, 0, IPC_RMID, 0);
shmctl (shmid, IPC_RMID, NULL);
}
}
shm_child.c (Child):
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#define N_DATA 6000
#define N_SHARED 2000
#define LOCK -1
#define UNLOCK 1
int i,j;
int *shmData;
static int shmid;
static int semid;
static struct sembuf semaphore;
/** Semaphore Operation */
static int semaphore_operation (int op) {
semaphore.sem_num = 0;
semaphore.sem_op = op;
semaphore.sem_flg = SEM_UNDO;
if( semop (semid, &semaphore, 1) == -1) {
perror(" semop ");
exit (EXIT_FAILURE);
}
return 1;
}
int main(int argc, char **argv) {
shmid = atoi(argv[0]);
semid = atoi(argv[1]);
printf("\nshm_child shared memoryid:%d\n",shmid);
printf("shm_child Semaphoren-ID:%d\n",semid);
/* Pointer auf Shared-Memory erstellen */
shmData = (int *)shmat(shmid,0,0);
if (shmData == (int *)(-1)) {
perror("shmat");
exit(1);
}
for(i=0;i<N_DATA;i=i+N_SHARED) {
semaphore_operation(LOCK);
for(j=0;j<N_SHARED;j++) {
//printf("%d-->%d --> %d\n",i,j+1,shmData[j]);
}
// if(i == 0 || i == 2000) {
printf("child-->%d-->0-->%d\n",i,shmData[0]);
printf("child-->%d-->1999->%d\n",i,shmData[1999]);
// }
semaphore_operation(UNLOCK);
sleep(1);
}
return 0;
}
Please help us
Thank you guys
Edit: Thank you very much for your answers. I can't mark the right answer because i dont know what its right. But i dont want try anything more. 15 hours are enough
The writer process shall give reader a permission to read, and wait for the reading completion. After that the reader shall give writer a permission to proceed, and wait for writing completion.
This goal cannot be achieved with a single semaphore. You need two, along the lines of:
// parent aka writer
writer_barrier = semaphore(UNLOCKED);
reader_barrier = semaphore(LOCKED);
start_reader();
while(...) {
lock(writer_barrier);
write_data();
unlock(reader_barrier);
}
// child aka reader
while(....)
lock(reader_barrier);
read_data();
unlock(writer_barrier);
}
I have a program with two processes that communicate with shared memory. On ctrl-c I want both processes to exit. I'm using a atomic_bool variable called stop to inform the processes to keep looping or exit when set to true. However when the atomic_bool variable stop is set to true the other process does not see the change. Meaning it still prints out 0 instead of 1, but the process that made the change shows 1. So why doesn't the second process see the change from false to true?
Control-c won't work for killing the process so use killall instead.
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <stdbool.h>
#include <stdatomic.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <errno.h>
struct shared_map
{
atomic_bool stop;
};
struct shared_map *map;
int compare_and_swap_loop(atomic_bool target, int value)
{
/* Loop until we can succesfully update the the value. */
while(1)
{
/* Grab a snapshot of the value that need to be updated. */
bool snapshot = atomic_load(&target);
if(atomic_compare_exchange_weak(&target, &snapshot, value) == true)
{
/* We succesfully updated the value let's exit this loop and return. */
break;
}
}
printf("result: %d\n", atomic_load(&target));
return 0;
}
static void ctrlc_handler(int sig)
{
compare_and_swap_loop(&map->stop, true);
return;
}
void setup_signal_handler(void)
{
(void) signal(SIGINT, ctrlc_handler);
return;
}
static int create_shared(void **pointer, int size)
{
*pointer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
if(*pointer == MAP_FAILED)
{
printf("mmap: %s\n", strerror(errno));
return -1;
}
return 0;
}
static void loop(void)
{
/* Set up signal handler. */
setup_signal_handler();
/* Check if we should stop or continue running. */
while(atomic_load(&map->stop) == false)
{
sleep(2);
printf("map->stop: %d\n", atomic_load(&map->stop));
}
return;
}
int main(void)
{
int rtrn;
pid_t pid;
rtrn = create_shared((void **)&map, sizeof(struct shared_map));
if(rtrn < 0)
{
printf("Can't create shared memory\n");
return -1;
}
atomic_init(&map->stop, false);
pid = fork();
if(pid == 0)
{
loop();
_exit(0);
}
else if(pid > 0)
{
int status;
waitpid(pid, &status, 0);
return 0;
}
else
{
printf("fork: %s\n", strerror(errno));
return -1;
}
return 0;
}
You're passing in a copy of your atomic variable to your compare_and_swap_loop function, which is not going to do you any good - you need to work on the same value that's shared between your processes.
You need to do it like this:
int compare_and_swap_loop(atomic_bool *target, int value)
{
/* Loop until we can succesfully update the the value. */
while(1)
{
/* Grab a snapshot of the value that need to be updated. */
bool snapshot = atomic_load(target);
if(atomic_compare_exchange_weak(target, &snapshot, value) == true)
{
/* We succesfully updated the value let's exit this loop and return. */
break;
}
}
printf("result: %d\n", atomic_load(target));
return 0;
}
the following code is written without the 'atomic_*' commands
but does show what is incorrect about your process.
Mostly, need to kill the child pid, not the parent pid
The following code displays the child pid, so it is easy to find
#define _GNU_SOURCE
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <stdbool.h>
//#include <stdatomic.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <errno.h>
struct shared_map
{
bool stop;
};
struct shared_map *map;
int compare_and_swap_loop( bool *target )
{
/* Loop until we can succesfully update the the value. */
while(1)
{
/* Grab a snapshot of the value that need to be updated. */
bool snapshot = *target;
if(snapshot)
{
/* We succesfully updated the value let's exit this loop and return. */
break;
}
}
return 0;
}
static void ctrlc_handler(int sig)
{
if( SIGINT==sig)
compare_and_swap_loop(&map->stop);
return;
}
void setup_signal_handler(void)
{
(void) signal(SIGINT, ctrlc_handler);
return;
}
static int create_shared(void **pointer, size_t size)
{
*pointer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
if(*pointer == MAP_FAILED)
{
perror("mmap failed");
return -1;
}
return 0;
}
static void loop(void)
{
/* Set up signal handler. */
setup_signal_handler();
/* Check if we should stop or continue running. */
while(!map->stop)
{
sleep(2);
printf("map->stop: %d\n", map->stop);
}
return;
}
int main(void)
{
int rtrn;
pid_t pid;
printf( "entered Main\n");
rtrn = create_shared((void **)&map, sizeof(struct shared_map));
if(rtrn < 0)
{
printf("Can't create shared memory\n");
return -1;
}
map->stop = false;
pid = fork();
if(pid == 0)
{ //then child
printf( "child process\n");
loop();
_exit(0);
}
else if(pid > 0)
{ // then parent
int status;
printf( "parent process\n");
printf( "child Pid: %d\n", pid);
waitpid(pid, &status, 0);
return 0;
}
else
{ // else, fork failed
perror("fork failed");
return -1;
}
return 0;
}
I want to create n processes running in parallel and have them lock a mutex, increment a counter, and then unlock and exit.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <pthread.h>
pthread_mutex_t mutex;
int main(int argc, char **argv) {
if (argc != 2)
return 0;
int n = atoi(argv[1]);
int i = 0;
int status = 0;
pthread_mutex_init(&mutex, NULL);
pid_t pid = 1;
static int *x;
x = mmap(NULL, sizeof *x, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
*x = 0;
printf("Creating %d children\n", n);
for(i = 0; i < n; i++) {
if (pid != 0)
pid = fork();
}
if (pid == 0) {
pthread_mutex_lock(&mutex);
*x = *x + 1;
printf("[CHLD] PID: %d PPID: %d X: %d\n", getpid(), getppid(), *x);
pthread_mutex_unlock(&mutex);
exit(0);
}
wait(&status);
printf("[PRNT] PID: %d X: %d\n", getpid(), *x);
munmap(x, sizeof *x);
return 0;
}
./procs 10000 however does not return with x=10000
I think this is because the mutex isn't shared between the processes, but what's the correct way to share the mutex?
Here's a port of my example in the comment using pthread_mutex. First time I've done this, but seems to work:
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <stdbool.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <pthread.h>
typedef struct
{
bool done;
pthread_mutex_t mutex;
} shared_data;
static shared_data* data = NULL;
void initialise_shared()
{
// place our shared data in shared memory
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_SHARED | MAP_ANONYMOUS;
data = mmap(NULL, sizeof(shared_data), prot, flags, -1, 0);
assert(data);
data->done = false;
// initialise mutex so it works properly in shared memory
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&data->mutex, &attr);
}
void run_child()
{
while (true)
{
puts("child waiting. .. ");
usleep(500000);
pthread_mutex_lock(&data->mutex);
if (data->done) {
pthread_mutex_unlock(&data->mutex);
puts("got done!");
break;
}
pthread_mutex_unlock(&data->mutex);
}
puts("child exiting ..");
}
void run_parent(pid_t pid)
{
puts("parent sleeping ..");
sleep(2);
puts("setting done ..");
pthread_mutex_lock(&data->mutex);
data->done = true;
pthread_mutex_unlock(&data->mutex);
waitpid(pid, NULL, NULL);
puts("parent exiting ..");
}
int main(int argc, char** argv)
{
initialise_shared();
pid_t pid = fork();
if (!pid) {
run_child();
}
else {
run_parent(pid);
}
munmap(data, sizeof(data));
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>
typedef struct {
pthread_mutex_t mutex;
int data;
}MUTEX_N_DATA;
int main(int argc, char **argv) {
if (argc != 2)
return 0;
int n = atoi(argv[1]);
int i = 0;
bool wait = true;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pid_t pid = 1;
MUTEX_N_DATA *x;
x = mmap(NULL, sizeof(MUTEX_N_DATA), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
x->data = 0;
pthread_mutex_init(&x->mutex, &attr);
printf("Creating %d children\n", n);
for(i = 0; i < n; i++) {
if (pid != 0) {
pid = fork();
printf("Created Child %d \n",pid);
}
}
if (pid == 0) {
pthread_mutex_lock(&x->mutex);
x->data = x->data + 1;
printf("[CHLD] PID: %d PPID: %d X: %d\n", getpid(), getppid(), x->data);
pthread_mutex_unlock(&x->mutex);
exit(0);
}
while(wait){
pthread_mutex_lock(&x->mutex);
if(x->data == n){
wait = false;
}
pthread_mutex_unlock(&x->mutex);
usleep(100000);
}
printf("[PRNT] PID: %d X: %d\n", getpid(), x->data);
munmap(x, sizeof *x);
return 0;
}
The wait() waits only until one of the child exits. with following changes it works.
mutex attribute set to shared & moved to mmap memory.
parent waiting for the count to reach the number of childrens.
https://man7.org/linux/man-pages/man2/fork.2.html
https://man7.org/linux/man-pages/man2/wait.2.html