โดยคำสั่ง 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 จนทำให้มีขนาดเล็กกระทัดรัด การรียกใช้งานง่าย และมีความทันสมัยรองรับสถาปัตยกรรมใหม่ๆได้ดีกว่ามาก
// sem1.c#include<unistd.h>/* Symbolic Constants */#include<sys/types.h>/* Primitive System Data Types */#include<errno.h>/* Errors */#include<stdio.h>/* Input/Output */#include<stdlib.h>/* General Utilities */#include<pthread.h>/* POSIX Threads */#include<string.h>/* String handling */#include<semaphore.h>/* Semaphore *//* prototype for thread routine */voidhandler(void*ptr);/* global vars *//* semaphores are declared global so they can be accessed in main() and in thread routine, here, the semaphore is used as a mutex */sem_t mutex;int counter; /* shared variable */intmain() {int i[2];pthread_t thread_a;pthread_t thread_b; i[0] =0; /* argument to threads */ i[1] =1;sem_init(&mutex,0,1); /* initialize mutex to 1 - binary semaphore */ /* second param = 0 - semaphore is local */ /* Note: you can check if thread has been successfully created by checking return value of pthread_create */pthread_create(&thread_a,NULL, (void*) &handler, (void*) &i[0]);pthread_create(&thread_b,NULL, (void*) &handler, (void*) &i[1]);pthread_join(thread_a,NULL);pthread_join(thread_b,NULL);sem_destroy(&mutex); /* destroy semaphore */ /* exit */exit(0);} /* main() */voidhandler(void*ptr) {int x; x =*((int*) ptr);printf("Thread %d: Waiting to enter critical region...\n", x);sem_wait(&mutex); /* down semaphore */ /* START CRITICAL REGION */printf("Thread %d: Now in critical region...\n", x);printf("Thread %d: Counter Value: %d\n", x, counter);printf("Thread %d: Incrementing Counter...\n", x); counter++;printf("Thread %d: New Counter Value: %d\n", x, counter);printf("Thread %d: Exiting critical region...\n", x); /* END CRITICAL REGION */sem_post(&mutex); /* up semaphore */pthread_exit(0); /* exit thread */}
$ gcc -o sem1 sem1.c -lpthread
$ ./sem1
Thread 1: Waiting to enter critical region...
Thread 1: Now in critical region...
Thread 1: Counter Value: 0
Thread 1: Incrementing Counter...
Thread 1: New Counter Value: 1
Thread 1: Exiting critical region...
Thread 0: Waiting to enter critical region...
Thread 0: Now in critical region...
Thread 0: Counter Value: 1
Thread 0: Incrementing Counter...
Thread 0: New Counter Value: 2
Thread 0: Exiting critical region...
// 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;intmain() {// initialize semaphore to 2sem_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);return0;}voiddoSomething(char c) {int i, time;for (i =0; i <3; i++) {// P operationif (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 operationsem_post(&sem); } }}void*doSomething1() {// thread AdoSomething('A');return0;}void*doSomething2() {// thread BdoSomething('B');return0;}void*doSomething3() {// thread CdoSomething('C');return0;}
$ 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
// sem3.c#include<semaphore.h>#include<pthread.h>#include<errno.h>#defineNUM_BOOTHS3#defineNUM_THREADS8typedefstruct {pthread_t id;int thread_number;} thread_data;/* * The voting booth semaphore. */sem_t semaphore;voiddo_vote(void) {sleep(2); /* It takes time to vote. */}/* * The voter thread body. */void*voter_thread(void*p) { thread_data *td = p;struct timespec abs_time;abs_time.tv_sec =time(NULL)+3;abs_time.tv_nsec =0;printf("%d checking for a booth.\n",td->thread_number);if (0==sem_timedwait(&semaphore,&abs_time)) {printf("%d entering booth.\n",td->thread_number);do_vote();printf("%d done voting.\n",td->thread_number);sem_post(&semaphore); } else {printf("%d tired of waiting.\n",td->thread_number); }return0;}intmain(void) {int i; thread_data data[NUM_THREADS]; /* This function must always be called before using a semaphore. */sem_init(&semaphore,0, NUM_BOOTHS); /* Start the threads. */for (i =0; i < NUM_THREADS; i++) { data[i].thread_number = i;if (pthread_create(&data[i].id,0, voter_thread,&data[i])!=0) {printf("Cannot create thread: %d.\n", errno); } } /* Wait for the threads to finish. */for (i =0; i < NUM_THREADS; i++) {if (0!=pthread_join(data[i].id,0)) {printf("Cannot join thread %d: %d.\n", i, errno); } }printf("Done.\n");sem_destroy(&semaphore);sleep(1);return0;}
$ gcc -o sem3 sem3.c -lpthread
$ ./sem3
6 checking for a booth.
6 entering booth.
5 checking for a booth.
5 entering booth.
7 checking for a booth.
7 entering booth.
4 checking for a booth.
3 checking for a booth.
2 checking for a booth.
1 checking for a booth.
0 checking for a booth.
7 done voting.
5 done voting.
6 done voting.
4 entering booth.
3 entering booth.
2 entering booth.
1 tired of waiting.
0 tired of waiting.
4 done voting.
3 done voting.
2 done voting.
Done.
// 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 */#defineMAXPIDS10voidworker(int i,void*args);intmain(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]);return1; } n =atoi(argv[1]);if (n > MAXPIDS)return1;#ifdefUSE_SHARED_MEMORYif ((id =shmget(IPC_PRIVATE,sizeof(sem_t), (S_IRUSR|S_IWUSR))) ==-1) {perror("Failed to create shared memory segment");return1;}if ((semlock = (sem_t*)shmat(id,NULL,0)) == (void*)-1) {perror("Failed to attach memory segment");return1;}#else /* try using ordinary process-private memory for sem_t variable */ semlock = (sem_t*) malloc(sizeof(sem_t));#endifif (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));return1; }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"); }#ifdefUSE_SHARED_MEMORYif (shmdt((void*)semlock)==-1) { /* shared memory detach */perror("Failed to destroy shared memory segment");return1;}#elsefree(semlock);#endifreturn0;}
// semexworker.c#include<errno.h>#include<semaphore.h>#include<stdio.h>#include<unistd.h>#defineTEN_MILLION10000000L#defineBUFSIZE1024voidworker(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