现代计算机系统中,多个进程可以并发执行,进程间必然存在共享资源和相互合作的问题。
同步主要是用户多个进程相互协作,共同完成任务,是进程间的直接制约问题;互斥则主要是为了多个进程分时使用有限的资源。
信号量(semaphore)是1965年由荷兰人Dijkstra提出的一种卓有成效的进程间同步及互斥工具。信号量在操作系统中实现时一般作为一个整数变量,这种信号量称为整型信号量。信号量S的物理意义:
S >= 0表示某资源的可用数;
S<0 其绝对值表示阻塞队列中等待该资源的进程数目;
信号量的两个操作方法是PV,P操作为S=S-1;表示申请一个资源,V操作为S=S+1;表示释放一个资源。因为对整数的加减1操作是在一个指令完成的,而硬件能够保证中断只能发生在指令之间,所以PV操作不会被中断打扰执行结果。
P
{
S = S-1;
if(s<0)
Wait(S); --------当前进程进入等待队列等待
}
V
{
S=S+1;
if(S<=0)
Resume(S); ----------唤醒等待队列中的一个进程
}
2.利用信号量实现互斥
初始化信号量mutex = 1; 当进程进入临界区时执行P操作,退出临界区时执行V操作。
P(mutex)
临界区代码;(操作互斥资源)
V(mutex)
3. 利用信号量实现同步
此时可以把信号想象成代表一个消息。当S=0表示表示消息未产生;当S>0则表示消息已经产生。例如:
(1)单缓冲区的生产者和消费者问题。
生产者进程P1不断地生产产品送入缓冲区,消费者进程P2不断地从缓冲区中提取产品消费。为了实现P1与P2进程间的同步,需要设置一个信号量S1,并且初始化为1,表示缓冲区为空,可以将产品放入缓冲区中;还需要设置另一个另一个信号量S2,初始值为0,表示缓冲区没有产品,可以提取消费。
P1:生产一个产品--->P(S1)测试缓冲区是否为空----->产品送缓冲区---->V(S2)设置有产品---->重复
P2: P(S2)测试是否有产品----->从缓冲区取出产品------->V(S1)设置缓冲区为空------->消费--->重复
(2)多缓冲区同步问题
设有一个生产者和一个消费者,缓冲区可以存放n件物品,生产者不断地生产产品,消费者不断地消费产品。
设置3个信号量,S, S1,S2。其中S是一个互斥信号量初值为1,对缓冲区资源进行互斥控制,S1表示是否可以将物品放入缓冲区,初值为n,S2表示缓冲区中是否有物品,初值为0。同步过程如下:
P1:生产一个产品----->P(S1)--->P(S)--->产品送缓冲区--->V(S)---->V(S2)
P2:P(S2)------>P(S)--->从缓冲区取出一个产品----->V(S)----->V(S1)----->消费
附上在Windows和Unix平台使用信号量的源码:
- 至于什么是信号量(Semaphore),网上相关资料多得很,一搜一大把,我就不做重复劳动了。只是把关于使用信号量的示例程序贴一下当作笔记,万一能对大家有点点帮助那就更好了。Windows 平台的代码来源于 MSDN,Unix 平台的代码是 Google 来的。
- Part 1 - Windows 平台信号量(Semaphore)示例程序
- #include <stdio.h>
- #include <Windows.h>
- #define MAX_SEM_COUNT 10
- #define THREADCOUNT 12
- HANDLE ghSemaphore;
- DWORD WINAPI ThreadProc( LPVOID );
- void main(int argc, char* argv[])
- {
- HANDLE aThread[THREADCOUNT];
- DWORD ThreadID;
- int i;
- // Create a semaphore with initial and max counts of MAX_SEM_COUNT
- ghSemaphore = CreateSemaphore(
- NULL, // default security attributes - lpSemaphoreAttributes是信号量的安全属性
- MAX_SEM_COUNT, // initial count - lInitialCount是初始化的信号量
- MAX_SEM_COUNT, // maximum count - lMaximumCount是允许信号量增加到最大值
- NULL); // unnamed semaphore - lpName是信号量的名称
- if (ghSemaphore == NULL)
- {
- printf("CreateSemaphore error: %d/n", GetLastError());
- return;
- }
- // Create worker threads
- for( i=0; i <THREADCOUNT; i++ )
- {
- aThread[i] = CreateThread(
- NULL, // default security attributes
- 0, // default stack size
- (LPTHREAD_START_ROUTINE) ThreadProc,
- NULL, // no thread function arguments
- 0, // default creation flags
- &ThreadID); // receive thread identifier
- if( aThread[i] == NULL )
- {
- printf("CreateThread error: %d/n", GetLastError());
- return;
- }
- }
- // Wait for all threads to terminate
- WaitForMultipleObjects(THREADCOUNT, aThread, TRUE, INFINITE);
- // Close thread and semaphore handles
- for( i=0; i <THREADCOUNT; i++ )
- CloseHandle(aThread[i]);
- CloseHandle(ghSemaphore);
- }
- DWORD WINAPI ThreadProc( LPVOID lpParam )
- {
- DWORD dwWaitResult;
- BOOL bContinue=TRUE;
- while(bContinue)
- {
- // Try to enter the semaphore gate.
- dwWaitResult = WaitForSingleObject(
- ghSemaphore, // handle to semaphore
- 0L); // zero-second time-out interval
- switch (dwWaitResult)
- {
- // The semaphore object was signaled.
- case WAIT_OBJECT_0:
- // TODO: Perform task
- printf("Thread %d: wait succeeded/n", GetCurrentThreadId());
- // Ensure a thread performs only once
- bContinue=FALSE;
- // Simulate thread spending time on task
- Sleep(5);
- // Release the semaphore when task is finished
- if (!ReleaseSemaphore(
- ghSemaphore, // handle to semaphore - hSemaphore是要增加的信号量句柄
- 1, // increase count by one - lReleaseCount是增加的计数
- NULL) ) // not interested in previous count - lpPreviousCount是增加前的数值返回
- {
- printf("ReleaseSemaphore error: %d/n", GetLastError());
- }
- break;
- // The semaphore was nonsignaled, so a time-out occurred.
- case WAIT_TIMEOUT:
- printf("Thread %d: wait timed out/n", GetCurrentThreadId());
- break;
- }
- }
- return TRUE;
- }
- Part 2 - Unix 平台信号量(Semaphore)示例程序
- /* semabinit.c - initialize a semaphore for use by programs sema and semb */
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/sem.h>
- #include <stdio.h>
- /* The semaphore key is an arbitrary long integer which serves as an
- external identifier by which the semaphore is known to any program
- that wishes to use it. */
- #define KEY (1492)
- void main()
- {
- int id; /* Number by which the semaphore is known within a program */
- /* The next thing is an argument to the semctl() function. Semctl()
- does various things to the semaphore depending on which arguments
- are passed. We will use it to make sure that the value of the
- semaphore is initially 0. */
- union semun {
- int val;
- struct semid_ds *buf;
- ushort * array;
- } argument;
- argument.val = 0;
- /* Create the semaphore with external key KEY if it doesn't already
- exists. Give permissions to the world. */
- id = semget(KEY, 1, 0666 | IPC_CREAT);
- /* Always check system returns. */
- if(id <0)
- {
- fprintf(stderr, "Unable to obtain semaphore. ");
- exit(0);
- }
- /* What we actually get is an array of semaphores. The second
- argument to semget() was the array dimension - in our case
- 1. */
- /* Set the value of the number 0 semaphore in semaphore array
- # id to the value 0. */
- if( semctl(id, 0, SETVAL, argument) <0)
- {
- fprintf( stderr, "Cannot set semaphore value. ");
- }
- else
- {
- fprintf(stderr, "Semaphore %d initialized. ", KEY);
- }
- }
- /* Semaphore example program a (sema.c) */
- /* We have two programs, sema and semb. Semb may be initiated at any
- time, but will be forced to wait until sema is executed. Sema and
- semb do not have to be executed by the same user! */
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/sem.h>
- #define KEY (1492)
- /* This is the external name by which the semaphore is known to any
- program that wishes to Access it. */
- void main()
- {
- int id; /* Internal identifier of the semaphore. */
- struct sembuf operations[1];
- /* An "array" of one operation to perform on the semaphore. */
- int retval; /* Return value from semop() */
- /* Get the index for the semaphore with external name KEY. */
- id = semget(KEY, 1, 0666);
- if(id <0)
- /* Semaphore does not exist. */
- {
- fprintf(stderr, "Program sema cannot find semaphore, exiting. ");
- exit(0);
- }
- /* Do a semaphore V-operation. */
- printf("Program sema about to do a V-operation. ");
- /* Set up the sembuf structure. */
- /* Which semaphore in the semaphore array : */
- operations[0].sem_num = 0;
- /* Which operation? Add 1 to semaphore value : */
- operations[0].sem_op = 1;
- /* Set the flag so we will wait : */
- operations[0].sem_flg = 0;
- /* So do the operation! */
- retval = semop(id, operations, 1);
- if(retval == 0)
- {
- printf("Successful V-operation by program sema. ");
- }
- else
- {
- printf("sema: V-operation did not succeed. ");
- perror("REASON");
- }
- }
- /* Think carefully about what the V-operation does. If sema is executed
- twice, then semb can execute twice. */
- /* Semaphore example program b (semb.c) */
- /* We have two programs, sema and semb. Semb may be initiated at any
- time, but will be forced to wait until sema is executed. Sema and
- semb do not have to be executed by the same user! */
- /* HOW TO TEST:
- Execute semb &
- The & is important - otherwise you would have have to move to
- a different terminal to execute sema.
- Then execute sema.
- */
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/sem.h>
- #define KEY (1492)
- /* This is the external name by which the semaphore is known to any
- program that wishes to access it. */
- void main()
- {
- int id; /* Internal identifier of the semaphore. */
- struct sembuf operations[1];
- /* An "array" of one operation to perform on the semaphore. */
- int retval; /* Return value from semop() */
- /* Get the index for the semaphore with external name KEY. */
- id = semget(KEY, 1, 0666);
- if(id <0)
- /* Semaphore does not exist. */
- {
- fprintf(stderr, "Program semb cannot find semaphore, exiting. ");
- exit(0);
- }
- /* Do a semaphore P-operation. */
- printf("Program semb about to do a P-operation. ");
- printf("Process id is %d ", getpid());
- /* Set up the sembuf structure. */
- /* Which semaphore in the semaphore array : */
- operations[0].sem_num = 0;
- /* Which operation? Subtract 1 from semaphore value : */
- operations[0].sem_op = -1;
- /* Set the flag so we will wait : */
- operations[0].sem_flg = 0;
- /* So do the operation! */
- retval = semop(id, operations, 1);
- if(retval == 0)
- {
- printf("Successful P-operation by program semb. ");
- printf("Process id is %d ", getpid());
- }
- else
- {
- printf("semb: P-operation did not succeed. ");
- }
- }
- /* Think carefully about what the V-operation does. If sema is executed
- twice, then semb can execute twice. */