Below i increment a counter 1000 in two different processes and use a global semaphores for synchronisation. But iCounter doesn't give me 2000? Could someone explain to me why?
#include <stdio.h>
#include <semaphore.h>
#include <stdlib.h>
#include <fcntl.h>
#define SEM_NAME "/sync"
#define NUMBER_OF_PROCESSES 2
int main ()
{
int iCounter = 0;
pid_t pid[NUMBER_OF_PROCESSES];
sem_t *sSem = sem_open (SEM_NAME,O_CREAT | O_EXCL, S_IRWXU | S_IRWXG | S_IRWXO, 1);
int i, iStatus;
for (i = 0; i < NUMBER_OF_PROCESSES; i++)
{
pid[i] = fork ();
if (pid[i] < 0)
{
printf ("Could not create process\n");
exit (1);
}
else if (pid[i] == 0)
{
int i;
for (i = 0; i < 1000; i++)
{
sem_init(sSem, 0, 1);
sem_wait (sSem);
iCounter++;
sem_post (sSem);
}
exit (0);
}
}
for (i = 0; i < NUMBER_OF_PROCESSES; i++)
waitpid (pid[i], &iStatus, WUNTRACED);
printf ("Value of iCounter = %d\n", iCounter);
sem_close (sSem);
sem_unlink (SEM_NAME);
}
You have two big issues:
You're not using named semaphores correctly. sem_init() is only meant to be used on anonymous ones, and only once per semaphore, where you're calling it on an already initialized one many times. That's undefined behavior.
Your iCounter variable isn't shared between the processes you create.
The following reworking of your program allocates it using POSIX shared memory (And has some general cleanup):
#include <errno.h>
#include <fcntl.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define SEM_NAME "/sync"
#define SHM_NAME "/counter"
#define NUMBER_OF_PROCESSES 2
int main() {
int *iCounter = MAP_FAILED;
pid_t pid[NUMBER_OF_PROCESSES];
int iStatus;
int ret = 0;
int memfd = -1;
sem_t *sSem = sem_open(SEM_NAME, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 1);
if (sSem == SEM_FAILED) {
perror("sem_open");
ret = 1;
goto cleanup;
}
memfd = shm_open(SHM_NAME, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
if (memfd < 0) {
perror("shm_open");
ret = 1;
goto cleanup;
}
if (ftruncate(memfd, sizeof *iCounter) < 0) {
perror("ftruncate");
ret = 1;
goto cleanup;
}
iCounter = mmap(NULL, sizeof *iCounter, PROT_READ | PROT_WRITE, MAP_SHARED,
memfd, 0);
if (iCounter == MAP_FAILED) {
perror("mmap");
ret = 1;
goto cleanup;
}
close(memfd);
*iCounter = 0;
for (int i = 0; i < NUMBER_OF_PROCESSES; i++) {
pid[i] = fork();
if (pid[i] < 0) {
fprintf(stderr, "Could not create process: %s\n", strerror(errno));
ret = 1;
goto cleanup;
} else if (pid[i] == 0) {
for (i = 0; i < 1000; i++) {
sem_wait(sSem);
(*iCounter)++;
sem_post(sSem);
}
sem_close(sSem);
return 0;
}
}
for (int i = 0; i < NUMBER_OF_PROCESSES; i++)
waitpid(pid[i], &iStatus, WUNTRACED);
printf("Value of iCounter = %d\n", *iCounter);
cleanup:
if (sSem != SEM_FAILED) {
sem_close(sSem);
sem_unlink(SEM_NAME);
}
if (memfd >= 0)
shm_unlink(SHM_NAME);
return ret;
}
Example:
$ gcc -g -O -Wall -Wextra example.c -lrt -lpthread
$ ./a.out
Value of iCounter = 2000
Related
In fork child, if we modify a global variable, it will not get changed in the main program.
Is there a way to change a global variable in child fork?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int glob_var;
main (int ac, char **av)
{
int pid;
glob_var = 1;
if ((pid = fork()) == 0) {
/* child */
glob_var = 5;
}
else {
/* Error */
perror ("fork");
exit (1);
}
int status;
while (wait(&status) != pid) {
}
printf("%d\n",glob_var); // this will display 1 and not 5.
}
You can use shared memory (shm_open(), shm_unlink(), mmap(), etc.).
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
static int *glob_var;
int main(void)
{
glob_var = mmap(NULL, sizeof *glob_var, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
*glob_var = 1;
if (fork() == 0) {
*glob_var = 5;
exit(EXIT_SUCCESS);
} else {
wait(NULL);
printf("%d\n", *glob_var);
munmap(glob_var, sizeof *glob_var);
}
return 0;
}
Changing a global variable is not possible because the new created process (child)is having it's own address space.
So it's better to use shmget(),shmat() from POSIX api
Or You can use pthread , since pthreadsare sharing the globaldata and the changes in global variable is reflected in parent.
Then read some Pthreads tutorial.
Here is an alternative solution.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
typedef struct
{
int id;
size_t size;
} shm_t;
shm_t *shm_new(size_t size)
{
shm_t *shm = calloc(1, sizeof *shm);
shm->size = size;
if ((shm->id = shmget(IPC_PRIVATE, size, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR)) < 0)
{
perror("shmget");
free(shm);
return NULL;
}
return shm;
}
void shm_write(shm_t *shm, void *data)
{
void *shm_data;
if ((shm_data = shmat(shm->id, NULL, 0)) == (void *) -1)
{
perror("write");
return;
}
memcpy(shm_data, data, shm->size);
shmdt(shm_data);
}
void shm_read(void *data, shm_t *shm)
{
void *shm_data;
if ((shm_data = shmat(shm->id, NULL, 0)) == (void *) -1)
{
perror("read");
return;
}
memcpy(data, shm_data, shm->size);
shmdt(shm_data);
}
void shm_del(shm_t *shm)
{
shmctl(shm->id, IPC_RMID, 0);
free(shm);
}
int main()
{
int var = 1;
shm_t *shm = shm_new(sizeof var);
int pid;
if ((pid = fork()) == 0)
{ /* child */
var = 5;
shm_write(shm, &var);
printf("child: %d\n", var);
return 0;
}
/* Wait for child to return */
int status;
while (wait(&status) != pid);
/* */
shm_read(&var, shm);
/* Parent is updated by child */
printf("parent: %d\n", var);
shm_del(shm);
return 0;
}
Build with:
$ gcc shm.c -o shm && ./shm
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:
trying to create semaphore rutine between two processes but code seems to break too early. Tried to compare with other students who succeeded and our codes looks almost the same except that my i do fork only after semget and semctl.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdlib.h>
int main()
{
pid_t n;
int semid;
int key = 98757;
short sarray[1] = {2};
struct sembuf sops[1];
int err;
semid = semget(key, 1, IPC_CREAT | 0666);
if (semid == -1)
{
perror("semid failed");
return 0;
}
err = semctl(semid, 1, SETALL, sarray);
if (err == -1);/*code beakes down here*/
{
perror("semctl failed");
return 0;
}
n = fork();
if (n < 0)
{
printf("n < 0");
return 0;
}
else if (n==0)/*child*/
{
sops[0].sem_num = 0;
sops[0].sem_op = 0;
sops[0].sem_flg = 0;
err = semop(semid, sops, 1);
if (err < 0)
{
printf("debugging semid < 0");
return 0;
}
printf("%d", n);
exit(0);
}
else/*parent*/
{
sleep(3);
sops[0].sem_num = 0;
sops[0].sem_op = -1;
sops[0].sem_flg = 0;
err = semop(semid, sops, 1);
if (err < 0)
{
printf("debugging semid < 0");
return 0;
}
wait(0);
}
return 0;
}
When you test err after calling semctl, there is an error in the if statement. The cause is hard to see and obscured my a comment that sits too close to the code.
Here is the test without that comment:
if (err == -1);
{
perror("semctl failed");
return 0;
}
The semicolon after the condition makes this an if with an empty body: The check is performed, but nothing is done.
The code block in curly braces is entered unconditionally, regardless of the value of err. Such blocks are legal and sometimes useful to structure your code and to define tightly scoped variables.
TL;DR: Remove the semicolon:
if (err == -1) {
perror("semctl failed");
return 0;
}
I need to write a program that is creating a N amount of sub processes and every single one of them adds one to a shared memory variable. My idea is to use semaphores and shared memory, but the processes are not waiting for each other and the shared memory variable is also not working as I want it.
mydefs.h
#ifndef __MYDEFS__H__
#define __MYDEFS__H__
// Includes
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <memory.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/shm.h>
#endif // __MYDEFS__H__
main.c
#include "mydefs.h"
#define PROC_COUNT 3
#define INITAL_MARKER_VALUE 0
#define PID_LEN 32
char mypid[PID_LEN];
int main()
{
int i, shm_id;
sem_t mutex;
if(sem_init(&mutex,1,1) < 0)
{
perror("semaphore initilization");
exit(0);
}
shm_id = shmget(IPC_PRIVATE, 4*sizeof(int), IPC_CREAT | 0666);
if (shm_id < 0) {
printf("shmget error\n");
}
int *shmpointer = shmat(shm_id,0,0);
memset(mypid, 0, sizeof(mypid));
sprintf(mypid, "%06d", getpid());
for(i = 0; i < PROC_COUNT; i++)
{
if (fork() == 0)
{
while(sem_wait(&mutex)!=0);
execl("slaveproc", "slaveproc", mypid, (char *)0);
shmpointer += 1;
sem_post(&mutex);
perror("\n Can't exec slave program. Cause ");
exit(1);
}
}
sleep(1);
printf("%d\n", *shmpointer);
return 0;
}
slaveproc.c
#include "mydefs.h"
int marker; // Marker value
int main(int argc, char *argv[])
{
master_pid = atoi(argv[1]);
printf("\n --------------------------------------");
printf("\n I'm the slave proc!");
printf("\n My pid: %d", getpid());
printf("\n My master's pid: %d", master_pid);
printf("\n --------------------------------------");
for(;;) pause();
return 0;
}
The problem (or at least "a problem") is that mutex is not in shared memory: it's allocated on the stack. When you fork(), the new process will have a completely separate copy from the old process, so calling sem_wait(&mutex) on one process will not affect the other process's mutex at all.
You should put mutex in the shared memory:
int main()
{
int i, shm_id;
shm_id = shmget(IPC_PRIVATE, sizeof(sem_t) + 4*sizeof(int), IPC_CREAT | 0666);
if (shm_id < 0) {
printf("shmget error\n");
}
int *shmpointer = shmat(shm_id,0,0);
sem_t *mutex = shmpointer;
shmpointer = (void*)shmpointer + sizeof(sem_t);
if(sem_init(mutex,1,1) < 0)
{
perror("semaphore initilization");
exit(0);
}
memset(mypid, 0, sizeof(mypid));
sprintf(mypid, "%06d", getpid());
for(i = 0; i < PROC_COUNT; i++)
{
if (fork() == 0)
{
while(sem_wait(mutex)!=0);
execl("slaveproc", "slaveproc", mypid, (char *)0);
shmpointer += 1;
sem_post(mutex);
perror("\n Can't exec slave program. Cause ");
exit(1);
}
}
sleep(1);
printf("%d\n", *shmpointer);
return 0;
}
You're also never writing to the memory in shmpointer (perhaps you meant (*shmpointer) += 1?), but I'll let you figure that out on your own.
we had an exam today and we had a task to implement a "train-handler".
There are 7 trains represented by one process each. Each train arrives after a couple of seconds, checks if 1 of our 3 traintracks is available. If not, wait...
If track is free, enter it and lock it.
Stay for at the train station for a few seconds, leave and unlock it.
Me and a few friends are trying to make our program run but we just can't get it done. It seems to be the problem that our shared memory is not synchronized properly (semaphore). Using a mac, so I have to use named semaphores.
compiled with: "gcc -Wall -Werror -std=gnu99 -lpthread process_trains.c -o test"
CODE:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <errno.h>
sem_t *sem;
int *shm_ptr;
int *initShm (int size) {
int shm_fd = 0;
if((shm_fd = shm_open("/shm", O_CREAT | O_RDWR, 0777)) == -1) {
perror("Error creating shared memory segment!");
}
if ((ftruncate(shm_fd, size)) == -1) {
perror("Error sizing shared memory segment!");
}
return (int*) mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
}
int trainAboutToArrive(int arrive, int stay, int Y){
int Z=0;
//Zug kommt in "arrive" Sekunden an
sleep(arrive);
while (shm_ptr[Z]!=0) {
Z++;
if(Z==3){
Z=0;
}
}
sem_wait(sem);
shm_ptr[Z]=1;
sem_post(sem);
printf("Zug %d ist auf Gleis %d eingefahren\n", Y, 1+Z);
//Zug hat einen Aufenthalt von "stay" Sekunden
sleep(stay);
sem_wait(sem);
shm_ptr[Z]=0;
sem_post(sem);
sem_close(sem);
printf("Zug %d verlässt Gleis %d\n", Y, 1+Z);
return EXIT_SUCCESS;
}
int main(int argc, char const *argv[]) {
shm_unlink("shm");
int i=0, tracks=3, trains=7, status;
int arrival[]={0,0,3,2,5,4,2};
int stay[]={2,3,7,2,1,4,3};
off_t size = sizeof(int)*tracks;
shm_ptr = initShm(size);
if((sem = sem_open("/semap",O_CREAT,0644,1)) == SEM_FAILED) {
perror("client sem_open");
}
for (i=0; i < tracks; i++) {
shm_ptr[i]= 0;
}
pid_t pids[trains];
for (i = 0; i < trains; i++) {
pids[i] = fork();
if(pids[i] == -1) {
perror("Error creating train-process!!");
} else if (pids[i] == 0) {
trainAboutToArrive(arrival[i], stay[i], 1+i);
exit(0);
}else if (pids[i] > 0) {
}
}
for(i=0; i < trains; i++){
waitpid(pids[i], &status, 0);
}
shm_unlink("shm");
return EXIT_SUCCESS;
}
Link with -pthread!!!! man page of all used semaphore functions tells us >.<
Thanks for everyones help!!
And for everyone who's interested, this is my code now. I improved a lot of things I didn't have time for in the exam. This runs perfectly and in my "beginners-eyes" this is not improvable by using the given functions (semaphores, shared mem...). If it is, I'd be grateful for tips & tricks ;)
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <errno.h>
int *shm_ptr;
int *initShm (off_t size) {
int shm_fd = 0;
if((shm_fd = shm_open("/shm", O_CREAT | O_RDWR, 0777)) == -1) {
perror("Error creating shared memory segment!");
}
if ((ftruncate(shm_fd, size)) == -1) {
perror("Error sizing shared memory segment!");
}
return (int*) mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
}
void initSem(sem_t **plats) {
if((plats[0] = sem_open("/one",O_CREAT,0644,1)) == SEM_FAILED) {
perror("client sem_open");
}
if((plats[1] = sem_open("/two",O_CREAT,0644,1)) == SEM_FAILED) {
perror("client sem_open");
}
if((plats[2] = sem_open("/three",O_CREAT,0644,1)) == SEM_FAILED) {
perror("client sem_open");
}
}
int trainAboutToArrive(int arrive, int stay, int train, sem_t **plats){
srand(getpid());
int platform = rand()%3;
sleep(arrive);
while (3) {
sem_wait(plats[platform]);
if(shm_ptr[platform]==0){
shm_ptr[platform]=1;
break;
}
sem_post(plats[platform]);
platform = rand() % 3;
}
printf("Train %d enters platform %d\n", train, 1+platform);
sleep(stay);
shm_ptr[platform]=0;
printf("Train %d leaves platform %d\n", train, 1+platform);
sem_post(plats[platform]);
sem_close(plats[platform]);
return EXIT_SUCCESS;
}
int main(int argc, char const *argv[]) {
shm_unlink("/shm");
sem_unlink("/one");
sem_unlink("/two");
sem_unlink("/three");
int i=0, tracks=3, trains=7, status;
int arrival[]={0,0,3,2,5,4,2};
int stay[]={2,3,7,2,1,4,3};
sem_t *plats[3];
off_t size = sizeof(int)*tracks;
shm_ptr = initShm(size);
initSem(plats);
for (i=0; i < tracks; i++) {
shm_ptr[i]= 0;
}
pid_t pids[trains];
for (i = 0; i < trains; i++) {
pids[i] = fork();
if(pids[i] == -1) {
perror("Error creating train-process!!");
} else if (pids[i] == 0) {
trainAboutToArrive(arrival[i], stay[i], 1+i, plats);
exit(0);
}else if (pids[i] > 0) {
}
}
for(i=0; i < trains; i++){
waitpid(pids[i], &status, 0);
}
shm_unlink("/shm");
sem_unlink("/one");
sem_unlink("/two");
sem_unlink("/three");
return EXIT_SUCCESS;
}