操作系統原理實驗-進程通信-綜合型

1.實驗目的

學習如何利用管道機制或消息緩衝隊列進行進程間的通信,並加深對上述通信機制的理解。提高學生分析問題和解決問題的能力,並學習撰寫規範的科學研究報告。

2.實驗內容和要求

實驗內容:

編寫一段程序,使用管道來實現父子進程之間的進程通信。子進程向父進程發送自己的進程表示符,以及某字符串。父進程則通過管道讀出子進程發來的消息,將消息顯示在屏幕上,然後終止。或者,編寫一段程序,使其用消息緩衝隊列來實現client和server進程之間的通信。

實驗要求:

(1)瞭解系統pipe() ,msgsnd(), msgrcv()的功能和實現過程。
(2)編寫一段程序,使其用管道來實現父子進程之間的進程通信。

3.主要儀器設備

儀器:PC機
實驗環境: Ubuntu16.04

4.預備知識

  1. 管道的概念

管道是連接讀寫進程的一個共享文件,允許進程以先進先出的方式寫入和讀出數據,並對讀寫操作進行同步。發送進程以字符流形式把大量數據送入管道尾部,接收進程從管道頭部接收數據。

  1. 管道讀寫進程之間的同步

(1)管道應該互斥使用,管道讀寫不能同時進行。一個進程正在執行管道寫入或讀出操作時,另一進程必須等待。讀寫結束時,喚醒等待進程,自己應阻塞。
(2)讀寫雙方必須能夠知道對方是否存在,只有存在才能通信。系統發送SIGPIPE信號通知進程對方不存在。
(3)管道空間的大小通常是固定的,讀寫操作時需要考慮寫滿和讀空問題。

  1. pipe函數
    創建管道:
int fds[2];
pipe (fds);

函數調用成功返回r/w兩個文件描述符,規定fd[0]->r;fd[1]->w。有點類似於0對應標準輸入,1對應標準輸出。向管道文件讀寫數據其實是在讀寫內核緩衝區。

  1. msgsnd函數
頭文件:
#include<sys/msg.h>
函數定義:
int msgsnd(int msgid, struct msgbuf *msgp, int msgsz, int msgflg);
功能:往隊列中發送一個消息
返回值:成功返回0,失敗返回-1;
參數:
msgid:消息標識id,也是msgget()函數的返回值。
msgp:指向消息緩衝區的指針,該結構體爲:
struct mymesg{
	long mtype;//消息類型
	char mtext[512];//存放的數據內容
}
msgsz:消息的字節大小,不包含消息類型的長度(4字節)
msgflg:可以設置爲0,控制消息進入消息隊列或返回進程中。
  1. msgrcv()函數
頭文件:
#include<sys/msg.h>
格式:
int msgrcv(int msgid, struct msgbuf *msgp, int msgsz, long mtype, int msgflg);
功能:讀取消息,並且把消息隊列中消息讀完。
返回值:成功返回0,失敗返回-1;
參數:
msgid:消息隊列的id號
msgp:我們要讀到的消息數據存儲消息的地址
msgsz:消息的長度
mtype:我們要從消息隊列中讀取的消息類型。如果此值爲0,則會讀取時間最長的那一條消息,即我們第一個寫入消息隊列中的消息。
msgflg:可以設置爲0,控制消息進入消息隊列或返回進程中。
  1. msgget()函數
	頭文件:#include<sys/msg.h>
	格式:int msgget(key_t key,int msgflg);
	功能:創建一個消息隊列或者取得一個消息隊列;
	返回值:成功則返回消息隊列的標識符,失敗返回-1;
	參數:
	key:消息隊列的鍵值,爲IPC_PRIVATE時創建一個只能被創建進程讀寫的消息隊列。IPC_CREAT如果內存不存在則創建一個消息隊列,否則直接取得。
  1. msgctl()函數
頭文件:#include<sys/msg.h>
格式:int msgctl(int msgid,int cmd,struct msgqid_ds *buf)
功能:對消息隊列的控制處理函數
返回值:成功返回0,失敗返回-1
參數:
msgid是msgget()函數的返回值
cmd 是對消息隊列的處理,如IPC_RMID是從系統內核中刪除隊列消息、IPC_STAT獲取消息隊列的詳細信息。IPC_SET設置消息隊列的信息。
buf是存放消息隊列狀態結構體的地址。

5.原理圖

在這裏插入圖片描述

  1. 父進程先調用pipe()創建管道,得到兩個文件描述符指向管道的讀端和寫端。
  2. 父進程fork()創建子進程,此時子進程也有兩個文件描述符指向同一管道。
  3. 父進程關閉管道寫端,子進程關掉管道讀端,從而實現子進程向管道里面寫入數據,父進程從管道里面讀取數據,從而實現了進程間通信。

6.實驗步驟與調試

  1. 使用pipe函數進行進程間通信
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/wait.h>
#include<sys/types.h>

int main()
{
	pid_t pid;
	char buf[1024];
	int fd[2];
	char *p1 = "this is child.\nAnd child's id is:";//定義字符數組p1
	
	if(-1==pipe(fd))
	{printf("pipe error.");}

	pid = fork();
	if(pid<0)
	{printf("fork error.");}
	
	else if(0==pid)
	{	
		int id = getpid();//獲得子進程序列號
		char *p2;
		sprintf(p2,"%d",id);//用sprintf把子進程序列號從int型轉爲char型數組
		close(fd[0]);//子進程關閉管道讀端
		write(fd[1],p1,strlen(p1));//子進程向管道寫入p1、p2
		write(fd[1],p2,strlen(p2));
		close(fd[1]);
	}
	
	else
	{
		close(fd[1]);//父進程關閉管道寫端
		int len = read(fd[0],buf,sizeof(buf));
		write(STDOUT_FILENO,buf,len);
		close(fd[0]);
	}
}

運行結果:
在這裏插入圖片描述

調試過程:
在使用pipe()進行管道通信時,只能使用write()函數向管道里面寫入數據。
write函數用法爲:write(int fd, const void *buf, size_t nbyte);
但是子進程序列號是int型,所以要用sprintf()函數把int型的id轉爲字符數組。

  1. 使用消息緩衝隊列進行進程間通信
利用消息緩衝隊列來實現server和client端的通信。
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/msg.h>
#define MSGKEY 75 //設置msgkey
struct msgform{//定義msgform結構體
	long mtype;
	char mtext[1024];
};

int main()
{
	int id;
	struct msgform msg;
	id = msgget(MSGKEY,0777|IPC_CREAT);//創建消息緩衝隊列
	
	if(-1==fork())
	{
		printf("fork error!\n");
	}

	else if(0==fork())
	{
		msg.mtype = 1;
		sprintf(msg.mtext,"%d",getpid());//獲取子進程標識符
		msgsnd(id,&msg,sizeof(msg),0);//子進程通信過程
		sleep(1);
		msgrcv(id,&msg,sizeof(msg),0,0);
		printf("receive reply form %s\n",msg.mtext);
	}
	else
	{
		msgrcv(id,&msg,sizeof(msg),0,0);//獲取消息
		if(1==msg.mtype)//父進程通信
		{
			printf("%d\n",getpid());
			printf("serving for client,%s\n",msg.mtext);
			sprintf(msg.mtext,"%d",getppid());
			msgsnd(id,&msg,sizeof(msg),0);
			sleep(2);
			msgctl(id,IPC_RMID,0);//刪除隊列消息
		}
	}
	return 0;
}

運行結果:
在這裏插入圖片描述

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