Incorrect process cleaning cycle:
while( waitpid( pid, &return_stat, 0 ) == 0 );
Since waitpid() returns the PID it is reporting to, this is not the cycle you want - it only waits for one PID to die. This may be what you need:
enum { debug = 1 }; int corpse; while ((corpse = waitpid((pid_t)-1. &return_stat, 0)) > 0) { if (debug) printf("PID %d died 0x%.4X\n", corpse, return_stat); }
You can set debug = 0 when you are sure that it is working correctly.
View Output Example
Another problem is the child code:
if( pid == 0 ) { // critical part sema.sem_op = LOCK; if( semop( sem_id, &sema, 1 ) < 0 ) ++( *ptr ); sema.sem_op = UNLOCK; if( semop( sem_id, &sema, 1 ) < 0 ) // end of the critical part exit( EXIT_SUCCESS ); }
You increase the pointer only if the first semop() fails; you exit (successfully) only if the second semop() .
You must exit unconditionally. You should only increment if the first semop() succeeds, and you should only do the second semop() if the first is executed. You will probably need the error message code after the if .
Another version
The residual problem that I see is that you have inverted the LOCK and UNLOCK values.
#define _XOPEN_SOURCE #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ipc.h> #include <sys/sem.h> #include <sys/shm.h> #include <sys/wait.h> #include <unistd.h> #define KEY 1000 #define LOCK +1 #define UNLOCK -1 static const char *arg0 = 0; static void err_setarg0(char *argv0) { arg0 = argv0; } static void err_syserr(const char *msg) { int errnum = errno; fprintf(stderr, "%s: %s", arg0, msg); if (errnum != 0) fprintf(stderr, " (%d: %s)", errnum, strerror(errnum)); fputc('\n', stderr); exit(EXIT_FAILURE); } int main(int argc, char **argv) { int *ptr; int pid; int shm_id; int sem_id; struct sembuf sema; err_setarg0(argv[argc-argc]); if ((sem_id = semget(KEY, 1, IPC_CREAT | 0666)) < 0) err_syserr("semget()"); sema.sem_num = 0; sema.sem_op = 0; sema.sem_flg = SEM_UNDO; if ((shm_id = shmget(KEY, 1, IPC_CREAT | 0666)) < 0) err_syserr("shmget()"); ptr = shmat(shm_id, NULL, 0); if (ptr == (int *)-1) err_syserr("shmat()"); *ptr = 0; printf("Looping\n"); for (int i = 0; i < 10; ++i) { pid = fork(); if (pid < 0) err_syserr("fork()"); else if (pid == 0) { // critical part sema.sem_op = LOCK; if (semop(sem_id, &sema, 1) < 0) err_syserr("semop() lock"); ++(*ptr); sema.sem_op = UNLOCK; if (semop(sem_id, &sema, 1) < 0) err_syserr("semop() unlock"); // end of the critical part exit(EXIT_SUCCESS); } } printf("Looped\n"); int return_stat; enum { debug = 1 }; int corpse; while ((corpse = waitpid((pid_t)-1, &return_stat, 0)) > 0) { if (debug) printf("PID %d died 0x%.4X\n", corpse, return_stat); } printf("value %d\n", *ptr); if (shmdt(ptr) == -1) err_syserr("shmdt()"); if (semctl(sem_id, 1, IPC_RMID, 0) == -1) err_syserr("semctl()"); if (shmctl(shm_id, IPC_RMID, 0) == -1) err_syserr("shmctl()"); return 0; }
Execution Example:
$ ./semop Looping Looped PID 17976 died 0x0000 PID 17977 died 0x0000 PID 17978 died 0x0000 PID 17979 died 0x0000 PID 17980 died 0x0000 PID 17981 died 0x0000 PID 17982 died 0x0000 PID 17983 died 0x0000 PID 17984 died 0x0000 PID 17985 died 0x0000 value 10 $
Note the use of err_syserr() to simplify error reporting. Along with err_setarg0() it is part of a large package of error reporting functions that I use regularly. In fact, my normal version is a printf like function with a format string and a list of variable arguments, but this simple version is suitable for this program and simpler.