Semaphores
/*
* % g++ semaphore.cc -o semaphore -lpthread
*/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/sem.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#define N 10
#define LOOP 5
/* A global variable protected by the mutex above */
int counter = 0;
/* A semaphore set id */
int semid;
/*
* Simulate mutex init: allocate a semaphore
*/
void my_mutex_init(void) {
struct sembuf op;
/*
* Quoted from the man page: The name choice IPC_PRIVATE was perhaps
* unfortunate, IPC_NEW would more clearly show its function.
* Alternatively, instead of IPC_PRIVATE, you can pick one yourselves
* as long as it's unique system-wise.
*
* 2nd paramter could be > 1, in that case, you could get a set/group
* of semaphores.
*
* 0600 means rw permission from its own process.
*/
if ((semid = semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)) < 0) {
fprintf(stderr, "Failed to allocate a semop: %s\n", strerror(errno));
exit(1);
}
// Up the semaphore by 1
op.sem_num = 0; // Could be 1, 2, ... if you have more semaphores
op.sem_op = +1;
op.sem_flg = 0;
if (semop(semid, &op, 1) < 0) {
fprintf(stderr, "Failed to set initial value to a semop %d: %s\n",
semid, strerror(errno));
exit(1);
}
}
/*
* Simulate mutex lock: decrement the semaphore by 1
*/
void my_mutex_lock(void) {
struct sembuf op;
op.sem_num = 0;
op.sem_op = -1;
op.sem_flg = 0;
if (semop(semid, &op, 1) < 0) {
fprintf(stderr, "Failed to decrement semop %d: %s\n",
semid, strerror(errno));
exit(1);
}
}
/*
* Simulate mutex unlock: increment the semaphore by 1
*/
void my_mutex_unlock(void) {
struct sembuf op;
op.sem_num = 0;
op.sem_op = +1;
op.sem_flg = 0;
if (semop(semid, &op, 1) < 0) {
fprintf(stderr, "Failed to increment semop %d: %s\n",
semid, strerror(errno));
exit(1);
}
}
/*
* Simulate mutex destroy: destroy the semaphore
* VERY IMPORTANT: without that, semaphore resource is not freed
*/
void my_mutex_destroy() {
if (semctl(semid, 0, IPC_RMID) < 0) {
fprintf(stderr, "Failed to destroy semop %d: %s\n",
semid, strerror(errno));
exit(1);
}
}
/*
* A callback function assigned to a thread via pthread_create()
*/
void *client(void *p) {
int i, old_i, new_i;
for (i = 0; i < LOOP; i++) {
usleep(rand() % 1000);
my_mutex_lock();
old_i = counter;
new_i = ++counter;
my_mutex_unlock();
printf("Thread %d (%x): changed counter from %d to %d\n",
(int)p, pthread_self(), old_i, new_i);
fflush(stdout);
}
return (NULL);
}
int main(void) {
int i;
pthread_t tid[N];
my_mutex_init();
for (i = 0; i < N; i++) {
/* Create threads and assign client as callback functions */
if (pthread_create(&tid[i], NULL, client, (void *)i)) {
fprintf(stderr, "Failed to create thread: %s\n", strerror(errno));
// Continue
}
}
/* Wait for threads to exit */
for (i = 0; i < N; i++) {
pthread_join(tid[i], NULL);
}
my_mutex_destroy();
return (0);
}
Shared Memory
#include <stdio.h>
#include <sys/shm.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
int main(void) {
int shmid; // A shared memory id
int *ptr;
// Allocate a chunk/segment of shared memory
if ((shmid = shmget(IPC_PRIVATE, sizeof(int), 0600 | IPC_CREAT)) < 0) {
fprintf(stderr, "Failed to get a chunk of shared memory: %s\n",
strerror(errno));
return (1);
}
// Map the shared memory to a variable
ptr = (int *)shmat(shmid, NULL, 0);
if ((int)ptr == -1) {
fprintf(stderr, "Failed to map shared memory: %s\n", strerror(errno));
return (1);
}
switch (fork()) {
case -1:
fprintf(stderr, "Failed to call fork(): %s\n", strerror(errno));
break;
case 0:
// Child
// Change memory in child process should also change memory in parent's
// since they shared the same memory.
*ptr = 1234;
break;
default:
// Parent
if (wait(NULL) < 0) {
fprintf(stderr, "Failed to call wait(): %s\n", strerror(errno));
}
// Should be 1234, same as child process!
printf("ptr = %d\n", *ptr);
// Make sure only one process destroy the shared memory after use.
// VERY INPORTANT to destroy the shared memory you are no longer used
if (shmctl(shmid, IPC_RMID, NULL) < 0) {
fprintf(stderr, "Failed to destroy shared memory: %s\n",
strerror(errno));
}
break;
}
return (0);
}