オペレーティングシステムの原理実験 - プロセスの同期

オペレーティングシステム原理実験報告書

 実験トピック実験  2 プロセスの同期   

実験 2、プロセスの同期

1.1実験の目的

  1. 最新のオペレーティング システムの中核はマルチプログラミング、マルチプロセッサ、分散プロセッサであり、これらのスキームとオペレーティング システム設計テクノロジの基礎は同時実行性です。マルチプロセッサ システムの場合でもシングルプロセッサ マルチプログラミング システムの場合でも、複数のプロセスが同時に実行されると、競合と協調の問題が発生します。
  2. オペレーティング システムの問題を解決するための相互排他と同期の使用について理解します。
  3. セマフォメカニズムを使用して、リーダー優先順位とライター優先順位のリーダーライター問題をそれぞれ実現し、相互排他と同期の基本概念を理解します。

1.2実験内容と要件

  1. Windows 環境では、n 個のスレッドを含むコンソール プロセスを作成します。これらの n 個のスレッドを使用して、n 個のリーダーまたはライターを表します。各スレッドは、対応するテスト データ ファイル (後述) の要件に従って読み取りおよび書き込み操作を実行します。セマフォ機構は、リーダー優先順位とライター優先順位のリーダーライター問題をそれぞれ実現するために使用されます。
  2. リーダー/ライター問題の読み取りおよび書き込み操作の制限 (リーダーファーストとライターファーストを含む):
  3. 1) 書き込みと書き込みの相互排他。つまり、2 人の書き込み者が同時に書き込みを行うことはできません。
  4. 2) 読み取り/書き込み相互排他。つまり、1 つのスレッドが読み取りを実行している間、別のスレッドが同時に書き込みを行うことはできません。
  5. 3) 読み取り/読み取りが許可されます。つまり、1 人以上のリーダーが読み取りを行うことができます。
  6. リーダーの優先順位に関する追加の制限: 別のリーダーが既に読み取りを行っているときにリーダーが読み取り操作を要求した場合、リーダーは読み取り操作を直接開始できます。
  7. ライターの優先順位に関する追加の制限: 別のライターが共有リソースへのアクセスを待機している間にリーダーが読み取り操作を要求した場合、リーダーは、待機中のライターがなくなるまで待機してから読み取り操作を開始する必要があります。
  8. 実行結果の表示要件: 各スレッドの作成時、読み書き操作アプリケーションの発行時、読み書き操作の開始時、および結果の読み書き操作の実行時に、プロンプト情報を 1 行表示する必要があります。すべてのプロセスが対応する読み取りおよび書き込み操作の制限に準拠していることを確認します。
  9. テスト データ ファイルには n 行のテスト データが含まれており、作成された n 個のスレッドがリーダーであるかライターであるか、および読み取りおよび書き込み操作の開始時刻と期間がそれぞれ記述されています。テスト データの各行には 4 つのフィールドが含まれており、各フィールドはスペースで区切られています。最初のフィールドは正の整数で、行プログラム番号を示します。2 番目のフィールドは対応するスレッドの役割を示し、R はリーダー、W はライターを示します。3 番目のフィールドは正の数値で、読み取りおよび書き込み操作の開始時間を示します。スレッドが作成された後、対応する遅延 (秒単位) の後に、共有リソースへの読み取りおよび書き込みの要求が発行されます。4 番目のフィールドは正の数で、読み取りおよび書き込み操作の継続時間を示します。スレッドがアプリケーションの読み取りと書き込みに成功すると、共有リソースの読み取りと書き込みが開始され、操作は対応する期間継続して終了し、共有リソースが解放されます。

テスト データ ファイルの例を次に示します。

1 R 3 5

                                                               2W45

3R52

4R65

                                                               5W5.13

1.3実験手法

  1. このラボを開始する前に、教科書の関連内容を確認してください。
  2. 次の準備が必要です。

1. Windows オペレーティング システムを実行しているコンピューター

2. Visual C Professional Edition または Enterprise Edition がコンピュータにインストールされている必要があります

1.4実験手順

(1) Windows 環境で、n 個のスレッドを含むコンソール プロセスを作成します。使用

これらの n 個のスレッドは、n 個のリーダーまたはライターを表します。各スレッドは、対応するテスト データ ファイルをプレスします (続いて、

はじめに) 読み取りおよび書き込み操作用。セマフォ メカニズムを使用して、それぞれリーダーファーストとライターファーストを実装します。

(2) リーダーライターの問題。リーダー/ライター問題の読み取りおよび書き込み操作の制限 (リーダーファーストとライターファーストを含む):

(3) テストデータファイルを使用して実験を実行し、結果を分析します

実験的なコードのフローチャートは次のとおりです。

 

1.5実験結果と分析手順

以下の図に示すように、サンプル コードをコンパイルし、選択インターフェイスから抜け出します。

 

テスト データ ファイルの例を次に示します。

1 R 3 5

2W45

3R52

4R65

5W5.13

コンテンツを txt ファイルに書き込み、サンプル コードのファイルを txt テキストの場所に変更します。

 

最初にファイルを読み取ることを選択し、つまり 1 を入力すると、実行結果は次のようになります。

 

プロセス:

① プロセス 1 は読み取りリクエストを送信し、読み取りファイルを入力します。

②プロセス 2 が書き込みリクエストを送信しますが、読み取りと書き込みは排他的であるため、書き込み操作は実行できません。

③ 処理 3 は読み込みリクエストを送信し、読み込んだファイルを入力します。

④ プロセス 5 が書き込み要求を送信しますが、読み取りと書き込みは排他的であるため、書き込み操作を実行できません。

⑤ 処理 4 は読み込みリクエストを送信し、読み込んだファイルを入力します。

⑥ 処理 3、1、4 が連続して読み出し動作を完了します。

⑦ 処理 2 は書き込み動作を開始し、終了します。

⑧プロセス5は書き込み動作を開始し、終了します。

書き込み優先度を選択、つまり 2 を入力すると、実行結果は次のようになります。

 

プロセス:

① プロセス 1 は書き込み要求を送信し、書き込みファイルに書き込みます。

② 処理 2 は書き込み要求を送信し、書き込みファイルに書き込みます。

③ プロセス 3、5、4 が書き込みリクエストを送信します。

④ プロセス 1 が終了し、プロセス 3 の書き込み動作が開始されます。

⑤ 処理 2 が終了し、処理 5 の書き込み動作が開始されます。

⑥ 処理 3 が終了し、処理 4 の書き込み動作が開始されます。

⑦5、4の工程が終了します。

実験結果の分析:

すべてのリーダーとすべてのライターを、それぞれリーダー待機キューとライター待機キューに格納します。読み取りが許可されるたびに、読み取り操作のために 1 つ以上のリーダー スレッドがリーダー キューから解放されます。書き込みが許可されるたびに、ライターからライターを解放するだけです。書き込みのキュー。

読者ファーストの状況:

    リーダーの優先順位は、ライターがファイルに書き込んでいない限り、リーダーが待つ必要がないことを意味します。したがって、整数変数を使用できます

数量read-count は、現在のリーダー数を記録します。これは、待機中のライター スレッドを解放するかどうかを決定するために使用されます (

read-count=0の場合、すべてのリーダーが読み取りを完了し、ライター待ちキュー内のライターを解放する必要があることを示します。それぞれ読んだ

リーダーがファイルの読み取りを開始するとき、read-count変数を変更する必要があります。したがって、グローバル変数read-countを変更するときに相互排他を実現するには、相互排他オブジェクトのミューテックスが必要です。

また、ライト-ライト排他を実現するには、クリティカルセクションオブジェクトwrite を追加する必要がありますライターが書き込みリクエストを発行するときは、クリティカル セクション オブジェクトの所有権を要求する必要があります。このようにして、read -count=1の場合、読み取り書き込みの相互排他も実現できます。

(つまり、最初のリーダーが到着したとき)、リーダー スレッドはクリティカル セクション オブジェクトの所有権も主張する必要があります。リーダーはクリティカル セクションの所有権を持っていますが、ライターはクリティカル セクション オブジェクトwriteをブロックしますライターがクリティカルセクションの所有権を持っている場合、最初のリーダーは「 read-count==1 」の判定後に書き込みをブロックし、残りのリーダーはread-countの判定を待っているためミューテックスでブロックされます

ライターファーストの場合:

ライターファーストはリーダーファーストと似ています。違いは、ライターが到着すると、できるだけ早くファイルに書き込む必要があり、待機中のライターがいる場合、新しく到着したリーダーは読み取りを許可されないことです。この目的のために、

タイプ変数write-count は、待機中のライターの数を記録するために使用されます。write-count=0の場合、待機中のリーダー スレッドのキューを解放できます。グローバル変数write-countの相互排他を実現するには、ミューテックス オブジェクトmutex3を追加する必要があります

ライターの優先順位を達成するには、クリティカル セクション オブジェクトの読み取りを追加する必要があります。ライターがファイルの書き込み中または待機中に、読み取りは行われません。

または、読み取り時にブロックする必要がありますグローバル変数read-countの操作で相互排他を実装することに加えて、リーダー スレッドにはブロッキング読み取りプロセスで相互排他を実装するための mutex オブジェクトも必要です。これら 2 つのミューテックス オブジェクトは、それぞれmutex1およびmutex2という名前になります

  

1.6実験体験(実験で遭遇した問題点と解決策

1) 実験の反省

サンプル コードは非常に長く、読みにくいですが、コード ロジックは厳密であり、プロセスの同期と相互排他をさらに理解しています。

2) 実験的収穫

1. すべてのリーダーとすべてのライターは、それぞれリーダー待機キューとライター待機キューに格納できます。読み取りが許可されると、読み取り操作のために 1 つ以上のリーダー スレッドがリーダー キューから解放されます。書き込みが許可されると、ライターは常に読み取り操作を実行します。書き込みのためにライターキューから解放されます。

2. プロセスの同期に関係するいくつかの API の機能を理解します。

①CreateThread関数:スレッドを作成します。

②CreateMutex関数:名前付きミューテックスオブジェクトを生成します。

③CreateSemaphore関数:名前付きまたは匿名のミューテックスオブジェクトを作成します。

④WaitForSingleObject関数:セマフォが出現するか最大待ち時間を超え、かつセマフォが1以上になるまでプログラムを待機させます。

⑤ReleaseSemaphore関数:指定されたサイズの量を、指されたセマフォに加算し、実行が成功した場合は0以外の値を返します。

⑥ReleaseMutex関数: ミューテックスをオープンするために使用されます。つまり、ミューテックスに1を追加し、呼び出しが成功した場合は0を返します。

⑦InitializeCriticalSection関数:クリティカルセクションオブジェクトを初期化します。

⑧EnterCriticalSection関数:指定されたクリティカルセクションオブジェクトの所有権を待ちます。

⑨LeaceCriticalSettion関数:指定されたクリティカルセクションオブジェクトポインタを解放します。

ソースコードの付録:




#include "windows.h"
#include <conio.h>
#include <stdlib.h>
#include <fstream.h>
#include <io.h>
#include <string.h>
#include <stdio.h>
#define READER 'R' // 读者
#define WRITER 'W' // 写者
#define INTE_PER_SEC 1000 // 每秒时钟中断数目。
#define MAX_THREAD_NUM 64 // 最大线程数目
#define MAX_FILE_NUM 32 // 最大数据文件数目
#define MAX_STR_LEN 32 // 字符串长度
int readcount=0; // 读者数目
int writecount=0; // 写者数目
CRITICAL_SECTION RP_Write; //临界区
CRITICAL_SECTION cs_Write;
CRITICAL_SECTION cs_Read;
struct ThreadInfo
{
int serial; // 线程序号
char entity; //线程类别(判断读者线程还是写者线程)
double delay;
double persist;
};
///
// 读者优先--读者线程
//p:读者线程信息
void RP_ReaderThread(void* p)
{
//互斥变量
HANDLE h_Mutex;
h_Mutex=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"mutex_for_readcount");
DWORD wait_for_mutex; //等待互斥变量所有权
DWORD m_delay; // 延迟时间
DWORD m_persist; // 读文件持续时间
int m_serial; //线程序号
//从参数中获得信息
m_serial=((ThreadInfo*)(p))->serial;
m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);
m_persist=(DWORD)(((ThreadInfo*)(p))->persist*INTE_PER_SEC);
Sleep(m_delay); //延迟等待
printf("Reader thread %d sents the reading require.\n",m_serial);
// 等待互斥信号,保证对 readcount 的访问、修改互斥
wait_for_mutex=WaitForSingleObject(h_Mutex,-1);
//读者数目增加
readcount++;
if(readcount==1)
{
 //第一个读者,等待资源
 EnterCriticalSection(&RP_Write);
}
ReleaseMutex(h_Mutex); //释放互斥信号
//读文件
printf("Reader thread %d begins to read file.\n",m_serial);
Sleep(m_persist);
// 退出线程
printf("Reader thread %d finished reading file.\n",m_serial);
//等待互斥信号,保证对 readcount 的访问、修改互斥
wait_for_mutex=WaitForSingleObject(h_Mutex,-1);
//读者数目减少
readcount--;
if(readcount==0)
{
 //如果所有读者读完,唤醒写者
 LeaveCriticalSection(&RP_Write);
}
ReleaseMutex(h_Mutex); //释放互斥信号
}



///
// 读者优先--写者线程
//p:写者线程信息
void RP_WriterThread(void* p)
{
DWORD m_delay; // 延迟时间
DWORD m_persist; // 写文件持续时间
int m_serial; //线程序号
//从参数中获得信息
m_serial=((ThreadInfo*)(p))->serial;
m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);
m_persist=(DWORD)(((ThreadInfo*)(p)) ->persist*INTE_PER_SEC);
Sleep(m_delay); //延迟等待
printf("Writer thread %d sents the writing require.\n",m_serial);
// 等待资源
EnterCriticalSection(&RP_Write);
//写文件
printf("Writer thread %d begins to Write to the file.\n",m_serial);
Sleep(m_persist);
// 退出线程
printf("Writer thread %d finished Writing to the file.\n",m_serial);
//释放资源
LeaveCriticalSection(&RP_Write);
}



///
// 读者优先处理函数
//file:文件名
void ReaderPriority(char* file)
{
DWORD n_thread=0; //线程数目
DWORD thread_ID; //线程 ID
DWORD wait_for_all; //等待所有线程结束
//互斥对象
HANDLE h_Mutex;
h_Mutex=CreateMutex(NULL,FALSE,"mutex_for_readcount");
//线程对象的数组
HANDLE h_Thread[MAX_THREAD_NUM];
ThreadInfo thread_info[MAX_THREAD_NUM];
readcount=0; // 初始化 readcount
InitializeCriticalSection(&RP_Write); //初始化临界区
ifstream inFile;
inFile.open("D:\\testdata.txt"); //打开文件
printf("Reader Priority:\n\n");
while(inFile)
{
 //读入每一个读者、写者的信息
 inFile>>thread_info[n_thread].serial;
 inFile>>thread_info[n_thread].entity;
 inFile>>thread_info[n_thread].delay;
 inFile>>thread_info[n_thread++].persist;
 inFile.get( );
}
for(int i=0;i< (int)(n_thread);i++)
{
 if(thread_info[i].entity==READER || thread_info[i].entity=='R')
 {
 
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RP_ReaderThread),&thread_info[i],0,&thread_ID); //创建读者线程
 }
else{
 //创建写者线程
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RP_WriterThread),&thread_info[i],0,&thread_ID);
 } 
}
//等待所有线程结束
wait_for_all=WaitForMultipleObjects(n_thread,h_Thread,TRUE,-1);
printf("All reader and writer have finished operating.\n");
}
///
// 写者优先--读者线程
//p:读者线程信息
void WP_ReaderThread(void* p)
{
//互斥变量
HANDLE h_Mutex1;
h_Mutex1=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"mutex1");
HANDLE h_Mutex2;
h_Mutex2=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"mutex2");
DWORD wait_for_mutex1; //等待互斥变量所有权
DWORD wait_for_mutex2;
DWORD m_delay; // 延迟时间
DWORD m_persist; // 读文件持续时间
int m_serial; //线程序号
//从参数中获得信息
m_serial=((ThreadInfo*)(p))->serial;
m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);
m_persist=(DWORD)(((ThreadInfo*)(p)) ->persist*INTE_PER_SEC);
Sleep(m_delay); //延迟等待
printf("Reader thread %d sents the reading require.\n",m_serial);
wait_for_mutex1= WaitForSingleObject(h_Mutex1,-1);
//进入读者临界区
 EnterCriticalSection(&cs_Read);
// 阻塞互斥对象 mutex2,保证对 readcount 的访问、修改互斥
wait_for_mutex2= WaitForSingleObject(h_Mutex2,-1);
//修改读者数目
readcount++;
if(readcount==1)
{
 //如果是第一个读者,等待写者写完
 EnterCriticalSection(&cs_Write);
}
ReleaseMutex(h_Mutex2); //释放互斥信号 mutex2
// 让其他读者进入临界区
LeaveCriticalSection(&cs_Write);
ReleaseMutex(h_Mutex1);
//读文件
printf("Reader thread %d begins to read file.\n",m_serial);
Sleep(m_persist);
// 退出线程
printf("Reader thread %d finished reading file.\n",m_serial);
// 阻塞互斥对象 mutex2,保证对 readcount 的访问、修改互斥
wait_for_mutex2= WaitForSingleObject(h_Mutex2,-1);
readcount--;
if(readcount==0)
{
 // 最后一个读者,唤醒写者
 LeaveCriticalSection(&cs_Write);
}
ReleaseMutex(h_Mutex2); //释放互斥信号
}
///
// 写者优先--写者线程
//p:写者线程信息
void WP_WriterThread(void* p)
{
DWORD m_delay; // 延迟时间
DWORD m_persist; // 写文件持续时间
int m_serial; //线程序号
DWORD wait_for_mutex3;
//互斥对象
HANDLE h_Mutex3;
h_Mutex3= OpenMutex(MUTEX_ALL_ACCESS,FALSE,"mutex3");
//从参数中获得信息
m_serial=((ThreadInfo*)(p))->serial;
m_delay=(DWORD)(((ThreadInfo*)(p))->delay*INTE_PER_SEC);
m_persist=(DWORD)(((ThreadInfo*)(p))->persist*INTE_PER_SEC);
Sleep(m_delay); //延迟等待
printf("Writer thread %d sents the writing require.\n",m_serial);
// 阻塞互斥对象 mutex3,保证对 writecount 的访问、修改互斥
wait_for_mutex3= WaitForSingleObject(h_Mutex3,-1);
writecount++; //修改读者数目
if(writecount==1)
{
 //第一个写者,等待读者读完
 EnterCriticalSection(&cs_Read);
}
ReleaseMutex(h_Mutex3);
//进入写者临界区
EnterCriticalSection(&cs_Write);
//写文件
printf("Writer thread %d begins to Write to the file.\n",m_serial);
Sleep(m_persist);
// 退出线程
printf("Writer thread %d finishing Writing to the file.\n",m_serial);
//离开临界区
LeaveCriticalSection(&cs_Write);
// 阻塞互斥对象 mutex3,保证对 writecount 的访问、修改互斥
wait_for_mutex3= WaitForSingleObject(h_Mutex3,-1);
writecount--; //修改读者数目
if(writecount==0)
{
 //写者写完,读者可以读
 LeaveCriticalSection(&cs_Read);
}
ReleaseMutex(h_Mutex3);
}
///
// 写者优先处理函数
//file:文件名
void WriterPriority(char* file)
{
DWORD n_thread=0; //线程数目
DWORD thread_ID; //线程 ID
DWORD wait_for_all; //等待所有线程结束
//互斥对象
HANDLE h_Mutex1;
h_Mutex1=CreateMutex(NULL,FALSE,"mutex1");
HANDLE h_Mutex2;
h_Mutex2=CreateMutex(NULL,FALSE,"mutex2");
HANDLE h_Mutex3;
h_Mutex3=CreateMutex(NULL,FALSE,"mutex3");
//线程对象
HANDLE h_Thread[MAX_THREAD_NUM];
ThreadInfo thread_info[MAX_THREAD_NUM];
readcount=0; // 初始化 readcount
writecount=0; // 初始化 writecount
InitializeCriticalSection(&cs_Write); //初始化临界区
InitializeCriticalSection(&cs_Read);
ifstream inFile;
inFile.open("D:\\testdata.txt"); //打开文件
printf("Writer Priority:\n\n");
while(inFile)
{
 //读入每一个读者、写者的信息
 inFile>>thread_info[n_thread].serial;
 inFile>>thread_info[n_thread].entity;
 inFile>>thread_info[n_thread].delay;
 inFile>>thread_info[n_thread++].persist;
 inFile.get( );
}
for(int i=0;i< (int)(n_thread);i++)
{
 if (thread_info[i].entity==READER || thread_info[i].entity=='R')
 {
 //创建读者线程
 
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(RP_WriterThread),&thread_info[i],0,&thread_ID);
 }
else{
 //创建写者线程
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(WP_WriterThread),&thread_info[i],0,&thread_ID);
 }
}
//等待所有线程结束
wait_for_all=WaitForMultipleObjects(n_thread,h_Thread,TRUE,-1);
printf("All reader and writer have finished operating.\n");
}
///
//主函数
int main(int argc,char* argv[])
{
char ch;
while (true)
{
 //打印提示信息
 printf("************************************************\n");
 printf(" 1:Reader Priority\n");
 printf(" 2:Writer Priority\n");
 printf(" 3:Exit Priority\n");
 printf("************************************************\n");
 printf("Enter your choice(1,2 or 3): ");
 //如果输入信息不正确,继续输入
 do{
 ch=(char)_getch( );
 }while(ch != '1' &&ch != '2' && ch != '3');
 system("cls");
 //选择 3,返回
 if(ch=='3')
 return 0;
 //选择 1,读者优先
 else if(ch=='1')
 ReaderPriority("thread.dat");
 //选择 2,写者优先
 else if(ch=='2')
 WriterPriority("thread.dat");
 //结束
 printf("\nPress Any Key To Continue: ");
 _getch( );
 system("cls");
}
return 0;
}

おすすめ

転載: blog.csdn.net/cangzhexingxing/article/details/124856879