計算機操作系統第五次實驗——信號量實現進程同步

1.實驗目的

利用信號量和PV操作實現進程的同步。

 

 

2.實驗軟硬件環境

  • 安裝Windows XP的計算機
  • VirtualBox軟件,以及在其上安裝的Ubuntu虛擬機

3.實驗內容

    生產者進程生產產品,消費者進程消費產品。當生產者進程生產產品時,如果沒有空緩衝區(倉庫)可用,那麼生產進程必須等待消費者進程釋放出一個緩衝區,當消費者進程消費產品時,如果緩衝區產品,那麼消費者進程將被阻塞,直到新的產品被生產出來。模擬一個生產者兩個消費者的情況。

 

   

使用函數:

 

1 semget函數

它的作用是創建一個新信號量或取得一個已有信號量,原型爲:

int semget(key_t  key, int num_sems, int sem_flags); 

semget函數成功返回一個相應信號標識符(非零),失敗返回-1.

第一個參數key是整數值(唯一非零),不相關的進程可以通過它訪問一個信號量,它代表程序可能要使用的某個資源,程序對所有信號量的訪問都是間接的,程序先通過調用semget函數並提供一個鍵,再由系統生成一個相應的信號標識符(semget函數的返回值),只有semget函數才直接使用信號量鍵,所有其他的信號量函數使用由semget函數返回的信號量標識符。如果多個程序使用相同的key值,key將負責協調工作。

第二個參數num_sems指定需要的信號量數目,它的值幾乎總是1。

第三個參數sem_flags是一組標誌,當想要當信號量不存在時創建一個新的信號量,可以和值IPC_CREAT做按位或操作。設置了IPC_CREAT標誌後,即使給出的鍵是一個已有信號量的鍵,也不會產生錯誤。而IPC_CREAT | IPC_EXCL則可以創建一個新的,唯一的信號量,如果信號量已存在,返回一個錯誤。

2  semop函數

它的作用是改變信號量的值,原型爲:

int semop(int sem_id, struct sembuf *sem_opa, size_tum_sem_ops);  

sem_id是由semget返回的信號量標識符,sembuf結構的定義如下:

struct sembuf{  

    short sem_num;//除非使用一組信號量,否則它爲0  

    short sem_op;//信號量在一次操作中需要改變的數據,通常是兩個數,一個是-1,P(等待)操作,一個是+1,即V(發送信號)操作。  

      short sem_flg;//通常爲SEM_UNDO,使操作系統跟蹤信號,並在進程沒有釋放該信號量而終止時,操作系統釋放信號量  

};  

3  semctl函數

該函數用來直接控制信號量信息,它的原型爲:

int semctl(int sem_id, int sem_num, int command, ...);  

如果有第四個參數,它通常是一個union semum結構,定義如下:

union semun{  

    int val;  

    struct semid_ds *buf;  

    unsigned short *arry;  

};  

前兩個參數與前面一個函數中的一樣,command通常是下面兩個值中的其中一個

SETVAL:用來把信號量初始化爲一個已知的值。這個值通過union semun中的val成員設置,其作用是在信號量第一次使用前對它進行設置。

IPC_RMID:用於刪除一個已經無需繼續使用的信號量標識符。

 

4.實驗程序及分析

實驗程序:

#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/sem.h>
#define KEY (key_t)14010322
union semun{  
    int val;  
    struct semid_ds *buf;  
    unsigned short *arry;  
};


static int sem_id = 0;

static int set_semvalue();
static void del_semvalue();
static void semaphore_p();
static void semaphore_v();

int main(int argc, char *argv[])
{
	//創建信號量,利用semget函數
	//初始化信號量,利用semctl函數
	int semid;
	int product = 1;
	int i;
	if((semid = semget(KEY,3,IPC_CREAT|0660))==-1)
	{
		printf("ERROR\n");
		return -1;
	}
	union semun arg[3];
	arg[0].val = 1;//mutex = 1;
	arg[1].val = 5;//empty = 5;
	arg[2].val = 0;//full = 0;

	for(i=0;i<3;i++)
	{
		semctl(semid,i,SETVAL,arg[i]);
	}

	for(i=0;i<3;i++)
	{
		printf("The semval(%d) = %d\n",i,semctl(semid,i,GETVAL,NULL));
	}

					pid_t p1,p2;
		      if((p1=fork())==0){
		         while(1){
							//生產者……………..
							semaphore_p(semid,1);//P(empty)
							printf("1\n");
							semaphore_p(semid,0);//P(mutex)
							printf("2\n");
							product++;
							printf("Producer %d: %d things",getpid(),product);
							semaphore_v(semid,0);//V(mutex);
							semaphore_v(semid,2);//V(full);
						
		         sleep(2);
		         }
		      }else{
		        if((p2=fork())==0){
		          while(1){
		             sleep(2);
							semaphore_p(semid,2);//p(full)
							printf("3\n");
							semaphore_p(semid,0);//p(mutex)
							printf("4\n");
							product--;
							printf("Consumer1 %d: %d things",getpid(),product);
							semaphore_v(semid,0);//v(mutex)
							semaphore_v(semid,1);//v(empty)
		             sleep(5);
		          }
		        }
						else{
		          while(1){
		             sleep(2);
							semaphore_p(semid,2);//p(full)
							printf("5\n");
							semaphore_p(semid,0);//p(mutex)
							printf("6\n");
							product--;
							printf("Consumer2 %d: %d things",getpid(),product);
							semaphore_v(semid,0);//v(mutex)
							semaphore_v(semid,1);//v(empty)
		             sleep(5);
		          }
		        }
		      }
}


static void del_semvalue()
{
	//刪除信號量
	union semun sem_union;

	if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
		fprintf(stderr, "Failed to delete semaphore\n");
}

static int set_semvalue()
{
	//用於初始化信號量,在使用信號量前必須這樣做
	union semun sem_union;
	sem_union.val = 1;
	if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
		return 0;
	return 1;
}

void semaphore_p(int sem_id,int semNum)
{
	//對信號量做減1操作,即等待P(sv)
	struct sembuf sem_b;
	sem_b.sem_num = semNum;
	sem_b.sem_op = -1;//P()
	sem_b.sem_flg = SEM_UNDO;
//	semop(semid,&sem_b,1);
	if(semop(sem_id, &sem_b, 1) == -1)
	{
		fprintf(stderr, "semaphore_p failed\n");
		return;
	}
	return;
}

void semaphore_v(int sem_id,int semNum)
{
	//這是一個釋放操作,它使信號量變爲可用,即發送信號V(sv)
	struct sembuf sem_b;
	sem_b.sem_num = semNum;
	sem_b.sem_op = 1;//V()
	sem_b.sem_flg = SEM_UNDO;
//	semop(semid,&sem_b,1);
	if(semop(sem_id, &sem_b, 1) == -1)
	{
		fprintf(stderr, "semaphore_p failed\n");
		return;
	}
	return ;
}

 

分析:

 

定義3個信號量,full,empty,mutex,分別表示產品個數,緩衝區空位個數,對緩衝區進行操作的互斥信號量,對應的初始化值分別爲0,5,1。

生產者:

P(empty)---->P(mutex)----->V(mutex)----->V(full)

消費者:

P(full)----->P(mutex)------->V(mutex)------->V(empty)

由此實現了緩衝區爲5,一個生產者和兩個消費者的同步。

 

 

5.實驗截圖

 

 

 

 

6.實驗心得體會

    此次實驗利用信號量實現了進程同步。其中用 semop函數具體實現了PV操作函數,並實現了一個生產者和兩個消費者之間的同步。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章