日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

操作系统实验报告8:进程间通信—消息机制

發布時間:2024/6/3 windows 57 豆豆
生活随笔 收集整理的這篇文章主要介紹了 操作系统实验报告8:进程间通信—消息机制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

操作系統實驗報告8

實驗內容

  • 實驗內容1:進程間通信—消息機制

    • 編譯運行課件 Lecture 09 例程代碼:

      • Algorithms 9-1 ~ 9-2.

      • 修改代碼,觀察在 msgsnd 和 msgrcv 并發執行情況下消息隊列的變化情況。

  • 實驗內容2:

    • 仿照 alg.8-4~8-6,編制基于 POSIX API 的進程間消息發送和消息接收例程。

實驗環境

  • 架構:Intel x86_64 (虛擬機)
  • 操作系統:Ubuntu 20.04
  • 匯編器:gas (GNU Assembler) in AT&T mode
  • 編譯器:gcc

技術日志

實驗內容1:進程間通信—消息機制

  • 驗證實驗Algorithms 9-1 ~ 9-2

分析:

實驗內容原理:

消息隊列是在消息的傳輸過程中保存消息的容器,消息被發送到隊列中,消息隊列管理器在將消息從它的源中繼到它的目標時充當中間人。隊列的主要目的是提供路由并保證消息的傳遞;如果發送消息時接收者不可用,消息隊列會保留消息,直到可以成功地傳遞它,可以傳送多種類型的數據,但是有大小限制。

首先,進程9-1 msgsnd會根據一個IPC 鍵值使用msgget()函數,創建一個消息隊列,然后使用msgctl()函數獲取和設置消息隊列的屬性,再使用msgsnd()函數向消息隊列中發送信息,這些消息每條包含一個消息類型號和消息內容,進程9-1 msgsnd結束后,發送的消息還留在消息隊列中,并沒有被釋放。

然后,進程9-2 msgrcv會根據同一個IPC 鍵值使用msgget()函數獲取之前創建的同一個消息隊列,然后使用msgctl()函數獲取和設置消息隊列的屬性,再使用msgrcv()函數從消息隊列中根據消息類型號獲取信息,每次將選擇的消息類型號的所有信息獲取出來,如果消息類型號為0,那么將消息隊列中的全部信息獲取出來,并彈出提示是否要刪除這個空的消息隊列,若是則該消息隊列被釋放,當消息隊列中還有信息時,進程9-2 msgrcv結束并不會釋放消息隊列,消息隊列中的消息也不會被刪除。

首先執行文件alg.9-1-msgsnd.c

執行程序命令:

gcc alg.9-1-msgsnd.c ./a.out /home/ubuntu/myshm

執行截圖:

實現細節解釋:

首先進入一個條件判斷語句,如果argc < 2,那么說明編譯命令出錯,打印Usage: ./a.out pathname提示我們補全路徑名,以便之后根據文件信息使用ftok()函數獲得IPC 鍵值,并返回EXIT_FAILURE表示異常退出。

如果編譯命令正確,那么繼續往下執行,如果獲取文件信息失敗,進行異常處理,使用creat(pathname, O_RDWR)函數在原來文件路徑處創建一個新的同名可讀可寫文件,并把creat()函數的返回值賦給ret,如果ret為-1,說明創建失敗,使用宏定義ERR_EXIT("creat()")打印錯誤信息和原因,否則打印語句shared file object created,說明創建成功。

接著,通過ftok(pathname, 0x27)獲得一個IPC 鍵值,ftok()函數會根據參數pathname的文件信息和序號參數0x27的計劃編號合成IPC key鍵值并賦給變量key,從而避免用戶使用key值時發生沖突。如果key值小于0,說明ftok()失敗,使用宏定義ERR_EXIT("ftok()")打印錯誤信息,否則打印IPC key = 0x后面跟生成的IPC 鍵值,顯示生成的IPC 鍵值。

接下來,使用msgget((key_t)key, 0666 | IPC_CREAT)創建一個新的或者打開一個已經存在的消息隊列,這個消息隊列與key值相對應,并把隊列標識符作為返回值賦給變量msqid,其中參數(key_t)key為轉換為(key_t)類型的之前通過ftok()函數得到的IPC鍵值,參數(0666 | IPC_CREAT)表示進程對消息隊列可讀可寫,IPC_CREAT表示如果消息隊列不存在,便創建消息隊列,否則就進行打開操作。

如果msqid的值為-1,說明msgget()失敗,使用宏定義ERR_EXIT("msgget()")打印錯誤信息,否則繼續向下。

然后使用fopen("./msgsnd.txt", "rb")以只讀方式打開目錄下的二進制文件alg.9-0-msgsnd.txt,并把指向該文件流的指針賦給fp,如果fp為空指針,說明fopen()失敗,文件打開失敗,使用宏定義ERR_EXIT("source data file: ./msgsnd.txt fopen()")打印錯誤信息,否則繼續向下。

接著,聲明一個struct msqid_ds類型的消息隊列管理結構體變量msqattr,使用msgctl(msqid, IPC_STAT, &msqattr)獲取和設置消息隊列的屬性,這個函數與共享內存的shmctl函數相似,其中參數msqid是隊列標識符,參數IPC_STAT表示把后一個參數&msqattr中的數據設置為消息隊列的當前關聯值,函數的返回值賦給ret。

然后打印消息隊列中存有的信息條數以及消息隊列中還剩下的空位數目。

接著進入一個while循環,檢測文件指針fp指向的文件流,如果文件結束,那么結束while循環。進入while循環后,一開始執行fscanf(fp, "%ld %s", &msg_type, buffer);語句,將文件每一行中開頭的數字輸入進msg_type,后面的姓名字符串輸入到buffer,并把fscanf函數的返回值賦給ret,如果ret變量等于EOF,說明文件已經到末尾了,退出while循環,否則繼續。

打印msg_type和buffer中的內容,即文件中每一行的消息類型號和字符串信息,把msg_type賦給struct msg_struct類型變量data的成員變量msg_type,把buffer賦給data的成員變量mtext,使用語句msgsnd(msqid, (void *)&data, TEXT_SIZE, 0)將消息寫入消息隊列中,其中參數msqid是消息隊列的標識符,參數data可以是任何類型的結構體,第一個字段為long類型,表明此發送消息的類型,參數TEXT_SIZE表示要發送的消息的大小,最后一個參數0表示當消息隊列滿時,函數將會阻塞,直到消息能寫進消息隊列,并把返回值賦給ret,如果ret為-1,說明msgsnd()失敗,使用宏定義ERR_EXIT("msgsnd()")打印錯誤信息,否則繼續向下,統計發送消息總條數的count加一,繼續執行while循環。

從while循環退出后,打印發送消息的總條數,關閉文件指針fp指向的文件流,使用系統命令ipcs -q查看使用消息隊列進行進程間通信的信息,最后exit(EXIT_SUCCESS)正常退出。

執行文件alg.9-2-msgrcv.c

執行程序命令:

gcc -o b.out alg.9-2-msgrcv.c ./b.out /home/ubuntu/myshm 2./b.out /home/ubuntu/myshm 0

實現細節解釋:

首先進入一個條件判斷語句,如果argc < 2,那么說明編譯命令出錯,打印Usage: ./b.out pathname msg_type提示我們補全路徑名,以便之后根據文件信息使用ftok()函數獲得IPC 鍵值,并返回EXIT_FAILURE表示異常退出。

如果編譯命令正確,那么繼續往下執行,如果獲取文件信息失敗,進行異常處理,使用宏定義ERR_EXIT("shared file object stat error")打印錯誤信息,否則繼續向下執行。

接著,通過ftok(pathname, 0x27)獲得一個IPC 鍵值,ftok()函數會根據參數pathname的文件信息和序號參數0x27的計劃編號合成IPC key鍵值并賦給變量key,從而避免用戶使用key值時發生沖突。如果key值小于0,說明ftok()失敗,使用宏定義ERR_EXIT("ftok()")打印錯誤信息,否則繼續向下執行,打印IPC key = 0x后面跟生成的IPC 鍵值,顯示生成的IPC 鍵值。

接下來,使用msgget((key_t)key, 0666)創建一個新的或者打開一個已經存在的消息隊列,這個消息隊列與key值相對應,并把隊列標識符作為返回值賦給變量msqid,其中參數key為IPC鍵值,后一個參數為0666,而不是(0666 | IPC_CREAT),因為之前已經創建過一個消息隊列了,所以不用再創建一個新的消息隊列了。

如果msqid的值為-1,說明msgget()失敗,使用宏定義ERR_EXIT("msgget()")打印錯誤信息,否則繼續向下。

如果argc < 3,那么說明沒有指定msgtype變量的值,那么默認該值為0,否則將argv[2]中的值賦給msgtype變量,如果這個值小于0,那么msgtype變量的值賦為0,打印語句Selected message type =和msgtype的值,顯示msgtype的值。

接著進入一個while循環,,一開始執行msgrcv(msqid, (void *)&data, TEXT_SIZE, msgtype, IPC_NOWAIT)語句,從消息隊列中讀取消息,然后把此消息從消息隊列中刪除,其中參數msqid是消息隊列的標識符,參數data是存放消息的結構體,結構體類型要與msgsnd()函數發送的類型相同,參數TEXT_SIZE表示要接收的消息的大小,參數msgtype為0時表示接收第一個消息,大于0表示接收等于msgtype的第一個消息,最后一個參數IPC_NOWAIT表示如果沒有返回條件的信息調用立即返回,函數返回值賦給ret,如果ret為-1,說明該種msgtype類型的信息已經讀取完了,打印讀取的信息數,并退出while循環,否則繼續執行。打印讀取到的消息的消息類型號和內容,代表讀取的信息數量的count變量數值加一,繼續while循環。

退出while循環后,聲明一個struct msqid_ds類型的變量msqattr,然后使用msgctl(msqid, IPC_STAT, &msqattr)獲取和設置消息隊列的屬性,把參數&msqattr中的數據設置為消息隊列的當前關聯值,函數的返回值賦給ret。

打印此時消息隊列中存有的消息條數,如果消息條數等于0,打印語句do you want to delete this msg queue?(y/n),并進入選擇判斷語句if (getchar() == 'y'),如果接下來輸入的字符為'y',那么使用msgctl(msqid, IPC_RMID, 0)語句刪除消息隊列,如果函數的返回值為-1,說明msgctl()失敗,使用perror("msgctl(IPC_RMID)")打印錯誤信息,否則繼續向下。

使用系統命令ipcs -q打印使用消息隊列進行進程間通信的信息,最后exit(EXIT_SUCCESS)正常退出。

執行截圖:

可以看到,當輸入的msgtype值為2時,消息隊列中兩個msgtype值為2的消息被取出,消息隊列中的消息數從8變成6,當輸入的msgtype值為0時,因為每次都會不區分類型地將消息隊列中的第一條消息取出,所以最后全部消息被取出,消息隊列為空,輸入’y’刪除這個空的消息隊列。

  • 修改代碼,觀察在 msgsnd 和 msgrcv 并發執行情況下消息隊列的變化情況。

實驗內容原理:

首先,進程msgsnd先被執行,使用一個文件路徑名和整數標識符通過ftok()獲得一個IPC鍵值,利用這個IPC鍵值創建一個消息隊列,然后使用vfork()和execv()將創建IPC鍵值時使用的文件路徑名傳給msgrcv進程,msgrcv進程通過同樣的文件路徑名和整數表標識符獲得同樣的IPC鍵值,通過這個IPC鍵值獲得同一個消息隊列。

此時msgrcv進程和msgsnd進程并發執行,在父進程msgsnd進程中,每休眠2s,向消息隊列發送一次消息,在子進程msgrcv進程中,每休眠3s,從消息隊列中讀取一次消息,消息隊列中的消息被一邊寫一邊讀,寫的速度比讀快,不會出現先讀完還有消息未寫入進程msgrcv就退出的情況。

當最后消息寫完時,msgsnd進程的while循環先退出,消息不再寫入,等待msgrcv進程結束,到了消息讀完時,msgrcv進程的while循環退出,彈出提示是否刪除消息隊列的提示,進行完操作后,msgrcv進程結束,然后msgsnd進程結束,程序退出。

執行程序命令:

gcc -o msgrcv.o msgrcv.c gcc msgsnd.c ./a.out /home/ubuntu/myshm

執行截圖:

分析:

可以看到,當有消息寫入消息隊列時,消息隊列的消息數加一,當有消息從消息隊列中讀出時,消息隊列的消息數減一。

實驗內容2:仿照 alg.8-4~8-6,編制基于 POSIX API 的進程間消息發送和消息接收例程。

實驗內容原理:

基于 POSIX API 的進程間消息發送和消息接收使用消息隊列進行,首先要在程序中包含頭文件#include <mqueue.h>,在這個頭文件中:

#include <bits/types.h>typedef int mqd_t;struct mq_attr { __syscall_slong_t mq_flags; /* Message queue flags. */ __syscall_slong_t mq_maxmsg; /* Maximum number of messages. */ __syscall_slong_t mq_msgsize; /* Maximum message size. */ __syscall_slong_t mq_curmsgs; /* Number of messages currently queued. */ __syscall_slong_t __pad[4]; };

可以看到mqd_t類型實際為int型,消息隊列屬性mq_attr的結構中,__syscall_slong_t實際上為long型,所有的成員變量都為長整型,mq_flags代表消息隊列的標志,0為阻塞模式,O_NONBLOCK為非阻塞模式,mq_maxmsg代表最大消息數,mq_msgsize代表每個消息最大的字節數,mq_curmsgs代表當前的消息數目。

使用POSIX標準規定的函數在進程操作消息隊列,相關的函數有:

mq_open()用于打開或創建一個消息隊列。mq_getattr()用于獲取當前消息隊列的屬性mq_send()用于向消息隊列中寫入一條消息mq_receive()用于從消息隊列中讀取一條消息mq_close()用于關閉一個消息隊列mq_unlink()用于刪除一個消息隊列

實現細節解釋:

首先在進程msgpthreadcon中使用語句mq_open(pathname, O_CREAT|O_RDWR, 0666, 0)根據文件路徑名創建一個可讀可寫的消息隊列,其中參數
pathname為消息隊列的名字,O_CREAT|O_RDWR為打開的方式,這里為可讀可寫,沒有則創建,0666代表默認訪問權限為可讀可寫,0代表采取默認屬性。然后創建msgproducer和msgconsumer兩個子進程,并發執行,實現通信,同時把創建消息隊列時用到的文件路徑名傳給這兩個進程。

在進程msgproducer中:

在進程msgproducer中,首先使用語句mq_open(argv[1], O_RDWR, 0666)根據和父進程相同的文件路徑名獲取同一個消息隊列,然后聲明一個struct mq_attr的變量mqAttr,用來獲取消息隊列的屬性。

然后進入一個while循環,在while循環中,每次一開始便使用mq_getattr(mqid, &mqAttr)獲取消息隊列的屬性到mqAttr,如果消息隊列中還有消息,那么每次進程休眠1s,等待消息讀出后再讀入消息,從鍵盤讀入消息到字符型數組buffer后,使用mq_send(mqid, buffer, mqAttr.mq_msgsize, 0)函數將buffer中的內容送到消息隊列,其中參數mqid為消息隊列的標識符,buffer為要傳送的內容,mqAttr.mq_msgsize消息的長度,0為消息的優先級,這里表示不設置優先級。

如果輸入的消息為"end",那么退出循環,使用mq_close(mqid)關閉消息隊列,退出進程。

在進程msgconsumer中:

首先使用語句mq_open(argv[1], O_RDWR, 0666)根據和父進程相同的文件路徑名獲取之前創建的消息隊列,然后聲明一個struct mq_attr的變量mqAttr,用來獲取消息隊列的屬性。

然后進入一個while循環,在while循環中,每次一開始便使用mq_getattr(mqid, &mqAttr)獲取消息隊列的屬性到mqAttr,如果消息隊列中沒有消息,那么每次進程休眠1s,等待消息寫入后再讀出消息,使用mq_receive(mqid, buffer, mqAttr.mq_msgsize, 0)函數將消息隊列中的消息讀入字符型數組buffer中,其中參數mqid為消息隊列的標識符,buffer為要讀入的字符型數組,mqAttr.mq_msgsize消息的長度,0為消息的優先級,這里表示不設置優先級。

如果讀出的消息為"end",那么退出循環,使用mq_close(mqid)關閉消息隊列,退出進程。

兩個進程都執行完后,回到進程msgpthreadcon重新執行,使用語句mq_unlink(argv[1])刪除消息隊列,程序退出。

執行程序命令:

gcc msgpthreadcon.c -lrt gcc -o msgproducer.o msgproducer.c -lrt gcc -o msgconsumer.o msgconsumer.c -lrt ./a.out /mymsg

執行截圖:

分析:

可以看到,Producer輸入什么,Consumer就輸出什么,每次輸入的內容和輸出的內容一樣,實現了Prodecer和Consumer之間的通信,當輸入的內容為end時,通信程序結束。

總結

以上是生活随笔為你收集整理的操作系统实验报告8:进程间通信—消息机制的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。