PV原子操做的具体定义以下:(好好理解,很重要的啊)git
● P操做:若是有可用的资源(信号量值>0),则此操做所在的进程占用一个资源(此时信号量值减1,进入临界区代码);若是没有可用的资源(信号量值=0),则此操做所在的进程被阻塞直到系统将资源分配给该进程(进入等待队列,一直等到资源轮到该进程)。github
● V操做:若是在该信号量的等待队列中有进程在等待资源,则唤醒一个阻塞进程;若是没有进程等待它,则释放一个资源(即信号量值加1)。数组
semget(获得一个信号量集标识符缓存 或建立一个信号量集对象)数据结构 |
||
所需头文件函数 |
#include <sys/types.h>spa #include <sys/ipc.h>线程 #include <sys/sem.h>code |
|
函数说明对象 |
获得一个信号量集标识符或建立一个信号量集对象并返回信号量集标识符 |
|
函数原型 |
int semget(key_t key, int nsems, int semflg) |
|
函数传入值 |
key |
0(IPC_PRIVATE):会创建新信号量集对象 |
大于0的32位整数:视参数semflg来肯定操做,一般要求此值来源于ftok返回的IPC键值 |
||
nsems |
建立信号量集中信号量的个数,该参数只在建立信号量集时有效 |
|
msgflg |
0:取信号量集标识符,若不存在则函数会报错 |
|
IPC_CREAT:当semflg&IPC_CREAT为真时,若是内核中不存在键值与key相等的信号量集,则新建一个信号量集;若是存在这样的信号量集,返回此信号量集的标识符 |
||
IPC_CREAT|IPC_EXCL:若是内核中不存在键值与key相等的信号量集,则新建一个消息队列;若是存在这样的信号量集则报错 |
||
函数返回值 |
成功:返回信号量集的标识符 |
|
出错:-1,错误缘由存于error中 |
||
附加说明 |
上述semflg参数为模式标志参数,使用时须要与IPC对象存取权限(如0600)进行|运算来肯定信号量集的存取权限 |
|
错误代码 |
EACCESS:没有权限 EEXIST:信号量集已经存在,没法建立 EIDRM:信号量集已经删除 ENOENT:信号量集不存在,同时semflg没有设置IPC_CREAT标志 ENOMEM:没有足够的内存建立新的信号量集 ENOSPC:超出限制 |
semop(完成对信号量的P操做或V操做) |
|
所需头文件 |
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> |
函数说明 |
对信号量集标识符为semid中的一个或多个信号量进行P操做或V操做 |
函数原型 |
int semop(int semid, struct sembuf *sops, unsigned nsops) |
函数传入值 |
semid:信号量集标识符 |
sops:指向进行操做的信号量集结构体数组的首地址,此结构的具体说明以下: struct sembuf { short semnum; /*信号量集合中的信号量编号,0表明第1个信号量*/ short val;/*若val>0进行V操做信号量值加val,表示进程释放控制的资源 */ /*若val<0进行P操做信号量值减val,若(semval-val)<0(semval为该信号量值),则调用进程阻塞,直到资源可用;若设置IPC_NOWAIT不会睡眠,进程直接返回EAGAIN错误*/ /*若val==0时阻塞等待信号量为0,调用进程进入睡眠状态,直到信号值为0;若设置IPC_NOWAIT,进程不会睡眠,直接返回EAGAIN错误*/ short flag; /*0 设置信号量的默认操做*/ /*IPC_NOWAIT设置信号量操做不等待*/ /*SEM_UNDO 选项会让内核记录一个与调用进程相关的UNDO记录,若是该进程崩溃,则根据这个进程的UNDO记录自动恢复相应信号量的计数值*/ }; |
|
nsops:进行操做信号量的个数,即sops结构变量的个数,需大于或等于1。最多见设置此值等于1,只完成对一个信号量的操做 |
|
函数返回值 |
成功:返回信号量集的标识符 |
出错:-1,错误缘由存于error中 |
|
错误代码 |
E2BIG:一次对信号量个数的操做超过了系统限制 EACCESS:权限不够 EAGAIN:使用了IPC_NOWAIT,但操做不能继续进行 EFAULT:sops指向的地址无效 EIDRM:信号量集已经删除 EINTR:当睡眠时接收到其余信号 EINVAL:信号量集不存在,或者semid无效 ENOMEM:使用了SEM_UNDO,但无足够的内存建立所需的数据结构 ERANGE:信号量值超出范围 |
semctl (获得一个信号量集标识符或建立一个 信号量集对象) |
|
所需头文件 |
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> |
函数说明 |
获得一个信号量集标识符或建立一个信号量集对象并返回信号量集标识符 |
函数原型 |
int semctl(int semid, int semnum, int cmd, union semun arg) |
函数传入值 |
semid 信号量集标识符 |
semnum |
信号量集数组上的下标,表示某一个信号量 |
cmd |
见下文表15-4 |
arg |
union semun { short val; /*SETVAL用的值*/ struct semid_ds* buf; /*IPC_STAT、IPC_SET用的semid_ds结构*/ unsigned short* array; /*SETALL、GETALL用的数组值*/ struct seminfo *buf; /*为控制IPC_INFO提供的缓存*/ } arg; |
函数返回值 |
成功:大于或等于0,具体说明请参照表15-4 |
出错:-1,错误缘由存于error中 |
|
附加说明 |
semid_ds结构见上文信号量集内核结构定义 |
错误代码 |
EACCESS:权限不够 EFAULT:arg指向的地址无效 EIDRM:信号量集已经删除 EINVAL:信号量集不存在,或者semid无效 EPERM:进程有效用户没有cmd的权限 ERANGE:信号量值超出范围 |
利用信号量封装得PV原子操做
代码实现:
https://github.com/manmao/Module/tree/master/Semaphore-PV
能够使用信号量,也能够使用互斥锁
pthread_mutex_t默认属性是:PTHREAD_PROCESS_PRIVATE,单进程内部使用,适用于同一个进程多个线程操做,可是能够修改pthread_mutext_t的属性为PTHREAD_PROCESS_SHARED,让其能够适用于多个进程的互斥
修改互斥锁的属性为进程共享
pthread_mutexattr_t mutexattr; pthread_mutex_t mutex; pthread_mutexattr_init(&mutexattr); pthread_mutexattr_setpshared(&mutexattr,PTHREAD_PROCESS_SHARED); //设置为进程共享 pthread_mutex_init(&mutex,&mutexattr);
pthread_mutex_t 实现进程互斥例子:
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> typedef struct _FOO { int nCount; int nData; }FOO,*PFOO; int main(int argc,char *argv[]) { FOO *ptr; pid_t pid; pthread_mutexattr_t mutexattr; pthread_mutex_t mutex; pthread_mutexattr_init(&mutexattr); pthread_mutexattr_setpshared(&mutexattr,PTHREAD_PROCESS_SHARED); //设置为进程共享 pthread_mutex_init(&mutex,&mutexattr); ptr = (PFOO)mmap(NULL,sizeof(FOO),PROT_READ | PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0); //匿名内存映射,让父子进程都操做ptr指向的内存区,若是不使用共享内存,则父子进程的ptr指向的是各自的内存空间 ptr->nCount = 1; ptr->nData = 2; printf("%d,%d\n",ptr->nCount,ptr->nData); if( (pid = fork()) < 0) { printf("fork error\n"); return -1; } else if( 0 == pid) //子进程 { for(int i = 0;i<3;i++) { pthread_mutex_lock(&mutex); ptr->nCount++; printf("child ++ === %d\n",ptr->nCount); pthread_mutex_unlock(&mutex); usleep(1000); } } else //父进程 { for(int i = 0;i<3;i++) { pthread_mutex_lock(&mutex); ptr->nCount += 2; printf("parent +2 === %d\n",ptr->nCount); pthread_mutex_unlock(&mutex); usleep(1000); } } waitpid(pid,NULL,0); munmap(NULL,sizeof(FOO)); return 0; }
pthread_mutexattr_t 操做API:
pthread_mutexattr_init:配置初始化 pthread_mutexattr_destroy:删除配置初始化接口申请的资源 pthread_mutexattr_setpshared:设置mutex是否进程间共享 pthread_mutexattr_settype:设置类型,如递归调用,错误检测等。 pthread_mutexattr_setprotocol:设置是否支持优先级翻转 pthread_mutexattr_setprioceiling:设置获取信号量的任务运行在最高优先级。