您的位置:首页 > 其它

进程通信:信号量和互斥锁

2016-10-22 00:00 155 查看
 PV原子操作的具体定义如下:(好好理解,很重要的啊)

  ● P操作:如果有可用的资源(信号量值>0),则此操作所在的进程占用一个资源(此时信号量值减1,进入临界区代码);如果没有可用的资源(信号量值=0),则此操作所在的进程被阻塞直到系统将资源分配给该进程(进入等待队列,一直等到资源轮到该进程)。

  ● V操作:如果在该信号量的等待队列中有进程在等待资源,则唤醒一个阻塞进程;如果没有进程等待它,则释放一个资源(即信号量值加1)。

System V信号量

semget(得到一个信号量集标识符
或创建一个信号量集对象)
所需头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
函数说明
得到一个信号量集标识符或创建一个信号量集对象并返回信号量集标识符
函数原型
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_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:设置获取信号量的任务运行在最高优先级。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  互斥 PV操作