Unix semaphore with the C programming language, tested under Debian
/** | |
* Compile this file as "sem_client" for the pair of programs to work properly | |
* (view the info on sem_server.c on how to run this) | |
* | |
* gcc -o sem_client sem_client.c -Wall -Werror | |
* | |
* This is needed because the server does an `exec` of this program | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <sys/sem.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include "common.h" | |
int main(int argc, char *argv[]) { | |
int sem_fd; | |
key_t sem_key; | |
int sem_id; | |
int i; | |
struct sembuf sop; | |
// Recover the sem_key from file | |
sem_fd = open(SEM_KEY_FILE, O_RDONLY); | |
if (sem_fd < 0) { | |
perror("Could not open sem key for reading"); | |
exit(1); | |
} | |
// Technically speaking, the read could read less than sizeof(key_t) | |
// Which would be wrong. | |
// But in our case, it is not likely to happen... | |
if (read(sem_fd, &sem_key, sizeof(key_t)) != sizeof(key_t)) { | |
perror("Error reading the semaphore key"); | |
exit(2); | |
} | |
// Done getting the semaphore key | |
close(sem_fd); | |
// Now obtain the (hopefully) existing sem | |
sem_id = semget(sem_key, 0, 0); | |
if (sem_id < 0) { | |
perror("Could not obtain semaphore"); | |
exit(3); | |
} | |
for (i = 0; i < 5; ++i) { | |
sop.sem_num = 0; | |
sop.sem_op = -1; | |
sop.sem_flg = SEM_UNDO; | |
printf("Client #%d waiting\n", getpid()); | |
semop(sem_id, &sop, 1); | |
printf("Client #%d acquired. Sleeping\n", getpid()); | |
sleep(1); | |
printf("Client #%d releasing\n", getpid()); | |
sop.sem_op = 1; | |
semop(sem_id, &sop, 1); | |
} | |
exit(0); | |
} |
/** | |
* Run these files: | |
* | |
* $ gcc -o sem_server sem_server.c | |
* $ gcc -o sem_client sem_client.c | |
* $ ./sem_server | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <sys/ipc.h> | |
#include <fcntl.h> | |
#include <sys/sem.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <sys/wait.h> | |
#include <sys/types.h> | |
#include <errno.h> | |
#include "common.h" | |
#define CLIENT_PATH_BUFSIZE 255 | |
int main(int argc, char *argv[]) { | |
key_t sem_key; | |
int sem_id; | |
int sem_fd; | |
char client_exe[CLIENT_PATH_BUFSIZE]; | |
int dir_len; | |
int i; | |
struct sembuf sop; | |
int pid; | |
int status; | |
sem_key = ftok("./sem_server.c", 42); | |
// Write the key to a file for children to pick it up | |
sem_fd = open(SEM_KEY_FILE, O_WRONLY | O_TRUNC | O_EXCL | O_CREAT, 0644); | |
if (sem_fd < 0) { | |
perror("Could not open sem.key"); | |
exit(1); | |
} | |
// Actual write of the key | |
if (write(sem_fd, &sem_key, sizeof(key_t)) < 0) { | |
perror("Could not write key to file"); | |
exit(2); | |
} | |
// Done with the key | |
close(sem_fd); | |
// Create the semaphore | |
sem_id = semget(sem_key, 1, IPC_CREAT | IPC_EXCL | 0600); | |
if (sem_id < 0) { | |
perror("Could not create sem"); | |
unlink(SEM_KEY_FILE); | |
exit(3); | |
} | |
if (semctl(sem_id, 0, SETVAL, 0) < 0) { | |
perror("Could not set value of semaphore"); | |
exit(4); | |
} | |
// Now create some clients | |
// First create the path to the client exec | |
getcwd(client_exe, CLIENT_PATH_BUFSIZE); | |
dir_len = strlen(client_exe); | |
strcpy(client_exe + dir_len, "/sem_client"); | |
printf("%s\n", client_exe); | |
for (i = 0; i < 5; ++i) { | |
if ((pid = fork()) < 0) { | |
perror("Could not fork, please create clients manually"); | |
} | |
else if (pid == 0) { | |
// We're in the child process, start a client | |
execl(client_exe, "sem_client", (char*)0); | |
_exit(127); | |
} | |
} | |
printf("Done creating clients, sleeping for a while\n"); | |
sleep(5); | |
printf("Increasing sem count\n"); | |
sop.sem_num = 0; | |
sop.sem_op = 1; | |
sop.sem_flg = 0; | |
if (semop(sem_id, &sop, 1)) { | |
perror("Could not increment semaphore"); | |
exit(5); | |
} | |
// Wait for all children to finish | |
for (;;) { | |
// Remove the zombie process, and get the pid and return code | |
pid = wait(&status); | |
if (pid < 0) { | |
if (errno == ECHILD) { | |
printf("All children have exited\n"); | |
break; | |
} | |
else { | |
perror("Could not wait"); | |
} | |
} | |
else { | |
printf("Child %d exited with status %d\n", pid, status); | |
} | |
} | |
// Delete semaphore and file | |
if (unlink(SEM_KEY_FILE) < 0) { | |
perror("Could not unlink key file"); | |
} | |
if (semctl(sem_id, 0, IPC_RMID) < 0) { | |
perror("Could not delete semaphore"); | |
} | |
exit(0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment