Created
July 15, 2012 22:00
-
-
Save aspyct/3118858 to your computer and use it in GitHub Desktop.
Unix semaphore with the C programming language, tested under Debian
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _COMMON_H_ | |
#define _COMMON_H_ | |
#define SEM_KEY_FILE ("sem.key") | |
#endif /* _COMMON_H_ */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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