信号量是一个计数器。可以用于多进程也可以用于多线程,主要用于共享数据的同步访问。
如果受保护的资源是可用的,那么信号量的值为正数,如果受保护的资源现在不可用,那么信号量的值为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]