1. 程式人生 > >Linux程序建立及同步實驗

Linux程序建立及同步實驗

用full、empty和mutex分別表示倉庫的庫存的同步訊號量、倉庫為空的同步訊號量和正在對倉庫進行操作的互斥訊號量。其初值分別為0、倉庫的容量(程式中使用MAX_BUFFRT_SIZE表示)和0。生產者:p(empty) -> p(mute) -> v(mutex) -> v(full)   消費者:p(full) -> p(mutex) -> v(mutex) ->v(empty)。

具體流程圖:


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/types.h>
 
#define MAX_BUFFER_SIZE 10
#define SHM_MODE 0600
#define SEM_MODE 0600
 
#define SEM_FULL 0
#define SEM_EMPTY 1
#define MUTEX 2
/*
#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
//	 union   semun   is   defined   by   including   <sys/sem.h>   
#else 
//   according   to   X/OPEN   we   have   to   define   it   ourselves  
union semun{
	int val;
	struct semid_ds *buf;
	unsigned short *array;
};
#endif
union semun su;//sem union,用於初始化訊號量
*/
 
struct my_buffer
{
	int head;
	int tail;
	char str[MAX_BUFFER_SIZE];
	int num;  //緩衝區裡字母數量
	int is_empty;
};
 
const int N_CONSUMER = 2;//消費者數量
const int N_PRODUCER = 2;//生產者數量
const int N_BUFFER = 10;//緩衝區容量
const int N_WORKTIME = 10;//工作次數
int shm_id = -1;
int sem_id = -1;
pid_t child;
pid_t parent;
 
//得到10以內的一個隨機數  
int get_random()  
{  
    int digit;  
    srand((unsigned)(getpid() + time(NULL)));  
    digit = rand() % 10;  
    return digit;  
}  
 
//得到A~Z的一個隨機字母  
char getRandChar()
{
    char letter;  
    srand((unsigned)(getpid() + time(NULL)));  
    letter = (char)((rand() % 26) + 'A');  
    return letter;  
}
 
//sem_id 表示訊號量集合的 id
//sem_num 表示要處理的訊號量在訊號量集合中的索引
//P操作
void waitSem(int sem_id,int sem_num)
{
	struct sembuf sb;
	sb.sem_num = sem_num;
	sb.sem_op = -1;//表示要把訊號量減一
	sb.sem_flg = SEM_UNDO;//
	//第二個引數是 sembuf [] 型別的,表示陣列
	//第三個引數表示 第二個引數代表的陣列的大小
	if(semop(sem_id,&sb,1) < 0){
		perror("waitSem failed");
		exit(1);
	}
}
 
//V操作
void sigSem(int sem_id,int sem_num)
{
	struct sembuf sb;
	sb.sem_num = sem_num;
	sb.sem_op = 1;
	sb.sem_flg = SEM_UNDO;
	//第二個引數是 sembuf [] 型別的,表示陣列
	//第三個引數表示 第二個引數代表的陣列的大小
	if(semop(sem_id,&sb,1) < 0){
		perror("sigSem failed");
		exit(1);
	}
}
 
//列印程序執行結果
void printTime()
{
	//列印時間
	time_t now;
	struct tm *timenow;         //例項化tm結構指標
	time(&now);
	timenow = localtime(&now);
	printf("執行時間: %s ",asctime(timenow));
}
 
int main(int argc, char ** argv)
{
	shm_id = shmget(IPC_PRIVATE,MAX_BUFFER_SIZE,SHM_MODE);   //申請共享記憶體
	if(shm_id < 0)
	{
		perror("create shared memory failed");
		exit(1);
	}
 
	struct my_buffer *shmptr;  
	shmptr = shmat(shm_id, 0, 0);   //將申請的共享記憶體附加到申請通訊的程序空間
	if (shmptr == (void*)-1)
	{  
        perror("add buffer to using process space failed!\n");  
        exit(1);  
    }  
 
	if((sem_id = semget(IPC_PRIVATE,3,SEM_MODE)) < 0)
	{                  								//建立三個訊號量,SEM_EMPTY,SEM_FULL和MUTEX
		perror("create semaphore failed! \n");
		exit(1);
	}
 
	if(semctl(sem_id,SEM_FULL,SETVAL,0) == -1)
	{												//將索引為0的訊號量設定為0-->SEM_FULL
		perror("sem set value error! \n");		
		exit(1);
	}
 
	if(semctl(sem_id,SEM_EMPTY,SETVAL,10) == -1)
	{												//將索引為1的訊號量設定為10-->SEM_EMPTY
	 	perror("sem set value error! \n");
	 	exit(1);
	}
	if(semctl(sem_id,MUTEX,SETVAL,1) == -1)
	{												//將索引為3的訊號量設定為1-->MUTEX
	 	perror("sem set value error! \n");
	 	exit(1);
	}
 
	shmptr -> head = 0;  
    shmptr -> tail = 0;  
    shmptr -> is_empty = 1;  
    shmptr -> num = 0;
 
	for(int i = 0; i < N_PRODUCER; i++)
	{
		parent = fork();
		if(parent < 0)
		{
			perror("the fork failed");
			exit(1);
		}
		else if(parent == 0)
		{
			shmptr = shmat(shm_id, 0, 0);   //將申請的共享記憶體附加到申請通訊的程序空間
			if (shmptr == (void*)-1)
			{  
        		perror("add buffer to using process space failed!\n");  
        		exit(1);  
    		}  
			int count = 0;
			for(int j = 0; j < N_WORKTIME; j++)
			{
				waitSem(sem_id, SEM_EMPTY);
				waitSem(sem_id, MUTEX);
				sleep(get_random()); 
 
				printf("-------------------------------------------------------------\n");
				printf("我是第 %d 個生產者程序,PID = %d\n", i + 1, getpid());
 
				/*生產產品*/
				char c = getRandChar();                                      //隨機獲取字母
				shmptr -> str[shmptr->tail] = c;
                shmptr -> tail = (shmptr->tail + 1) % MAX_BUFFER_SIZE;  
                shmptr -> is_empty = 0;           //寫入新產品  
				shmptr -> num++;
 
				/*列印輸出結果*/
				printTime();              //程式執行時間
 
				int p;
				printf("緩衝區資料(%d個):",shmptr -> num);                   //列印緩衝區中的資料
				p = (shmptr->tail-1 >= shmptr->head) ? (shmptr->tail-1) : (shmptr->tail-1 + MAX_BUFFER_SIZE);  
                for (p; !(shmptr -> is_empty) && p >= shmptr -> head; p--)  
                {  
                    printf("%c", shmptr -> str[p % MAX_BUFFER_SIZE]);  
                }  
                printf("\t 生產者 %d  放入 '%c'. \n", i + 1, c);  
				printf("-------------------------------------------------------------\n");
 
				fflush(stdout);
				sigSem(sem_id, MUTEX);
				sigSem(sem_id, SEM_FULL);
			}
			//將共享段與程序之間解除連線  
            shmdt(shmptr);  
            exit(0); 
		} 
	}
 
	for(int i = 0; i < N_CONSUMER; i++)
	{
		child = fork();
		if(child < 0)//呼叫fork失敗
		{
			perror("the fork failed");
			exit(1);
		}
		else if(child == 0)
		{
			int count = 0; 
			shmptr = shmat(shm_id, 0, 0);   //將申請的共享記憶體附加到申請通訊的程序空間
			if (shmptr == (void*)-1)
			{  
        		perror("add buffer to using process space failed!\n");  
        		exit(1);  
    		} 
			for(int j = 0; j < N_WORKTIME; j++)
			{
 
				waitSem(sem_id, SEM_FULL);
				waitSem(sem_id, MUTEX);
				sleep(get_random()); 
 
				printf("-------------------------------------------------------------\n");
				printf("我是第 %d 個消費者程序,PID = %d\n", i + 1, getpid());
				/*消費資料*/
				char lt = shmptr -> str[shmptr -> head];  
                shmptr -> head = (shmptr -> head + 1) % MAX_BUFFER_SIZE;  
                shmptr -> is_empty = (shmptr->head == shmptr->tail);  //
				shmptr -> num--;
				/*列印輸出結果*/
				printTime(); //程式執行時間
 
				int p;
				printf("緩衝區資料(%d個):",shmptr -> num);                   //列印緩衝區中的資料
				p = (shmptr -> tail - 1 >= shmptr -> head) ? (shmptr -> tail-1) : (shmptr -> tail - 1 + MAX_BUFFER_SIZE);  
                for (p; !(shmptr -> is_empty) && p >= shmptr -> head; p--)  
                {  
                    printf("%c", shmptr -> str[p % MAX_BUFFER_SIZE]);  
                }  
                printf("\t 消費者 %d  取出 '%c'. \n", i + 1, lt);  
				printf("-------------------------------------------------------------\n");
 
				fflush(stdout);
				sigSem(sem_id,MUTEX);
				sigSem(sem_id,SEM_EMPTY);
			}
			//將共享段與程序之間解除連線  
        	shmdt(shmptr);  
        	exit(0);
		}  
	}
 
	    //主程序最後退出  
    while (wait(0) != -1);  
    //將共享段與程序之間解除連線  
    shmdt(shmptr);  
    //對共享記憶體區執行控制操作  
    shmctl(shm_id,IPC_RMID,0);//當cmd為IPC_RMID時,刪除該共享段  
    shmctl(sem_id,IPC_RMID,0);  
    printf("主程序執行結束!\n");  
    fflush(stdout);  
    exit(0); 
	return 0;
}

最終執行結果:

以上就是4個綜合題目的具體實現,其實每個題目都不復雜,只要搞懂了原理,耐住性子還是可以做出來的。不懂的話就要記得多查資料,努力去理解,希望每一個人都可以體會到Linux的別樣美!