要求
1.假设系统有n个进程,每个进程用一个进程控制块(PCB)来代表。进程控制块的格式如下表所示,且参数意义也相同。进程的优先数、到达时间和估计运行时间由用户程序任意设定,且优先数越低,优先级越高。调度时,总是选择优先级最高的进程运行。
2.绪队列的第一个到达进程。另外再设一个当前运行进程指针,指向当前正运行的进程。
3.处理机调度时,总是选择已经到达队列的优先级最高的进程运行。为了采用动态优先级调度,进程每运行一次,其优先级就减1。
4.由于本题目是模拟实验,所以对被选中的进程并不实际启动运行,而只是执行如下操作:
1)优先数加1;
2)估计运行时间减1;
3)输出当前运行进程的名字。
用这三个操作来模拟进程的一次运行。
5.进程运行一次后,应判断该进程的剩余运行时间是否为0,若不为0,且其优先级低于就绪队列的其他进程的优先级,则选择一个高优先级进程抢占CPU运行,若该进程的剩余运行时间为0,则将该进程的状态置为完成状态“C”,并撤出就绪队列。
6.若就绪队列不为空,则重复上述的步骤(4)和(5)直到所有进程都运行完为止。
7.在所设计的调度程序中,应包含显示或打印语句,以便显示或打印每次选中进程的名称及运行一次后进程的变化以及就绪队列中各进程排队情况。
实现代码思路
为每一个进程PCB创建一个线程来模拟其进程。每当选出一个适合运行的进程时,则模拟该进程的线程执行上面的三个操作。在执行这三个操作之间要进行加锁,即线程间互斥,以此来保证同一时刻只有一个线程在运行。
实验心得
1.加锁和解锁一定要对称。运时时程序发生死锁,表现为程序停止运行,但不退出程序,就要考虑加锁和解锁是否对称,是否存在加锁了却没有解锁的情况。
2.程序停止运行,但不退出程序还有另一种情况就是pthread_join(tid[i], NULL); 在等待线程终止,但线程已经不能再运行了。这时就要考虑线程终止的条件是否合适。
3.在设计线程并发并且线程内要循环运行互斥运行某段代码时,要使用两个while()语句来实现循环,例如下面代码的
while (1)
{
while (RunningPro->name == name && mark == 0)
{
//循环代码
}
}
代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MaxProNum 1000
struct PCB//进程控制块信息
{
int name;//进程名
struct PCB *next;//链接指针
int RunTime;//估计运行时间
int ArriveTime;//到达时间
int PN;//进程优先级
char state;//进程状态
};
int Time = 0;//时钟
int mark = 1;//当mark==1时,表示被选中的进程已经运行完。当mark==0,已经选出下一次运行的进程
int AllProNum_copy;
int RunningProNum=0;//当前运行的线程数量
pthread_mutex_t mutex;//互斥信号量
struct PCB temp[MaxProNum];//保存每个进程的PCB的数组
struct PCB *RunningPro = NULL;//指向正在运行的进程的PCB的指针
struct PCB *TailPro = NULL;//指向就绪队列第一个进程的PCB的指针
struct PCB *HeadPro = NULL;//指向就绪队列最后一个进程的PCB的指针
void *threadprocess(void *arg)
{
int name = *(int *)arg;
while (1)
{
while (RunningPro->name == name && mark == 0)
{
pthread_mutex_lock(&mutex);//加锁
printf("进程%d正在运行\n", name);
RunningPro->PN = RunningPro->PN + 1;//进程优先级数+1
RunningPro->RunTime = RunningPro->RunTime - 1;//进程运行时间-1
mark = 1;
if (RunningPro->RunTime == 0)//若进程运行完
{
RunningPro->state = 'C';//将进程状态置为'C'
AllProNum_copy--;
printf("进程%d完成退出\n", name);
printf("\n\n");
pthread_mutex_unlock(&mutex);//解锁
pthread_exit(NULL);//终止进程
}
pthread_mutex_unlock(&mutex);//解锁
printf("\n\n");
break;
}
sleep(0);
}
}
int main()
{
pthread_t tid[MaxProNum];
int M[MaxProNum];
pthread_mutex_init(&mutex, NULL);//初始化互斥量
int AllProNum;//设置总进程数
printf("请输入进程数:");
scanf("%d",&AllProNum);
AllProNum_copy = AllProNum;
printf("**********进程信息**********\n");
for (int i = 0; i < AllProNum; i++)
{
temp[i].name = i;
temp[i].next = NULL;
temp[i].RunTime = rand() % 10+1;
temp[i].ArriveTime = rand() % 50+2;
temp[i].PN = rand() % 10;
temp[i].state = 'R';
M[i] = i;
printf("进程名:%d 到达时间:%d 运行时间:%d 优先级数:%d 状态:%c\n",temp[i].name,temp[i].ArriveTime,temp[i].RunTime,temp[i].PN,temp[i].state);
}
int mark1 = 0;
while (AllProNum_copy)
{
while (mark == 1)
{
pthread_mutex_lock(&mutex);
printf("**********时间:%d**********\n",Time);
if(AllProNum_copy==0)//如果全部进程运行完
break;
for (int i = 0; i < AllProNum; i++)//该循环将到达时间为现在时间的线程放入队列
{
if (temp[i].ArriveTime == Time)
{
RunningProNum++;
if (HeadPro==NULL)//如果这是第一个线程
{
HeadPro = TailPro =&temp[i];//令头指针和尾指针都指向它
pthread_create(&tid[i], NULL, threadprocess, (void *)&M[i]);//为该进程生成一个线程
}
else
{
TailPro->next = &temp[i];//尾插法
temp[i].next = NULL;
TailPro = &temp[i];
pthread_create(&tid[i], NULL, threadprocess, (void *)&M[i]);//为该进程生成一个线程
}
}
}
if(HeadPro==NULL)//如现在没有进程则直接跳过
{
Time++;
printf("无进程运行\n\n");
pthread_mutex_unlock(&mutex);//加锁
continue;
}
struct PCB *RunPro = NULL;
struct PCB *tmp = HeadPro;
struct PCB *last = HeadPro;
int minPN = 1024;
while (tmp != NULL)//获得就绪队列和优先级数最小的线程
{
if (tmp->state == 'C')//如果线程状态为C,则将该线程从就绪队列中移除
{
RunningProNum--;
if(RunningProNum==0)//如果当前运行进程数为0
{
HeadPro=TailPro=NULL;
break;
}
if(tmp==HeadPro)//如果需要移去的是头节点
{
HeadPro=HeadPro->next;
tmp = HeadPro;
}
else if (tmp==TailPro)//如果需要移去的是尾节点
{
TailPro = last;
last->next=NULL;
break;
}
else//如果需要移去的是其他节点
{
last->next =tmp->next;
tmp = tmp->next;
}
continue;
}
printf("(名字%d 优先数%d 运行时间%d)->",
tmp->name, tmp->PN,tmp->RunTime);
if (tmp->PN < minPN && tmp->RunTime != 0)//获得优先级数最小的线程
{
RunPro = tmp;
minPN=tmp->PN;
}
last = tmp;
tmp = tmp->next;
}
if(RunningProNum==0) //如果当前运行进程数为0
{
Time++;
pthread_mutex_unlock(&mutex);//解锁
continue;
}
RunningPro = RunPro;//将RunningPro置为下次运行的进程的PCB地址
printf("\n最适合运行的线程:%d\n",
RunningPro->name);
Time++;
mark = 0;
pthread_mutex_unlock(&mutex);//解锁
sleep(0);
}
if(AllProNum_copy==0)
break;
}
printf("全部线程完成\n");
for (int i = 0; i < AllProNum; i++)//等待所有线程结束
pthread_join(tid[i], NULL);
return 0;
}