//信號量與PV操作的一個例子,模擬借書還書操作,共4個用戶,每用戶最多借15本書,書庫共40本書,在書庫沒書時,該用戶處于睡眠等待狀態(tài),直到有其他用戶還書,若所有用戶均處于等待狀態(tài),則產生了死鎖
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);
}
以下是解決死鎖之后的代碼,主進程負責監(jiān)測子進程,如果發(fā)生死鎖,就發(fā)信號阻止某個進程,禁止其消費,只允許其生產,而若該子進程不能生產,則用信號通知父進程,讓其另選其他進程阻止。而若子進程監(jiān)測到資源可供消費,則向自身發(fā)信號,設置其為可消費狀態(tài)。
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);
}