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操作函數,並實現了一個生產者和兩個消費者之間的同步。