โดยคำสั่ง down (หรือ wait()) นั้นจะทำการลดค่าที่ละหนึ่ง และคำสั่ง up (หรือ signal) ก็จะเป็นการเพิ่มค่าที่ละหนึ่งเช่นเดียวกัน เมื่อโปรเซสใดก็ตามที่เรียกคำสั่ง wait() แล้วค่า S ณ ตอนนั้นมีค่าน้อยกว่าหรือเท่ากับศูนย์ โปรเซสเหล่านั้นก็จะต้องถูกบล็อคให้รอจนกว่าโปรเซสที่เข้าในเขตวิกฤตจะออกมาแล้วทำการเพิ่มค่า S โดยเรียกคำสั่ง signal()
เพื่อให้เข้าใจได้ง่ายขึ้นเกี่ยวกับ semaphore สามารถเปรียบเทียบได้กับตะกร้าที่มีลูกบอล (S) จำนวนหนึ่งอยู่ โดยเมื่อมีโปรเซสใดต้องการขอเข้าถึงข้อมูลส่วนกลางในเขตวิกฤตจะต้องหยิบลูกบอลหนึ่งลูกจากตะกร้าโดยเมื่อใดที่โปรเซสนั้นเข้าถึงข้อมูลเสร็จสิ้นแล้ว ก็จะออกมาจากเขตวิกฤติแล้วคืนลูกบอลนั้นกลับไปในตะกร้าเช่นเดิม
ระบบที่รองรับ semaphore นั้นมีทั้ง System V และ POSIX ซึ่ง POSIX semaphore นั้นถือว่าเป็นตัวที่พัฒนาตามหลังจาก System V semaphore ที่มีอยู่กับระบบปฏิบัติการ UNIX มานาน รวมทั้งอาจจะมีความยุ่งยากในการเรียกใช้งาน ทำให้ POSIX semaphore ได้ถูกพัฒนาให้ดีขึ้นและแก้ไขจุดเสียของ System V semaphore จนทำให้มีขนาดเล็กกระทัดรัด การรียกใช้งานง่าย และมีความทันสมัยรองรับสถาปัตยกรรมใหม่ๆได้ดีกว่ามาก
// sem2.c
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
void* doSomething1();
void* doSomething2();
void* doSomething3();
sem_t sem;
int main() {
// initialize semaphore to 2
sem_init(&sem, 1, 2);
pthread_t thread1, thread2, thread3;
pthread_create(&thread1, NULL, &doSomething1, NULL);
pthread_create(&thread2, NULL, &doSomething2, NULL);
pthread_create(&thread3, NULL, &doSomething3, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
pthread_join(thread3, NULL);
return 0;
}
void doSomething(char c) {
int i, time;
for (i = 0; i < 3; i++) {
// P operation
if (sem_wait(&sem) == 0) {
// generate random amount of time (< 30 seconds)
time = (int) ((double) rand() / RAND_MAX * 30);
printf("Thread %c enters and sleeps for %d seconds...\n", c, time);
sleep(time);
printf("Thread %c leaves the critical section\n", c);
// V operation
sem_post(&sem);
}
}
}
void* doSomething1() {
// thread A
doSomething('A');
return 0;
}
void* doSomething2() {
// thread B
doSomething('B');
return 0;
}
void* doSomething3() {
// thread C
doSomething('C');
return 0;
}
$ gcc -o sem2 sem2.c -lpthread -lrt
$ ./sem2
Thread C enters and sleeps for 25 seconds...
Thread B enters and sleeps for 11 seconds...
Thread B leaves the critical section
Thread B enters and sleeps for 23 seconds...
Thread C leaves the critical section
Thread C enters and sleeps for 23 seconds...
Thread B leaves the critical section
Thread B enters and sleeps for 27 seconds...
Thread C leaves the critical section
Thread C enters and sleeps for 5 seconds...
Thread C leaves the critical section
Thread A enters and sleeps for 10 seconds...
Thread B leaves the critical section
Thread A leaves the critical section
Thread A enters and sleeps for 23 seconds...
Thread A leaves the critical section
Thread A enters and sleeps for 8 seconds...
Thread A leaves the critical section
// semex.c
/* A program derived from Robbins and Robbins ch12, ch14, and ch15 examples */
/* See http://vip.cs.utsa.edu/usp/programs.html and R&R book */
/* Shows use of POSIX semaphores to provide mutex between child processes */
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/stat.h>
/* Without use of shared memory, the semaphore doesn't work across
processes! Of course it would work with threads in one process,
since they share the process address space. */
/* uncomment the next line to start using shared memory for sem_t object */
/*#define USE_SHARED_MEMORY */
#define MAXPIDS 10
void worker(int i, void *args);
int main(int argc, char *argv[]) {
int error;
int i;
int id, n;
sem_t *semlock;
pid_t pids[MAXPIDS];
if (argc != 2) { /* check for valid number of command-line arguments */
fprintf(stderr, "Usage: %s numprocesses\n", argv[0]);
return 1;
}
n = atoi(argv[1]);
if (n > MAXPIDS)
return 1;
#ifdef USE_SHARED_MEMORY
if ((id = shmget(IPC_PRIVATE, sizeof(sem_t), (S_IRUSR|S_IWUSR))) == -1) {
perror("Failed to create shared memory segment");
return 1;
}
if ((semlock = (sem_t *)shmat(id, NULL, 0)) == (void *)-1) {
perror("Failed to attach memory segment");
return 1;
}
#else
/* try using ordinary process-private memory for sem_t variable */
semlock = (sem_t*) malloc(sizeof(sem_t));
#endif
if (sem_init(semlock, 1/*shared across processes*/, 1) == -1) {
perror("Failed to initialize semaphore");
} else {
for (i = 0; i < n; i++) {
if ((pids[i] = fork()) < 0) {
fprintf(stderr, "Failed to create process:%s\n",
strerror(error));
return 1;
}
if (pids[i] == 0) { /* child */
worker(i, semlock);
exit(0);
}
}
/* here in parent of all */
for (i = 0; i < n; i++)
wait(0);
fprintf(stderr, "workers all done\n");
}
#ifdef USE_SHARED_MEMORY
if (shmdt((void *)semlock) == -1) { /* shared memory detach */
perror("Failed to destroy shared memory segment");
return 1;
}
#else
free(semlock);
#endif
return 0;
}
// semexworker.c
#include <errno.h>
#include <semaphore.h>
#include <stdio.h>
#include <unistd.h>
#define TEN_MILLION 10000000L
#define BUFSIZE 1024
void worker(int i, void *args) {
char buffer[BUFSIZE];
char *c;
sem_t *semlockp;
struct timespec sleeptime;
semlockp = (sem_t*) args;
sleeptime.tv_sec = 0;
sleeptime.tv_nsec = TEN_MILLION;
snprintf(buffer, BUFSIZE, "This is process %ld\n", (long) getpid());
c = buffer;
setbuf(stderr, NULL); // specify no buffering for stderr
/****************** entry section *******************************/
while (sem_wait(semlockp) == -1) /* Entry section */
if (errno != EINTR) {
fprintf(stderr, "Thread failed to lock semaphore\n");
return;
}
/****************** start of critical section *******************/
while (*c != '\0') {
fputc(*c, stderr); // no buffering: output each synchronously
c++;
nanosleep(&sleeptime, NULL);
}
/****************** exit section ********************************/
if (sem_post(semlockp) == -1) /* Exit section */
fprintf(stderr, "Thread failed to unlock semaphore\n");
/****************** remainder section ***************************/
return;
}
$ gcc -o semex semex.c semexworker.c -lrt -lpthread -DUSE_SHARED_MEMORY
$ ./semex 4
This is process 5595
This is process 5594
This is process 5593
This is process 5592
workers all done