//信号量与PV操作的一个例子,模拟借书还书操作,共4个用户,每用户最多借15本书,书库共40本书,在书库没书时,该用户处于睡眠等待状态,直到有其他用户还书,若所有用户均处于等待状态,则产生了死锁
int Semget(key_t semkey,int nsems,int semflgs);
bool P(int sid,int semnum,int n);
bool V(int sid,int semnum,int n);
void sig_usr1(int);
static int pid[4];
int main(int argc,char **argv)
{
int semid;
if((semid=Semget(4446,1,IPC_CREAT|IPC_EXCL|0666))==-1)exit(-1);
if(semctl(semid,0,SETVAL,40)==-1)perr_exit("semctl"); //书的总量
int book,sleeptime;
int borrow;
for(int i=0;i<4;i++){ //4个用户
pid[i]=fork();
if(pid[i]<0){perror("Fork");i--;}
else if(pid[i]==0){
srand(time(NULL)+i);
book=0;
while(1){
sleeptime=rand()%7;
borrow=sleeptime-3; //每次最多借还3本
if(borrow>0&&borrow+book<=15) //每人最多借15本
{
if(!P(semid,0,borrow))
continue;
book+=borrow;
cout<<"User "<<i<<" borrow "<<borrow<<" books from libary,"
<<"now it remains ";
cout<<semctl(semid,0,GETVAL,0)<<" books\n";
sleep(sleeptime);
}
else if(borrow<0&&borrow+book>=0)
{
if(!V(semid,0,-borrow))continue;
book+=borrow;
cout<<"User "<<i<<" gives "<<-borrow<<" books back to libary,"
<<"now it remains "; //由于V与GETVAL并不是原子操作,此处可能打印错误
//有可能在归还与取值之间,书被借出
cout<<semctl(semid,0,GETVAL,0)<<" books\n";
sleep(sleeptime);
}
}
return 0;
}
}
signal(SIGUSR1,sig_usr1);
while(1)usleep(1000);
return 0;
}
int Semget(key_t semkey,int nsems,int semflgs)
{
int semid;
if ((semid = semget(semkey, 0, 0)) == -1) { /* Semaphore does not exist - Create. */
if ((semid = semget(semkey,nsems,semflgs)) != -1)
return semid;
else if (errno == EEXIST) {
if((semid = semget(semkey, 0, 0)) == -1) {
perror("IPC error 1: semget"); return -1;
}
}
else {
perror("IPC error 2: semget"); return -1;
}
}
return semid;
}
void sig_usr1(int sig)
{
for(int i=0;i<4;i++)
kill(pid[i],SIGQUIT);
kill(getpid(),SIGQUIT);
}
bool Semset(int sid,int semnum,int n)
{
struct sembuf sm;
sm.sem_num=semnum;
sm.sem_op=n;
sm.sem_flg=SEM_UNDO;
if(semop(sid,&sm,1)==-1){
perror ("semop");
return false;
}
return true;
}
bool P(int sid,int semnum,int n)
{
return Semset(sid,semnum,-n);
}
bool V(int sid,int semnum,int n)
{
return Semset(sid,semnum,n);
}
以下是解决死锁之后的代码,主进程负责监测子进程,如果发生死锁,就发信号阻止某个进程,禁止其消费,只允许其生产,而若该子进程不能生产,则用信号通知父进程,让其另选其他进程阻止。而若子进程监测到资源可供消费,则向自身发信号,设置其为可消费状态。
int Semget(key_t semkey,int nsems,int semflgs);
bool P(int sid,int semnum,int n);
bool V(int sid,int semnum,int n);
void sig_usr1(int);
void sig_usr2(int);
#define MaxUser 5
static int pid[MaxUser];
volatile bool good=true;
static int lastdeny;
static int user;
#define SIGDENY SIGUSR2+1
#define SIGALLOW SIGUSR2
#define SIGNEXTDENY SIGUSR2+2
int main(int argc,char **argv)
{
int semid;
if((semid=Semget(4446,1,IPC_CREAT|IPC_EXCL|0666))==-1)exit(-1);
if(semctl(semid,0,SETVAL,40)==-1)perr_exit("semctl");
int book,sleeptime;
int borrow;
for(user=0;user<MaxUser;user++){
pid[user]=fork();
if(pid[user]<0){perror("Fork");user--;}
else if(pid[user]==0){
srand(time(NULL)+user);
book=0;
signal(SIGALLOW,sig_usr2);
signal(SIGDENY,sig_usr2);
while(1){
sleeptime=rand()%6;
borrow=sleeptime-2;
if(!good &&semctl(semid,0,GETNCNT,0)<MaxUser-1)
kill(getpid(),SIGALLOW);
else if(borrow>0&&borrow+book<=15&&good)
{
if(!P(semid,0,borrow))
continue;
book+=borrow;
cout<<"User "<<user <<" borrow "<<borrow<<" books from libary,"
<<"now it remains ";
cout<<semctl(semid,0,GETVAL,0)<<" books\n";
sleep(sleeptime);
}
else if(borrow<0&&borrow+book>=0)
{
if(!V(semid,0,-borrow))continue;
book+=borrow;
cout<<"User "<<user<<" gives "<<-borrow<<" books back to libary,"
<<"now it remains ";
cout<<semctl(semid,0,GETVAL,0)<<" books\n";
sleep(sleeptime);
}
else if(!good&&book==0) //cannot borrow and no book keeping
{
kill(getpid(),SIGALLOW);
kill(getppid(),SIGNEXTDENY); //send signal to parent to deny some other user
}
}
return 0;
}
}
signal(SIGUSR1,sig_usr1);
signal(SIGNEXTDENY,sig_usr2);
srand(time(NULL));
volatile int x=0;
int y=0;
while(1){
if(semctl(semid,0,GETNCNT,0)>=MaxUser){ //deadlock appear
while(pid[(x=rand()%MaxUser)]==lastdeny);
kill((lastdeny=pid[x]),SIGDENY); //make some user cannot borrow
}
usleep(1000);
y++;
if(y%1000==0){
y=0;
cout<<"\t\tMain Running\t\t"<<semctl(semid,0,GETNCNT,0)<<"waits\n";
}
}
return 0;
}
int Semget(key_t semkey,int nsems,int semflgs)
{
int semid;
if ((semid = semget(semkey, 0, 0)) == -1) { /* Semaphore does not exist - Create. */
if ((semid = semget(semkey,nsems,semflgs)) != -1)
return semid;
else if (errno == EEXIST) {
if((semid = semget(semkey, 0, 0)) == -1) {
perror("IPC error 1: semget"); return -1;
}
}
else {
perror("IPC error 2: semget"); return -1;
}
}
return semid;
}
void sig_usr1(int sig)
{
for(int i=0;i<MaxUser;i++)
kill(pid[i],SIGQUIT);
kill(getpid(),SIGQUIT);
}
void sig_usr2(int sig)
{
if(sig==SIGALLOW){ //allow user borrow book
good=true;
cout<<"/*--------------------------User"<<user
<<" Allowed-----------------------*/"<<endl;
}
else if(sig==SIGDENY){ //deny user borrow book
good=false;
cout<<"/*---------------------------User"<<user
<<" Denied-----------------------*/"<<endl;
}
else if(sig==SIGNEXTDENY){ //deny some user
volatile int deny;
while(pid[deny=rand()%MaxUser]==lastdeny);
kill((lastdeny=pid[deny]),SIGDENY);
}
}
bool Semset(int sid,int semnum,int n)
{
struct sembuf sm;
sm.sem_num=semnum;
sm.sem_op=n;
sm.sem_flg=SEM_UNDO;
if(semop(sid,&sm,1)==-1){
cout<<"/**********User "<<user<<" semop error:"
<<strerror(errno)<<"**************/"<<endl;
return false;
}
return true;
}
bool P(int sid,int semnum,int n)
{
return Semset(sid,semnum,-n);
}
bool V(int sid,int semnum,int n)
{
return Semset(sid,semnum,n);
}