public
Last active

Unix semaphore with the C programming language, tested under Debian

  • Download Gist
common.h
C
1 2 3 4 5 6
#ifndef _COMMON_H_
#define _COMMON_H_
 
#define SEM_KEY_FILE ("sem.key")
 
#endif /* _COMMON_H_ */
sem_client.c
C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
/**
* 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);
}
sem_server.c
C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
/**
* 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);
}

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.