操作系统 实验三 进程同步的经典算法
操作系统 实验三 进程同步

集美大学计算机工程学院实验报告课程名称:操作系统指导教师:王丰实验成绩:实验编号:实验三实验名称:进程同步班级:计算12姓名:学号:上机实践日期:2015.5上机实践时间:2学时一、实验目的1、掌握用Linux信号灯集机制实现两个进程间的同步问题。
2、共享函数库的创建二、实验环境Ubuntu-VMware、Linux三、实验内容⏹需要的信号灯: System V信号灯实现☐用于控制司机是否可以启动车辆的的信号灯 S1=0☐用于控制售票员是否可以开门的信号灯 S2=0System V信号灯实现说明□ System V的信号灯机制属于信号灯集的形式, 一次可以申请多个信号灯.□同样利用ftok()生成一个key: semkey=ftok(path,45);□利用key申请一个包含有两个信号灯的信号灯集, 获得该集的idsemid=semget(semkey,2,IPC_CREAT | 0666);□定义一个联合的数据类型union semun{int val;struct semid_ds *buf;ushort *array;};□利用semctl()函数对信号灯初始化,参数有:信号灯集的id: semid要初始化的信号灯的编号:sn要设定的初始值:valvoid seminit(int semid, int val,int sn){union semun arg;arg.val=val;semctl(semid,sn,SETVAL,arg);}利用初始化函数,初始化信号灯:seminit(semid,0,0);//用来司机启动汽车的同步seminit(semid,0,1);//用来售票员开门的同步控制□利用semop()函数, 对信号灯实现V操作:sembuf是一个在头部文件中的预定义结构、semid—信号灯集id, sn—要操作的信号灯编号void semdown(int semid,int sn){/* define P operating*/struct sembuf op;op.sem_num=sn;op.sem_op=-1;//P操作为-1op.sem_flg=0;semop(semid,&op,1);}2、Linux的静态和共享函数库·Linux生成目标代码: gcc -c 源程序文件名(将生成一个与源程序同名的.o目标代码文件。
进程间同步的几种方法

进程间同步的几种方法进程间同步是指两个或多个进程之间进行协调,以确保它们能够正确地执行。
这是多任务操作系统中的重要问题,因为进程之间共享资源,包括内存、文件和网络连接等。
进程同步的关键是确保一组进程在处理共享资源时,能够避免发生竞态条件(Race Condition)和死锁(Deadlock)。
竞态条件指多个进程同时访问共享资源,导致不正确的结果。
死锁指多个进程互相等待,导致它们都无法继续执行。
1. 互斥锁互斥锁是最常见的同步方法之一,它被用来保护共享资源,确保同一时刻只有一个进程可以访问它。
当一个进程获取了锁,其他进程必须等待,直到锁被释放。
在 POSIX 系统中,互斥锁可以通过 pthread_mutex_t 数据类型实现。
我们可以使用pthread_mutex_init() 函数初始化锁,使用 pthread_mutex_lock() 函数获取锁,使用pthread_mutex_unlock() 函数释放锁。
下面是一个例子,展示了如何使用互斥锁同步两个进程对共享变量的访问:```c#include <pthread.h>#include <stdio.h>int count = 0;pthread_mutex_t lock;void *increment(void *arg) {for (int i = 0; i < 1000000; i++) {pthread_mutex_lock(&lock); // 获取锁count++;pthread_mutex_unlock(&lock); // 释放锁}return NULL;}在上面的例子中,我们创建了两个线程,它们分别对共享变量 count 进行了一百万次的递增操作。
我们使用了互斥锁来保护 count 变量,确保同一时刻只有一个线程可以访问它。
2. 信号量3. 条件变量条件变量可以被用来支持更高级的同步机制,如互斥锁和信号量。
进程同步实验报告

一、实验目的1. 理解进程同步的概念和原理;2. 掌握进程同步的基本方法和机制;3. 学会使用信号量实现进程同步;4. 通过实验验证进程同步机制的有效性。
二、实验原理1. 进程同步:在多道程序设计中,进程的执行是并发的,但某些情况下需要保证多个进程按照一定的顺序执行,以避免出现数据不一致、死锁等问题。
进程同步是指通过某种机制,协调多个进程的执行顺序,保证它们能够正确、有效地共享资源。
2. 信号量:信号量是一种特殊的变量,用于实现进程同步。
信号量具有两个原子操作:P操作(wait)和V操作(signal)。
P操作用于申请资源,V操作用于释放资源。
3. 互斥锁:互斥锁是一种常见的进程同步机制,用于保证临界资源的互斥访问。
当一个进程进入临界区时,它会尝试获取互斥锁,如果锁已被其他进程获取,则该进程进入等待状态;当进程退出临界区时,它会释放互斥锁。
三、实验内容1. 实验环境:Linux操作系统,C语言编程环境。
2. 实验工具:gcc编译器、gdb调试器。
3. 实验步骤:(1)创建一个互斥锁,用于保护临界资源。
(2)编写两个进程,分别模拟对临界资源的访问。
(3)在进程访问临界资源前,使用P操作尝试获取互斥锁。
(4)在进程访问临界资源后,使用V操作释放互斥锁。
(5)编译并运行程序,观察进程执行情况。
四、实验结果与分析1. 实验结果:(1)在互斥锁的保护下,两个进程能够按照预期顺序访问临界资源。
(2)当其中一个进程正在访问临界资源时,另一个进程会进入等待状态。
(3)当进程访问临界资源完成后,它会释放互斥锁,允许其他进程访问。
2. 实验分析:(1)互斥锁能够有效地保护临界资源,避免数据不一致问题。
(2)信号量P操作和V操作保证了进程的同步,避免了死锁现象。
(3)通过实验验证了进程同步机制的有效性。
五、实验总结本次实验通过使用信号量和互斥锁,实现了进程同步。
实验结果表明,信号量和互斥锁能够有效地保证进程按照预期顺序执行,避免数据不一致和死锁等问题。
操作系统:进程同步

操作系统:进程同步基本概念在 Os 中引⼊进程后,虽然提⾼了资源的利⽤率和系统的吞吐量,但由于进程的异步性,也会给系统造成混乱,尤其是在他们争⽤临界资源时。
例如,当多个进程去争⽤⼀台打印机时,有可能使多个进程的输出结果交织在⼀起,难于区分;⽽当多个进程去争⽤共享变量、表格、链表时,有可能致使数据处理出错。
进程同步的主要任务是对多个相关进程在执⾏次序上进⾏协调,以使并发执⾏的诸进程之间能有效地共享资源和相互合作,从⽽使程序的执⾏具有可再现性。
在资源共享的情况下:保证诸进程以互斥的⽅式访问临界资源—必须以互斥⽅式访问的共享资源;在相互合作的关系中:进程同步的主要任务是保证相互合作的诸进程在执⾏次序上协调,(有些教材把这种功能称做“协调”)。
相互合作的进程可能同时存在资源共享的关系。
如何实现进程互斥,需要让进程以互斥的⽅式进⼊各⾃的临界区,先执⾏进⼊区的代码。
⼈为地加⼀段代码。
临界资源必须以互斥⽅式访问的共享资源counter的例⼦:在机器语⾔中实现两个进程给count加⼀的操作register1 = countregister1 = register1 + 1count = register1register2 = countregister2 = register2 + 1count = register2但是如果是并发执⾏,可能会出现下⾯的情况register1 = countregister2 = countregister1 = register1 + 1register2 = register2 + 1count = register1count = register2结果就不对了。
可见,counter应该作为临界资源。
多个进程必须对其进⾏互斥访问临界区在每个进程中访问临界资源的那段代码称为临界区。
如果能保证诸进程互斥地进⼊⾃⼰的临界区,便可实现诸进程对临界资源的互斥访问。
每个进程在进⼊临界区之前,应先对欲访问的临界资源进⾏检查,看它是否正被访问。
操作系统实验参考代码

目录实验一WINDOWS进程初识 (2)实验二进程管理 (6)实验三进程同步的经典算法 (10)实验四存储管理 (14)试验五文件系统试验 (18)实验有关(参考)代码实验一WINDOWS进程初识1、实验目的(1)学会使用VC编写基本的Win32 Consol Application(控制台应用程序)。
(2)掌握WINDOWS API的使用方法。
(3)编写测试程序,理解用户态运行和核心态运行。
2、程序清单清单1-1 一个简单的Windows控制台应用程序// hello项目# include <iostream>void main(){std::cout << “Hello, Win32 Consol Application” << std :: endl ;}清单1-2 核心态运行和用户态运行时间比计算// proclist项目# include <windows.h># include <tlhelp32.h># include <iostream.h>// 当在用户模式机内核模式下都提供所耗时间时,在内核模式下进行所耗时间的64位计算的帮助方法DWORD GetKernelModePercentage(const FILETIME& ftKernel,const FILETIME& ftUser){// 将FILETIME结构转化为64位整数ULONGLONG qwKernel=(((ULONGLONG)ftKernel.dwHighDateTime)<<32)+ftKernel.dwLowDateTime;ULONGLONG qwUser=(((ULONGLONG)ftUser.dwHighDateTime)<<32)+ftUser.dwLowDateTime;// 将消耗时间相加,然后计算消耗在内核模式下的时间百分比ULONGLONG qwTotal=qwKernel+qwUser;DWORD dwPct=(DWORD)(((ULONGLONG)100*qwKernel)/qwTotal);return(dwPct);}// 以下是将当前运行过程名和消耗在内核模式下的时间百分数都显示出来的应用程序void main(int argc,char *argv[]){if(argc<2){cout<<"请给出你要查询的程序名"<<endl;exit(0);}// 对当前系统中运行的过程拍取“快照”HANDLE hSnapshot=::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,// 提取当前过程0);// 如果是当前过程,就将其忽略// 初始化过程入口PROCESSENTRY32 pe;::ZeroMemory(&pe,sizeof(pe));pe.dwSize=sizeof(pe);BOOL bMore=::Process32First(hSnapshot,&pe);BOOL found = FALSE;while(bMore){// 打开用于读取的过程if(!strcmp(pe.szExeFile,argv[1])){found = TRUE;HANDLE hProcess=::OpenProcess(PROCESS_QUERY_INFORMA TION,// 指明要得到信息FALSE,// 不必继承这一句柄pe.th32ProcessID);// 要打开的进程if (hProcess!=NULL){// 找出进程的时间FILETIME ftCreation,ftKernelMode,ftUserMode,ftExit;::GetProcessTimes(hProcess,// 所感兴趣的进程&ftCreation,// 进程的启动时间&ftExit,// 结束时间(如果有的话)&ftKernelMode,// 在内核模式下消耗的时间&ftUserMode);// 在用户模式下消耗的时间// 计算内核模式消耗的时间百分比DWORD dwPctKernel=::GetKernelModePercentage(ftKernelMode,// 在内核模式上消耗的时间ftUserMode);// 在用户模式下消耗的时间// 向用户显示进程的某些信息cout<< "process ID: " << pe.th32ProcessID<< ",EXE file:" << pe.szExeFile<< ",%d in Kernel mode: " << dwPctKernel << endl;// 消除句柄::CloseHandle(hProcess);}}// 转向下一个进程bMore=::Process32Next(hSnapshot,&pe);}if(found==FALSE){cout<<"当前系统没有这个可执行程序正在运行"<<endl;exit(0);}}清单1-3 核心态运行和用户态运行时间测试程序#include <stdio.h>main(){int i,j;while(1){for(i=0;i<1000;i++);for(j=1;j<1000;j++) printf(“enter kernel mode running.”);}}实验二进程管理1、实验目的1) 通过创建进程、观察正在运行的进程和终止进程的程序设计和调试操作,进一步熟悉操作系统的进程概念,理解Windows进程的“一生”。
进程的调度实验报告(3篇)

第1篇一、实验目的通过本次实验,加深对操作系统进程调度原理的理解,掌握先来先服务(FCFS)、时间片轮转(RR)和动态优先级(DP)三种常见调度算法的实现,并能够分析这些算法的优缺点,提高程序设计能力。
二、实验环境- 编程语言:C语言- 操作系统:Linux- 编译器:GCC三、实验内容本实验主要实现以下内容:1. 定义进程控制块(PCB)结构体,包含进程名、到达时间、服务时间、优先级、状态等信息。
2. 实现三种调度算法:FCFS、RR和DP。
3. 创建一个进程队列,用于存储所有进程。
4. 实现调度函数,根据所选算法选择下一个执行的进程。
5. 模拟进程执行过程,打印进程执行状态和就绪队列。
四、实验步骤1. 定义PCB结构体:```ctypedef struct PCB {char processName[10];int arrivalTime;int serviceTime;int priority;int usedTime;int state; // 0: 等待,1: 运行,2: 完成} PCB;```2. 创建进程队列:```cPCB processes[MAX_PROCESSES]; // 假设最多有MAX_PROCESSES个进程int processCount = 0; // 实际进程数量```3. 实现三种调度算法:(1)FCFS调度算法:```cvoid fcfsScheduling() {int i, j;for (i = 0; i < processCount; i++) {processes[i].state = 1; // 设置为运行状态printf("正在运行进程:%s\n", processes[i].processName); processes[i].usedTime++;if (processes[i].usedTime == processes[i].serviceTime) { processes[i].state = 2; // 设置为完成状态printf("进程:%s 完成\n", processes[i].processName); }for (j = i + 1; j < processCount; j++) {processes[j].arrivalTime--;}}}```(2)RR调度算法:```cvoid rrScheduling() {int i, j, quantum = 1; // 时间片for (i = 0; i < processCount; i++) {processes[i].state = 1; // 设置为运行状态printf("正在运行进程:%s\n", processes[i].processName); processes[i].usedTime++;processes[i].serviceTime--;if (processes[i].serviceTime <= 0) {processes[i].state = 2; // 设置为完成状态printf("进程:%s 完成\n", processes[i].processName); } else {processes[i].arrivalTime++;}for (j = i + 1; j < processCount; j++) {processes[j].arrivalTime--;}}}```(3)DP调度算法:```cvoid dpScheduling() {int i, j, minPriority = MAX_PRIORITY;int minIndex = -1;for (i = 0; i < processCount; i++) {if (processes[i].arrivalTime <= 0 && processes[i].priority < minPriority) {minPriority = processes[i].priority;minIndex = i;}}if (minIndex != -1) {processes[minIndex].state = 1; // 设置为运行状态printf("正在运行进程:%s\n", processes[minIndex].processName);processes[minIndex].usedTime++;processes[minIndex].priority--;processes[minIndex].serviceTime--;if (processes[minIndex].serviceTime <= 0) {processes[minIndex].state = 2; // 设置为完成状态printf("进程:%s 完成\n", processes[minIndex].processName); }}}```4. 模拟进程执行过程:```cvoid simulateProcess() {printf("请选择调度算法(1:FCFS,2:RR,3:DP):");int choice;scanf("%d", &choice);switch (choice) {case 1:fcfsScheduling();break;case 2:rrScheduling();break;case 3:dpScheduling();break;default:printf("无效的调度算法选择。
操作系统复习资料全第二章 进程管理(3)-经典同步问题

信号量S的值除初始化(为资源数目)外,其值只能通过原
语wait和signal,也称P、V操作来改变。
整型信号量的P、V操作描述
wait和signal
wait(S): while S≤0 do no-op S∶=S-1; signal(S): S∶=S+1; 解释:P或wait操作:当S≤0时,说明无资源可用,一直测试直到其他进程 释放该类资源。
1. 至多只允许有四位哲学家同时去拿左边的筷子,最终能保证至少 有一位哲学家能够进餐。(增加一个总资源信号量S=4)
2. 仅当哲学家的左、右两只筷子均可用时,才允许他拿起筷子进餐 (AND型信号量)。
3. 规定奇数号哲学家先拿他左边的筷子,然后再去拿右边的筷子; 而偶数号哲学家则相反。按此规定,将是1、 2号哲学家竞争1号 筷子;3、4号哲学家竞争3号筷子。即五位哲学家都先竞争奇数 号筷子,获得后,再去竞争偶数号筷子,最后总会有一位哲学家 能获得两只筷子而进餐。
1. 利用记录型信号量解决读者 为实现Reader与Writer进程间在读或写时的互斥而设置了一个互 斥信号量Wmutex; 设置一个整型变量Readcount表示正在读的进程数目; 当 Readcount=0 时,表示尚无 Reader 进程在读时, Reader 进 程才需要执行 Wait(Wmutex) 操作。若 wait(Wmutex) 操作成功, 做Readcount+1和读文件操作; 当Reader进程在执行了Readcount减1操作后其值为0时,才须 执行signal(Wmutex)操作,以便让Writer进程写; 又因为Readcount是一个可被多个 Reader进程访问的临界资源, 因此,应该为它设置一个互斥信号量rmutex。
计算机操作系统原理 ch3 进程同步

算法3的问题
该算法可确保准则2。但又出现新问 题。它可能造成谁也不能进入。如,当pi 和pj几乎同时都要进入,分别把自己的 标志置为true,都立即检查对方的标志, 发现对方为true. 最终谁也不能进入。 这 不能保证准则1。
19
课本上的解法4
Pi进程: Repeat flag[i]:=true; While flag[j] do no_op; begin flag[i]:=false; <delay for a short time> flag[i]:=true; end Critical section flag[i]:=false;
29
用原语实现进程互斥
锁即操作系统中的一标志位,0表示资源可用, 1表示资源已 被占用。用户程序不能对锁直 接操作,必须通过操作系统提供的上锁和开 锁原语来操作。 通常锁用w表示,上锁开锁原语分别用lock(w)、 unlock(w)来表示。
30
上锁和开锁原语
上锁原语lock(w)可描述为: L:if(w==1) goto L else w=1;
开锁原语unlock(w)可描述为: w=0;
31
用原语实现进程互斥
32
改进的上锁原语
上述上锁原语中存在忙等待。
33
改进的开锁原语
34
信号量及P、V操作
1965年,由荷兰学者Dijkstra提出(所以 P、V分别是荷兰语的test (proberen) 和 increment (verhogen) ) 一种卓有成效的进程同步机制 最初提出的是二元信号量(互斥) 推广到一般信号量(多值)(同步) P、V操作是原语
必须置一次且只能置一次初值 初值不能为负数 只能执行P、V操作
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
3-1修改后结果图
从中你可以得出什么结论:
5:修改清单3-1中的程序,按程序注释中的说明修改信号量EmptySemaphore的初始化方法,看看结果有何不同。运行结果:
6:根据步骤4的结果,并查看MSDN,回答下列问题
(2)了解互斥体对象,通过对生产者消费者等进程间同步与互斥经典算法的实现,加深对P、V原语以及利用P、V原语进行进程间同步与互斥操作的理解。
实验设备(环境):
(1)一台安装有Cygwin Terminal的计算机
(2)WindowsXP操作系统
(3)VC++6.0
实验内容:
(1)生产者消费者问题
(2)读者写者问题
int main()
{
Mutex = CreateMutex(NULL,FALSE,NULL);
EmptySemaphore = CreateSemaphore(NULL,SIZE_OF_BUFFER,SIZE_OF_BUFFER,NULL);
FullSemaphore = CreateSemaphore(NULL,0,SIZE_OF_BUFFER,NULL);
if (hThreads[i]==NULL) return -1;
}
while(p_ccontinue){
if(getchar()){ //按回车后终止程序运行
p_ccontinue = false;
}
}
return 0;
}
//生产一个产品。void Produce()
{
std::cout << std::endl<< "Producing " << ++ProductID << " ... ";
#define W WRITER
typedef struct _Person
{
HANDLE m_hThread;//定义处理线程的句柄
int m_nType;//进程类型(读写)
int m_nStartTime;//开始时间
int m_nWorkTime;//运行时间
int m_nID;//进程号
}Person;
P:Take() V:Append()
4)CreateMutex能用CreateSemaphore替代吗?尝试修改程序3-1,将信号量Mutex完全用CreateSemaphore及相关函数实现。写出要修改的语句:
Mutex=CreateSemaphore(NULL,false,false,NULL);
out = (out+1)%SIZE_OF_BUFFER;
std::cerr << "Succeed" << std::endl;
//输出缓冲区当前的状态
for (int i=0;i<SIZE_OF_BUFFER;++i){
std::cout << i <<": " << buffer[i];
if (i==in) std::cout << " <--生产";
const unsigned short PRODUCERS_COUNT = 3; //生产者的个数
const unsigned short CONSUMERS_COUNT = 1; //消费者的个数
//总的线程数
const unsigned short THREADS_COUNT = PRODUCERS_COUNT+CONSUMERS_COUNT;
}
//生产者
DWORD WINAPI Producer(LPVOID lpPara)
{
while(p_ccontinue){
WaitForSingleObject(EmptySemaphore,INFINITE);//p(empty);
WaitForSingleObject(Mutex,INFINITE);//p(mutex);
std::cout << "Succeed" << std::endl;
}
//把新生产的产品放入缓冲区
void Append()
{
std::cerr << "Appending a product ... ";
buffer[in] = ProductID;
in = (in+1)%SIZE_OF_BUFFER;
{
while(p_ccontinue){
WaitForSingleObject(FullSemaphore,INFINITE);//P(full);
WaitForSingleObject(Mutex,INFINITE);//P(mutex);
Take();
Consume();
Sleep(1500);
(2)读者写者问题
根据实验(1)中所熟悉的P、V原语对应的实际Windows API函数,并参考教材中读者、写者问题的算法原理,尝试利用Windows API函数实现第一类读者写者问题(读者优先)。
拷贝清单3-2中的程序,编译成可执行文件,运行截图:
读者写者运行结果
程序清单:
程序3-1代码:
#include <windows.h>
HANDLE hThreads[THREADS_COUNT];
DWORD producerID[PRODUCERS_COUNT];
DWORD consumerID[CONSUMERS_COUNT];
//创建生产者线程
for (int i=0;i<PRODUCERS_COUNT;++i){
hThreads[i]=CreateThread(NULL,0,Producer,NULL,0,&producerID[i]);
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#define MAX_PERSON 100 //最多100人
#define READER 0 //读者
#define WRITER 1 //写者
#define END -1 //结束
#define R READER
1)CreateMutex中有几个参数,各代表什么含义。
有3个参数
1. LPSECURITY_ATTRIBUTES IpMutexAttributes代表安全属性的指针
2. BOOL bInitialOwner代表布尔bInitialOwner
3. LPCTSTR IpName代表LPCTSTR类型IpName
#include <iostream>
const unsigned short SIZE_OF_BUFFER = 2;
unsigned short ProductID = 0;
unsigned short ConsumeID = 0;
unsigned short in = 0;
unsigned short out = 0;
int buffer[SIZE_OF_BUFFER];
bool p_ccontinue = true;
HANDLE Mutex;
HANDLE FullSemaphore;
HANDLE EmptySemaphore;
DWONAPI Consumer(LPVOID);
2)CreateSemaphore中有几个参数,各代表什么含义,信号量的初值在第几个参数中。
有4个参数:
1、表示采用不允许继承的默认描述符
2、设置信号机的初始计数
3、设置信号机的最大计数
4、指定信号机对象的名称。
3)程序中P、V原语所对应的实际Windows API函数是什么,写出这几条语句。
4)CreateMutex能用CreateSemaphore替代吗?尝试修改程序3-1,将信号量Mutex完全用CreateSemaphore及相关函数实现。写出要修改的语句:
ReleaseMutex(Mutex);//V(mutex);
ReleaseSemaphore(EmptySemaphore,1,NULL);//V(empty);
}
return 0;
}
3-2代码:
#include <windows.h>
#include <ctype.h>
#include <stdio.h>
2:在“命令提示符”窗口运行步骤1中生成的可执行文件。运行结果:
在命令窗口运行过程图
3-1运行结果图
3:仔细阅读源程序,找出创建线程的WINDOWS API函数,回答下列问题:线程的第一个执行函数是什么(从哪里开始执行)?它位于创建线程的API函数的第几个参数中?
第一个执行函数是Producer;位于第三个参数中。
if (hThreads[i]==NULL) return -1;
}
//创建消费者线程
for (i=0;i<CONSUMERS_COUNT;++i){
hThreads[PRODUCERS_COUNT+i]=CreateThread(NULL,0,Consumer,NULL,0,&consumerID[i]);
5, R, 4, 3,
6, R, 17,7,