信号量是一个计数器。可以用于多进程也可以用于多线程,主要用于共享数据的同步访问。
如果受保护的资源是可用的,那么信号量的值为正数,如果受保护的资源现在不可用,那么信号量的值为0。
请求访问保护资源(P操作):
要访问受保护的资源的进程或者线程试图对信号量的值减1,如果信号量的值现在不可用,即信号量为0,减1操作将被阻塞(休眠),直到信号量大于0时,才会得以继续执行。
释放保护资源(V操作):
要释放受保护的资源时,信号量的值将会加1,此时信号量的值大于0,其他请求该资源被信号量阻塞的线程或者进程将被唤醒。
总之,信号量为0(信号量不能小于0),无可用资源,信号量大于0,有可用资源。
释放资源,则将信号量加1,请求资源,则信号量减1。
初始化:
控制单个资源,信号量初始值为1,N个资源,初始值为N
1.信号量的创建
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
(1)key从ftok函数获取
(2)nsems信号量集的个数,换句话来说,semget创建的信号量集id,可以控制nsems个资源的访问和释放。
(3)semflg是信号量的权限设置,如果信号量不存在,semflg要跟IPC_CREAT相与,否则,要跟IPC_PRIVATE相与,如果设置权限所有人都能访问,semflg设置为0666|IPC_CREAT或者0666|IPC_PRIVATE。
(4)函数返回值为信号量集id,如为-1,则创建失败。
2.信号量的资源操作(P和V)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
(1)semid是信号量集id
(2)struct sembuf 结构体定义有如下成员:
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
其中sem_num是要操作的信号量集中的资源序号,比如有10个资源,序号为(0,1,2,...,9),
sem_op的值为正数、负数,0,三种。
设置为正数表示释放资源操作(V操作)
设置为负数表示要请求资源访问的操作(P操作)
设置0表示等待可用资源为0。
(3)sem_flg设置为IPC_NOWAIT或SEM_UNDO
4.信号量的删除
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
设置cmd为IPC_RMID即为删除信号量
其中semnum可以忽视
semctl( semid,0,IPC_RMID);
以下代码设置一个信号量管理多线程中的5个临界资源的同步访问
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/msg.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#define SemKey 6541
#define SemNum 5
#define SemOpNegative (-1)
#define SemOpPositive 1
#define SemOpZero (0)
static int gs_nSemid = -1;
int Sem_init(int *pnSemId,int nSemNum)
{
key_t key=-1;
int nSemid = -1;
if(pnSemId == NULL ||
nSemNum <= 0)
{
printf("\n param error [%s %d]\n",__FUNCTION__,__LINE__);
return -1;
}
*pnSemId = -1;
key = ftok("/share/1.tmp",SemKey);
if(key == -1)
{
printf("\n if(key == -1) errno=%d [%s]\n",errno,strerror(errno));
return -1;
}
else
{
printf("\n ftok key[%d]\n",key);
}
nSemid = semget(nSemid,SemNum ,0666|IPC_CREAT);
if(-1 == nSemid)
{
printf("\n if(key == -1) errno=%d [%s]\n",errno,strerror(errno));
return -1;
}
*pnSemId = nSemid;
return 0;
}
//nSemNumber为信号量集的序号(0 1 2 3 ...)
//这里实现一次只操作一个
//semflag 设置为IPC_NOWAIT
int Sem_P(int nSemId,int nSemNumber,short semflag)//请求资源
{
int ret = -1;
struct sembuf stuSemBuf = {0};
if(nSemId < 0 ||
nSemNumber < 0)
{
printf("\n param error [%s %d]\n",__FUNCTION__,__LINE__);
return -1;
}
memset(&stuSemBuf,0,sizeof(struct sembuf));
stuSemBuf.sem_num = nSemNumber;
stuSemBuf.sem_op = SemOpNegative;
stuSemBuf.sem_flg = semflag;
ret = semop(nSemId,&stuSemBuf,1);// 1为操作的个数
if(-1 == ret)
{
printf("\n nSemNumber[%d] failed Sem_P errno=%d [%s]\n",nSemNumber,errno,strerror(errno));
return -1;
}
printf("\n nSemNumber[%d] success Sem_P ",nSemNumber);
return 0;
}
int Sem_V(int nSemId,int nSemNumber,short semflag)
{
int ret = -1;
struct sembuf stuSemBuf = {0};
if(nSemId < 0 ||
nSemNumber < 0)
{
printf("\n param error [%s %d]\n",__FUNCTION__,__LINE__);
return -1;
}
memset(&stuSemBuf,0,sizeof(struct sembuf));
stuSemBuf.sem_num = nSemNumber;
stuSemBuf.sem_op = SemOpPositive;
stuSemBuf.sem_flg = semflag;
ret = semop(nSemId,&stuSemBuf,1);// 1为操作的个数
if(-1 == ret)
{
printf("\n nSemNumber[%d] failed Sem_V errno=%d [%s]\n",nSemNumber,errno,strerror(errno));
return -1;
}
printf("\n nSemNumber[%d] success to Sem_V ",nSemNumber);
return 0;
}
void *func_1(void* pArg)
{
static int i =0;
int ret = -1;
while(1)
{
i++;
ret = Sem_P(gs_nSemid,i%SemNum,IPC_NOWAIT);
sleep(i%3);
}
return (void *)0;
}
void *func_2(void* pArg)
{
static int i =0;
int ret = -1;
sleep(10);
while(1)
{
i++;
ret = Sem_V(gs_nSemid,i%SemNum,IPC_NOWAIT);
sleep(i%5);
}
return (void *)0;
}
int main()
{
pthread_t thread01_id = -1;
pthread_t thread02_id = -1;
int ret = -1;
ret = Sem_init(&gs_nSemid,SemNum);
if(-1 == ret)
{
printf("\n Sem_init error ! \n");
return -1;
}
printf("\n Sem_init gs_nSemid=%d SemNum=%d\n",gs_nSemid,SemNum);
pthread_create(&thread01_id, NULL, func_1, NULL);
pthread_create(&thread02_id, NULL, func_2, NULL);
//semctl(gs_nSemid,0,IPC_RMID);删除信号量
while(1)
{
sleep(1000);
}
return 0;
}
运行结果:
./a.out
ftok key[-1929294457]
Sem_init gs_nSemid=0 SemNum=5
nSemNumber[1] failed Sem_P errno=11 [Resource temporarily unavailable]
nSemNumber[2] failed Sem_P errno=11 [Resource temporarily unavailable]
nSemNumber[3] success Sem_P
nSemNumber[4] failed Sem_P errno=11 [Resource temporarily unavailable]
nSemNumber[0] failed Sem_P errno=11 [Resource temporarily unavailable]
nSemNumber[1] failed Sem_P errno=11 [Resource temporarily unavailable]
nSemNumber[2] failed Sem_P errno=11 [Resource temporarily unavailable]
nSemNumber[3] failed Sem_P errno=11 [Resource temporarily unavailable]
nSemNumber[4] failed Sem_P errno=11 [Resource temporarily unavailable]
nSemNumber[0] failed Sem_P errno=11 [Resource temporarily unavailable]
nSemNumber[1] success to Sem_V
nSemNumber[1] success Sem_P
nSemNumber[2] success to Sem_V
nSemNumber[2] success Sem_P
nSemNumber[3] failed Sem_P errno=11 [Resource temporarily unavailable]
nSemNumber[3] success to Sem_V
nSemNumber[4] failed Sem_P errno=11 [Resource temporarily unavailable]
nSemNumber[0] failed Sem_P errno=11 [Resource temporarily unavailable]
nSemNumber[1] failed Sem_P errno=11 [Resource temporarily unavailable]
nSemNumber[4] success to Sem_V
nSemNumber[2] failed Sem_P errno=11 [Resource temporarily unavailable]
nSemNumber[3] success Sem_P
nSemNumber[4] success Sem_P
nSemNumber[0] failed Sem_P errno=11 [Resource temporarily unavailable]
nSemNumber[0] success to Sem_V
nSemNumber[1] success to Sem_V
nSemNumber[2] success to Sem_V
nSemNumber[1] success Sem_P
nSemNumber[2] success Sem_P
nSemNumber[3] failed Sem_P errno=11 [Resource temporarily unavailable]
nSemNumber[3] success to Sem_V
nSemNumber[4] failed Sem_P errno=11 [Resource temporarily unavailable]
nSemNumber[0] success Sem_P
nSemNumber[1] failed Sem_P errno=11 [Resource temporarily unavailable]
nSemNumber[4] success to Sem_V
nSemNumber[2] failed Sem_P errno=11 [Resource temporarily unavailable]
查看信号量id控制的某个具体的资源,比如说第五个资源。
展示出了P操作和V操作的结合情况。
P请求资源,减一操作,无资源时阻塞或者设置errno马上返回。
V释放资源,加1操作,使得其他线程可以请求资源
C:\Users\zhouzhenhe\Desktop\3.txt (41 hits)
Line 12: nSemNumber[4] failed Sem_P errno=11 [Resource temporarily unavailable]
Line 22: nSemNumber[4] failed Sem_P errno=11 [Resource temporarily unavailable]
Line 33: nSemNumber[4] failed Sem_P errno=11 [Resource temporarily unavailable]
Line 39: nSemNumber[4] success to Sem_V
Line 43: nSemNumber[4] success Sem_P
Line 54: nSemNumber[4] failed Sem_P errno=11 [Resource temporarily unavailable]
Line 59: nSemNumber[4] success to Sem_V
Line 63: nSemNumber[4] success Sem_P
Line 72: nSemNumber[4] failed Sem_P errno=11 [Resource temporarily unavailable]
Line 76: nSemNumber[4] success to Sem_V
Line 83: nSemNumber[4] success Sem_P
Line 94: nSemNumber[4] failed Sem_P errno=11 [Resource temporarily unavailable]
Line 99: nSemNumber[4] success to Sem_V
Line 103: nSemNumber[4] success Sem_P
Line 114: nSemNumber[4] failed Sem_P errno=11 [Resource temporarily unavailable]
Line 119: nSemNumber[4] success to Sem_V
Line 123: nSemNumber[4] success Sem_P
Line 132: nSemNumber[4] failed Sem_P errno=11 [Resource temporarily unavailable]
Line 136: nSemNumber[4] success to Sem_V
Line 143: nSemNumber[4] success Sem_P
Line 154: nSemNumber[4] failed Sem_P errno=11 [Resource temporarily unavailable]