unix或linux实现P,V操作,主要是通过信号等操作来实现。
信号灯的创建和获取通过调用semget函数来实现 例如mysem_id=semget(SEM_KEY,SEMSIZE,IPC_CREAT|0666)),其中SEM_KEY为欲创建或调用的信号灯的键值,可以自己定义 比如#define SEM_KEY 7589 ,SEMSIZE 为信号等的数量,semget实际是得到一个信号灯数组,这个数组的长度可以自己定义 #define SEMSIZE 5 这样就得到了一个信号灯数量为5的信号灯数组。创建或取得信号等数组后,可以对各个信号灯进行赋值。一下为赋值函数
/**
* s_id 第s_id个信号灯
* ini_value 初始值
*/
void init_mysemvalue(int s_id,int ini_value)
{
key_t key;
int i;
struct sembuf op;
union semun arg;
// op.sem_num = s_id;
op.sem_op = 1;
op.sem_flg = 0;
// key=ftok(PATHNAME,'O');
if(mysem_id == -1){
if((mysem_id=semget(SEM_KEY,SEMSIZE,IPC_CREAT|0666))==-1){
perror("semget err!\n");
exit(-1);
}
}
/*arg.array = (unsigned short *) calloc(SEMSIZE,sizeof(unsigned short));
if(arg.array == NULL){
printf("No enough memory to calloc!\n");
exit(-1);
}
arg.array[s_id] = ini_value;*/
arg.val = ini_value;
if(semctl(mysem_id,s_id,SETVAL,arg) == -1){
perror("semctl err:");
exit(-1);
}
}
其中 union semun 的定义为
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
};
取得指定的信号等的当前值
int getmysemvalue(int s_id)
{
int i_value;
if(mysem_id == -1){/*如果没有创建或者获取到信号灯的句柄,要先获取句柄*/
if((mysem_id=semget(SEM_KEY,SEMSIZE,IPC_CREAT|0666))==-1){
perror("semget err!\n");
exit(-1);
}
}
i_value = semctl(mysem_id,s_id,GETVAL);
if(i_value == -1){
perror("semctl err:");
exit(-1);
}
return i_value;
}
mywait用于对信号等进可回滚的P操作,可回滚的意思是,当对信号灯进行P操作的进程因为某些意外或者有意识的退出(死亡)后,信号等将自动恢复到操作前的状态,例如某个进程因为做了P操作阻塞了,在阻塞过程中被人人为的kill掉,该进程死后,系统讲自动的对该信号量做一次V操作以保持平衡,这样的好处是在进行互斥操作的多进程程序不会因为某个进程只做P操作而未做V操作就退出后,发生死锁行为。
void mywait(int s_id)
{
struct sembuf op;
int i_result;
op.sem_num = s_id;
op.sem_op = -1;
op.sem_flg = SEM_UNDO;
if(mysem_id == -1)
init_mysem(0);
while((i_result = semop(mysem_id,&op,1)) == -1 && errno == EINTR) usleep(5000);
if(i_result==-1){
printf("mywait sid = %d errno = %d %s\n",s_id,errno,strerror(errno));
if(errno == ERANGE){
printf("semval = %d\n",getmysemvalue(s_id));
}
perror("semop err!\n");
exit(-1);
}
}
mysignal对信号灯进行可回滚的V操作,回滚的好处和刚才的mywait一样
void mysignal(int s_id)
{
struct sembuf op;
int i_result;
op.sem_num = s_id;
op.sem_op = 1;
op.sem_flg = SEM_UNDO;
if(mysem_id == -1)
init_mysem(0);
while((i_result = semop(mysem_id,&op,1)) == -1 && errno == EINTR) usleep(5000);
if(i_result==-1){
printf("mysignal s_id = %d errno = %d %s\n",s_id,errno,strerror(errno));
if(errno == ERANGE){
printf("semval = %d\n",getmysemvalue(s_id));
}
perror("semop err!\n");
exit(-1);
}
}
myuwait 是对信号灯进行不可回滚的P操作,使用起来要各位小心。否则容易发生死锁
void myuwait(int s_id)
{
struct sembuf op;
int i_result;
op.sem_num = s_id;
op.sem_op = -1;
op.sem_flg = 0;
if(mysem_id == -1)
init_mysem(0);
while((i_result = semop(mysem_id,&op,1)) == -1 && errno == EINTR) usleep(5000);
if(i_result==-1){
printf("myuwait s_id = %d errno = %d %s\n",s_id,errno,strerror(errno));
if(errno == ERANGE){
printf("semval = %d\n",getmysemvalue(s_id));
}
perror("semop err!\n");
exit(-1);
}
}
myusignal是对信号灯进行不可回滚的V操作
void myusignal(int s_id)
{
struct sembuf op;
int i_result;
op.sem_num = s_id;
op.sem_op = 1;
op.sem_flg = 0;
if(mysem_id == -1)
init_mysem(0);
while((i_result = semop(mysem_id,&op,1)) == -1 && errno == EINTR) usleep(5000);
if(i_result==-1){
printf("myusignal s_id = %d errno = %d %s\n",s_id,errno,strerror(errno));
if(errno == ERANGE){
printf("semval = %d\n",getmysemvalue(s_id));
}
perror("semop err!\n");
exit(-1);
}
}
需要注意的是多某个信号灯变量做可回滚的P操作,就必须使用可回滚的V操作,就是说如果对某个信号灯用mywait操作,就必须使用mysignal做V操作,而不能使用myusignal,并且其他地方要对该信号灯变量做P,V操作,都必须使用mywait 和 mysignal ,mywait 和 myuwait不能对同一个信号等变量混合使用,否则系统会发生不可预测的错误,我的经验是,混合使用到一定时间后,信号等变量的值会突然成一个很大的随机数。
代码范例可到 下载
阅读(472) | 评论(0) | 转发(0) |