实验四 进程调度实验
一、实验目的
1、了解操作系统CPU管理的主要内容。
2、加深理解操作系统管理控制进程的数据结构--PCB。
3、掌握几种常见的CPU调度算法(FCFS、SJF、HRRF、RR)的基本思想和实现过程。
4、用C语言模拟实现CPU调度算法。
5、掌握CPU调度算法性能评价指标的计算方法。
6、通过对进程调度算法的模拟加深对进程概念和进程调度算法的理解。
二、实验内容
1、用C语言编写程序,模拟单处理器下先来先服务算法FCFS,要求显示各进程的到达时间、服务时间、完成时间,周转时间以及该算法的平均周转时间和平均带权周转时间。运行以下参考程序,给出结果截图并分析该算法的优缺点。(3分)
参考程序:
#include <stdio.h>
#include <stdlib.h>
struct PCB //先来先服务FCFS
{
char name[10]; //进程名
float arrivetime; //到达时间
float servetime; //服务时间
float finishtime; //完成时间
float roundtime; //周转时间
float daiquantime; //带权周转时间
};
struct PCB a[50];//定义进程数组
struct PCB *sortarrivetime(struct PCB a[], int n);//声明到达时间冒泡排序函数
void FCFS(struct PCB a[],int n,float *t1,float *t2);//先来先服务算法
//按到达时间进行冒泡排序
struct PCB *sortarrivetime(struct PCB a[], int n){
int i, j;
struct PCB t;
int flag; //标志变量,记录在每一趟冒泡中是否有元素交换,没有交换则结束冒泡
for (i = 1; i<n; i++) //外层循环控制比较趟数
{
flag = 0; //初始值设置为0
for (j = 0; j<n - i; j++) //内存循环控制每一趟的比较次数
{
if (a[j].arrivetime>a[j + 1].arrivetime) //将到达时间短的交换到前边
{
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
flag = 1; //有交换,flag置1
}
}
if (flag == 0)//如果一趟排序中没发生任何交换,则排序结束
{
break;
}
}
return a; //返回排序后进程数组
}
//先来先服务算法
void FCFS(struct PCB a[],int n,float *t1,float *t2)
{
int i;
a[0].finishtime = a[0].arrivetime + a[0].servetime; //完成时间=到达时间+服务时间
a[0].roundtime = a[0].finishtime - a[0].arrivetime; //周转时间=完成时间-到达时间
a[0].daiquantime = a[0].roundtime / a[0].servetime; //带权时间=周转时间/服务时间
for (i = 1; i<n; i++)
{
if (a[i].arrivetime<a[i-1].finishtime)//当前到达时间在上一个作业结束时间之前
{
a[i].finishtime = a[i-1].finishtime + a[i].servetime;//完成时间=上一个完成时间+服务时间
a[i].roundtime = a[i].finishtime - a[i].arrivetime; //周转时间=完成时间-到达时间
a[i].daiquantime = a[i].roundtime / a[i].servetime; //带权时间=周转时间/服务时间
}
else //当前到达时间在上一个作业结束时间之后
{
a[i].finishtime = a[i].arrivetime + a[i].servetime;//完成时间=到达时间+服务时间
a[i].roundtime = a[i].finishtime - a[i].arrivetime; //周转时间=完成时间-到达时间
a[i].daiquantime = a[i].roundtime / a[i].servetime;//带权时间=周转时间/服务时间
}
}
printf("=============================================================\n");
printf("进程相关信息如下:\n\n");
printf("进程名 ");
printf("到达时间 ");
printf("服务时间 ");
printf("完成时间 ");
printf("周转时间 ");
printf("带权周转时间\n");
for (i = 0;i<n;i++)
{
printf("%-10s",a[i].name);
printf("%-10.0f",a[i].arrivetime);
printf("%-10.0f",a[i].servetime);
printf("%-10.0f",a[i].finishtime);
printf("%-10.0f",a[i].roundtime);
printf("%10.2f\n",a[i].daiquantime);
*t1 += a[i].roundtime;
*t2 += a[i].daiquantime;
}
}
int main()
{
float t1 ; //总周转时间
float t2 ; //总带权周转时间
float avr_t1 ; //平均周转时间
float avr_t2 ; //平均带权周转时间
int n, i;
char select = ' '; //选择算法变量标识
while (select != '2') //不为退出标识,保持循环
{
t1 = 0.0f;
t2 = 0.0f;
system("clear");
printf("\n请选择算法:1.先来先服务算法 2.退出程序\n\n请输入选择: ");
scanf("%c", &select);
if (select == '1') //先来先服务算法
{
printf("\n=====================先来先服务算法FCFS=====================\n\n");
printf("请输入进程数:");
scanf("%d", &n);
for (i = 0; i<n; i++)
{
printf("%d 进程名:", i + 1);
scanf("%s", a[i].name);
printf("到达时间:");
scanf("%f", &a[i].arrivetime);
printf("服务时间:");
scanf("%f", &a[i].servetime);
}
getchar();
sortarrivetime(a, n);//按到达时间先后进行冒泡排序
FCFS(a,n,&t1,&t2); //先来先服务算法
avr_t1 = t1 / n;
avr_t2 = t2 / n;
printf("\n");
printf("平均周转时间为:%2.2f\n", avr_t1);
printf("平均带权周转时间为:%2.2f\n", avr_t2);
getchar();
}
else if (select == '2')
{
exit(0);
}
else
{
printf("please enter right choose!\n");
}
}
return 0;
}
请同学按下列给出的数据测试运行结果:
进程 | 到达时间 | 服务时间 |
P1 | 0 | 4 |
P2 | 1 | 6 |
P3 | 2 | 3 |
P4 | 3 | 1 |
P5 | 7 | 2 |
要求给出编译及运行过程和运行结果:
分析该算法优缺点:
优点:
- 简单易实现,容易理解和掌握。
- 公平性较高,先到达的进程先被服务,避免了饥饿问题。
- 适用于短作业或者作业到达时间分布均匀的情况。
缺点:
- 无法考虑作业的优先级,如果有高优先级的作业到达,可能会导致低优先级作业等待时间较长。
- 依赖于作业到达时间的顺序,如果到达时间相同,则先被调度的作业将占据资源,导致其他作业等待时间增加。
- 无法充分利用资源,忙等问题较为明显。
- 编程实现最短作业优先算法 SJF。(3分)
参考程序框架:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct PCB
{
char name[10]; //进程名
float arrivetime; //到达时间
float servetime; //服务时间
float finishtime; //完成时间
float roundtime; //周转时间
float daiquantime; //带权周转时间
};
struct PCB a[50];//初始化指针和数组
struct PCB *sortarrivetime(struct PCB a[], int n);//声明到达时间冒泡排序函数
void SJF(struct PCB a[], int n, float *t1, float *t2);//声明短作业优先算法函数
struct PCB *sortarrivetime(struct PCB a[], int n)
{
int i, j;
struct PCB t;
int flag; //标志变量,记录在每一趟冒泡中是否有元素交换,没有交换则结束冒泡
for (i = 1; i < n; i++) //外层循环控制比较趟数
{
flag = 0; //初始值设置为0
for (j = 0; j < n - i; j++) //内存循环控制每一趟的比较次数
{
if (a[j].arrivetime > a[j + 1].arrivetime) //将到达时间短的交换到前边
{
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
flag = 1; //有交换,flag置1
}
}
if (flag == 0)//如果一趟排序中没发生任何交换,则排序结束
{
break;
}
}
return a; //返回排序后进程数组
}
//短作业优先算法
void SJF(struct PCB a[], int n, float *t1, float *t2)
{
int i, c, d;
struct PCB t;
a[0].finishtime = a[0].arrivetime + a[0].servetime; //完成时间=到达时间+服务时间
a[0].roundtime = a[0].finishtime - a[0].arrivetime; //周转时间=完成时间-提交时间
a[0].daiquantime = a[0].roundtime / a[0].servetime; //带权时间=周转时间/服务时间
for (i = 1; i < n; i++)
{
for (c = i; c < n - 1; c++)
{
for (d = c + 1; d < n; d++) //d=i+1改成d=c+1
{
if ((a[i - 1].finishtime >= a[c].arrivetime) && (a[i - 1].finishtime >= a[d].arrivetime) && (a[c].servetime > a[d].servetime))
{
t = a[c];
a[c] = a[d];
a[d] = t;
}
}
}
if (a[i].arrivetime < a[i - 1].finishtime) //当前到达时间在上一个作业结束时间之前
{
a[i].finishtime = a[i - 1].finishtime + a[i].servetime;
}
else //当前到达时间在上一个作业结束时间之后
{
a[i].finishtime = a[i].arrivetime + a[i].servetime;
}
a[i].roundtime = a[i].finishtime - a[i].arrivetime;
a[i].daiquantime = a[i].roundtime / a[i].servetime;
}
printf("=============================================================\n");
printf("进程相关信息如下:\n\n");
printf("进程名 ");
printf("到达时间 ");
printf("服务时间 ");
printf("完成时间 ");
printf("周转时间 ");
printf("带权周转时间\n");
for (i = 0; i < n; i++)
{
printf("%-10s", a[i].name);
printf("%-10.0f", a[i].arrivetime);
printf("%-10.0f", a[i].servetime);
printf("%-10.0f", a[i].finishtime);
printf("%-10.0f", a[i].roundtime);
printf("%10.2f\n", a[i].daiquantime);
*t1 += a[i].roundtime;
*t2 += a[i].daiquantime;
}
}
int main()
{
float t1; //总周转时间
float t2; //总带权周转时间
float avr_t1; //平均周转时间
float avr_t2; //平均带权周转时间
int n, i;
char select = ' '; //选择算法变量标识
while (select != '2') //不为退出标识,保持循环
{
t1 = 0.0f;
t2 = 0.0f;
system("clear");
printf("请选择算法:1.短作业优先算法 2.退出程序\n\n请输入选择: ");
scanf(" %c", &select);
if (select == '1') //短作业优先算法
{
printf("\n=====================短作业优先算法SJF=====================\n\n");
printf("请输入进程数:");
scanf("%d", &n);
for (i = 0; i < n; i++)
{
printf("%d 进程名:", i + 1);
scanf("%s", a[i].name);
printf("到达时间:");
scanf("%f", &a[i].arrivetime);
printf("服务时间:");
scanf("%f", &a[i].servetime);
}
getchar();
a[n] = *sortarrivetime(a, n);
SJF(a, n, &t1, &t2); //调短作业优先算法
avr_t1 = t1 / n; //平均周转时间
avr_t2 = t2 / n; //平均带权周转时间
printf("\n");
printf("平均周转时间为:%2.2f \n", avr_t1);
printf("平均带权周转时间为:%2.2f \n", avr_t2);
getchar();
}
else if (select == '2')
{
exit(0);
}
else
{
printf("please enter right choose!\n");
getchar();
}
}
return 0;
}编译及执行过程以及结果截屏:
分析该算法的优缺点:
优点:
- 可以保证平均等待时间最短,能够实现较高的系统运行效率。
- 由于短作业先执行,可以减少平均周转时间和平均等待时间,提高系统的响应速度。
- 可以确保最短的作业最先得到服务,从而避免了长作业占用系统资源的情况。
- 相对简单、容易实现。
缺点:
- 需要预先知道每个作业的估计运行时间,而实际的运行时间往往是未知的,这对于实际系统来说是很难预测的。
- 对于长作业来说,可能导致其长时间得不到服务,从而造成长作业的饥饿现象。
- 对于作业的估计运行时间要求较高,如果估计不准确,可能导致优先级排序错误,进一步影响系统的性能。
- 对于作业的到达时间不敏感,如果作业的到达时间不同,可能会出现饥饿现象或者长作业等待时间增加的情况。
3.编程实现最高响应比优先算法HRN,并分析该算法的优缺点。(要求给出程序设计分析和调试通过的程序,并给出编译,运行步骤和执行结果截图。)(3分)
编程程序:
#include <stdio.h>
#include <stdlib.h>
struct PCB {
char name[10]; // 进程名
float arrivetime; // 到达时间
float servetime; // 服务时间
float finishtime; // 完成时间
float roundtime; // 周转时间
float daiquantime; // 带权周转时间
float response_ratio; // 响应比
};
struct PCB a[50]; // 初始化指针和数组
struct PCB *sortarrivetime(struct PCB a[], int n); // 声明到达时间冒泡排序函数
void HRN(struct PCB a[], int n, float *t1, float *t2); // 声明最高响应比优先算法函数
// 按到达时间进行冒泡排序
struct PCB *sortarrivetime(struct PCB a[], int n)
{ int i, j;
struct PCB t;
int flag; //标志变量,记录在每一趟冒泡中是否有元素交换,没有交换则结束冒泡
for (i = 1; i<n; i++) //外层循环控制比较趟数
{
flag = 0; //初始值设置为0
for (j = 0; j<n - i; j++) //内存循环控制每一趟的比较次数
{
if (a[j].arrivetime>a[j + 1].arrivetime) //将到达时间短的交换到前边
{
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
flag = 1; //有交换,flag置1
}
}
if (flag == 0)//如果一趟排序中没发生任何交换,则排序结束
{
break;
}
}
return a; //返回排序后进程数组
}
// 最高响应比优先算法
void HRN(struct PCB a[], int n, float *t1, float *t2) {
int i, j, max_idx;
float max_ratio;
struct PCB temp;
a[0].finishtime = a[0].arrivetime + a[0].servetime; // 完成时间=到达时间+服务时间
a[0].roundtime = a[0].finishtime - a[0].arrivetime; // 周转时间=完成时间-到达时间
a[0].daiquantime = a[0].roundtime / a[0].servetime; // 带权时间=周转时间/服务时间
*t1 += a[0].roundtime;
*t2 += a[0].daiquantime;
for (i = 1; i < n; i++) {
max_ratio = 0.0;
max_idx = -1;
for (j = i; j < n; j++) {
a[j].response_ratio = (a[i - 1].finishtime - a[j].arrivetime + a[j].servetime) / a[j].servetime;
if (a[j].response_ratio > max_ratio) {
max_ratio = a[j].response_ratio;
max_idx = j;
}
}
temp = a[i];
a[i] = a[max_idx];
a[max_idx] = temp;
if (a[i].arrivetime < a[i - 1].finishtime) { // 当前到达时间在上一个作业结束时间之前
a[i].finishtime = a[i - 1].finishtime + a[i].servetime;
a[i].roundtime = a[i].finishtime - a[i].arrivetime;
a[i].daiquantime = a[i].roundtime / a[i].servetime;
} else { // 当前到达时间在上一个作业结束时间之后
a[i].finishtime = a[i].arrivetime + a[i].servetime;
a[i].roundtime = a[i].finishtime - a[i].arrivetime;
a[i].daiquantime = a[i].roundtime / a[i].servetime;
}
*t1 += a[i].roundtime;
*t2 += a[i].daiquantime;
}
printf("=============================================================\n");
printf("进程相关信息如下:\n\n");
printf("进程名 ");
printf("到达时间 ");
printf("服务时间 ");
printf("完成时间 ");
printf("周转时间 ");
printf("带权周转时间\n");
for (i = 0; i < n; i++) {
printf("%-10s", a[i].name);
printf("%-10.0f", a[i].arrivetime);
printf("%-10.0f", a[i].servetime);
printf("%-10.0f", a[i].finishtime);
printf("%-10.0f", a[i].roundtime);
printf("%10.2f\n", a[i].daiquantime);
}
}
int main() {
float t1; // 总周转时间
float t2; // 总带权周转时间
float avr_t1; // 平均周转时间
float avr_t2; // 平均带权周转时间
int n, i;
char select = ' '; // 选择算法变量标识
while (select != '2') { // 不为退出标识,保持循环
t1 = 0.0f;
t2 = 0.0f;
system("clear");
printf("请选择算法:1.最高响应比优先算法 2.退出程序\n\n请输入选择: ");
scanf("%c", &select);
if (select == '1') { // 最高响应比优先算法
printf("\n=====================最高响应比优先算法HRN=====================\n\n");
printf("请输入进程数:");
scanf("%d", &n);
for (i = 0; i < n; i++) {
printf("%d 进程名:", i + 1);
scanf("%s", a[i].name);
printf("到达时间:");
scanf("%f", &a[i].arrivetime);
printf("服务时间:");
scanf("%f", &a[i].servetime);
}
getchar();
sortarrivetime(a, n); // 按到达时间进行冒泡排序
HRN(a, n, &t1, &t2); // 调用最高响应比优先算法
avr_t1 = t1 / n; // 平均周转时间
avr_t2 = t2 / n; // 平均带权周转时间
printf("\n");
printf("平均周转时间为:%2.2f \n", avr_t1);
printf("平均带权周转时间为:%2.2f \n", avr_t2);
getchar();
} else if (select == '2') {
exit(0);
} else {
printf("please enter right choose!\n");
getchar();
}
}
return 0;
}
分析该算法的优缺点:
优点:
- 能够保证每个作业都有机会执行,避免了作业饥饿的情况。
- 能够根据当前作业的响应比来决定下一个执行的作业,可以有效地提高作业的响应速度。
- 能够根据作业的响应比来动态调整作业的执行顺序,使得高响应比的作业能够更早执行,提高系统的整体性能。
缺点:
- 需要计算每个作业的响应比,这涉及到对作业的运行时间和等待时间的估计,因此计算复杂度较高。
- 不能满足实时性要求,因为它只关注作业的响应比而不考虑截止时间。
- 编程实现时间片轮换算法,并分析算法的优缺点。(要求给出程序设计分析和调试通过的程序,并给出编译,运行步骤和执行结果截图。)(附加题)
编程程序:
#include <stdio.h>
#include <stdlib.h>
struct PCB //定义进程控制块
{
char name[10]; //进程名
float arrivetime; //到达时间
float servetime; //服务时间
float finishtime; //完成时间
float roundtime; //周转时间
float daiquantime; //带权周转时间
};
struct PCB a[50];//定义进程数组
struct PCB *sortarrivetime(struct PCB a[], int n);//声明到达时间冒泡排序函数
void RR(struct PCB a[], int n, float qt, float *t1, float *t2);//时间片轮换算法
//按到达时间进行冒泡排序
struct PCB *sortarrivetime(struct PCB a[], int n){
int i, j;
struct PCB t;
int flag; //标志变量,记录在每一趟冒泡中是否有元素交换,没有交换则结束冒泡
for (i = 1; i < n; i++) //外层循环控制比较趟数
{
flag = 0; //初始值设置为0
for (j = 0; j < n - i; j++) //内循环控制每一趟的比较次数
{
if (a[j].arrivetime > a[j + 1].arrivetime) //将到达时间短的交换到前边
{
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
flag = 1; //有交换,flag置1
}
}
if (flag == 0)//如果一趟排序中没发生任何交换,则排序结束
{
break;
}
}
return a; //返回排序后的进程数组
}
//时间片轮换算法
void RR(struct PCB a[], int n, float qt, float *t1, float *t2)
{
int i, j;
float remaintime[50]; //剩余服务时间数组
float starttime[50]; //开始时间数组
float responsetime[50]; //响应时间数组
float waitingtime[50]; //等待时间数组
int finishnum = 0; //已完成的进程数
float curtime = 0.0f; //当前时间
float nextstarttime = a[0].arrivetime; //下一个进程开始时间
for (i = 0; i < n; i++)
{
remaintime[i] = a[i].servetime; //剩余服务时间初始化为服务时间
starttime[i] = 0.0f; //开始时间初始化为0
responsetime[i] = 0.0f; //响应时间初始化为0
waitingtime[i] = 0.0f; //等待时间初始化为0
}
while (finishnum < n)
{
for (i = 0; i < n; i++)
{
if (a[i].arrivetime <= curtime && remaintime[i] > 0) //进程到达时间小于等于当前时间并且剩余服务时间大于0
{
if (starttime[i] == 0) //开始时间等于0,即第一次执行该进程
{
starttime[i] = curtime; //记录开始时间
responsetime[i] = curtime - a[i].arrivetime; //计算响应时间
}
if (remaintime[i] > qt) //剩余服务时间大于时间片
{
curtime += qt; //当前时间加上时间片
remaintime[i] -= qt; //剩余服务时间减去时间片
}
else
{
curtime += remaintime[i]; //当前时间加上剩余服务时间
a[i].finishtime = curtime; //记录完成时间
a[i].roundtime = curtime - a[i].arrivetime; //计算周转时间
a[i].daiquantime = a[i].roundtime / a[i].servetime; //计算带权周转时间
remaintime[i] = 0; //剩余服务时间置0
finishnum++; //已完成的进程数加1
}
}
else if (a[i].arrivetime > curtime) //进程到达时间大于当前时间
{
curtime = a[i].arrivetime; //当前时间更新为进程到达时间
break;
}
}
}
printf("=============================================================\n");
printf("进程相关信息如下:\n\n");
printf("进程名 ");
printf("到达时间 ");
printf("服务时间 ");
printf("完成时间 ");
printf("周转时间 ");
printf("带权周转时间 ");
printf("等待时间 ");
printf("响应时间\n");
for (i = 0; i < n; i++)
{
printf("%-10s", a[i].name);
printf("%-10.0f", a[i].arrivetime);
printf("%-10.0f", a[i].servetime);
printf("%-10.0f", a[i].finishtime);
printf("%-10.0f", a[i].roundtime);
printf("%10.2f", a[i].daiquantime);
waitingtime[i] = a[i].roundtime - a[i].servetime; //计算等待时间
printf("%10.0f", waitingtime[i]);
printf("%10.0f\n", responsetime[i]);
*t1 += a[i].roundtime;
*t2 += a[i].daiquantime;
}
}
int main()
{
float t1; //总周转时间
float t2; //总带权周转时间
float avr_t1; //平均周转时间
float avr_t2; //平均带权周转时间
int n, i;
float qt; //时间片大小
char select = ' '; //选择算法变量标识
while (select != '2') //不为退出标识,保持循环
{
t1 = 0.0f;
t2 = 0.0f;
system("clear");
printf("\n请选择算法:1.时间片轮换算法 2.退出程序\n\n请输入选择: ");
scanf("%c", &select);
if (select == '1') //时间片轮换算法
{
printf("\n=====================时间片轮换算法RR=====================\n\n");
printf("请输入进程数:");
scanf("%d", &n);
for (i = 0; i < n; i++)
{
printf("%d 进程名:", i + 1);
scanf("%s", a[i].name);
printf("到达时间:");
scanf("%f", &a[i].arrivetime);
printf("服务时间:");
scanf("%f", &a[i].servetime);
}
printf("时间片大小:");
scanf("%f", &qt);
getchar();
sortarrivetime(a, n); //按到达时间先后进行冒泡排序
RR(a, n, qt, &t1, &t2); //时间片轮换算法
avr_t1 = t1 / n;
avr_t2 = t2 / n;
printf("\n");
printf("平均周转时间为:%2.2f\n", avr_t1);
printf("平均带权周转时间为:%2.2f\n", avr_t2);
getchar();
}
else if (select == '2')
{
exit(0);
}
else
{
printf("please enter right choose!\n");
}
}
return 0;
分析该算法的优缺点:
优点:
- 公平性:时间片轮转算法保证每个进程都有相同的机会来执行,不会出现饥饿问题。
- 响应时间短:当一个新的进程就绪时,它可以很快的获得执行的机会,因为每个进程都有固定长度的时间片。
- 实现简单:时间片轮转算法的实现相对简单,只需要维护一个就绪队列,并在时间片用尽时进行进程切换。
缺点:
- 平均等待时间较长:如果一个进程需要执行的时间比较长,它可能需要等待很多轮才能再次获得执行的机会,导致平均等待时间较长。
- 频繁的上下文切换:当时间片长度很短时,会导致频繁的上下文切换,增加了系统开销。
- 不适合长任务:对于执行时间较长的任务,时间片轮转算法效果不好,因为
三、实验总结和体会(1分)
本次实验是关于操作系统进程调度的实验。在实验中,我学习了进程调度的基本概念和算法,并进行了实际操作。在实验中,我首先了解了进程调度的概念和作用。进程调度是操作系统中的一项重要功能,它负责决定哪个进程在何时运行。进程调度的目标是使系统的资源得到最优的利用,提高系统的吞吐量和响应速度。
在实验过程中,我学习了不同的进程调度算法,例如先来先服务(FCFS)、短作业优先(SJF)和时间片轮转(RR)等。我了解了这些算法的特点和适用场景。
在实验操作中,我通过编写和调试代码,实现了一个简单的进程调度模拟器。我能够根据不同的调度算法,对一组进程进行调度,并观察它们的运行情况。
通过这次实验,我对进程调度有了更深入的理解。我学会了如何选择合适的进程调度算法,以实现最优的资源利用和系统性能。同时,我也学会了如何编写和调试相关的代码,提高了自己的编程能力。
相关文章:
实验四 进程调度实验
一、实验目的 1、了解操作系统CPU管理的主要内容。 2、加深理解操作系统管理控制进程的数据结构--PCB。 3、掌握几种常见的CPU调度算法(FCFS、SJF、HRRF、RR)的基本思想和实现过程。 4、用C语言模拟实现CPU调度算法。 5、掌握CPU调度算法性能评价指…...
静态多态和动态多态的区别
C多态机制深度解析 多态是面向对象编程的核心特性,允许通过统一接口执行不同实现。在C中,多态表现为基类指针或引用调用虚函数时,根据实际对象类型执行对应派生类的函数逻辑。 基础实现示例 定义基类与派生类,演示动态绑定…...
现代化Android开发:Compose提示信息的最佳封装方案
在 Android 开发中,良好的用户反馈机制至关重要。Jetpack Compose 提供了现代化的 UI 构建方式,但提示信息(Toast/Snackbar)的管理往往显得分散。本文将介绍如何优雅地封装提示信息,提升代码可维护性。 一、基础封装方案 1. 简单 Snackbar …...
Android学习总结之Retrofit篇
1. 注解原理概述 在 Java 里,注解是一种元数据,它为代码提供额外信息但不影响程序的实际逻辑。注解可以在类、方法、字段等元素上使用,并且能在编译时、运行时通过反射机制被读取。Retrofit 充分利用了 Java 注解机制,通过自定义…...
Python 第 12、13 节课 - 元组和列表
- 第 94 篇 - Date: 2025 - 04 - 26 Author: 郑龙浩/仟墨 【Python 在校课堂笔记】 Python 第 12、13 节课 - 元组和列表 上课时间: 2025-04-21(12) 2025-04-24(13) 文章目录 Python 第 12、13 节课 - 元组和列表一 元组1 元组的…...
新特性版本升级指引
✨ 升级到新特性版本时的配置迁移 1️⃣ 🔧 配置迁移工具说明 当您将应用升级到新特性版本时,可能需要处理部分配置属性的重命名或移除问题。 2️⃣ 🚀 启用方法 Spring Boot 提供了环境分析工具: 应用启动时打印诊断信息运行时…...
6.1 客户服务:智能客服与自动化支持系统的构建
随着企业数字化转型的加速,客户服务作为企业与用户交互的核心环节,正经历从传统人工服务向智能化、自动化服务的深刻变革。基于大语言模型(LLM)和智能代理(Agent)的技术为构建智能客服与自动化支持系统提供…...
从新手到高手:小程序开发进阶技巧分享
小程序开发从入门到精通需要经历技术积累、架构优化和工程化实践等多个阶段。以下是结合真实项目经验的进阶路线与核心技术要点,涵盖性能优化、架构设计、跨平台开发等关键领域: 一、性能调优实战技巧 1. 首屏渲染加速方案 // 预请求关键数据ÿ…...
S参数的含义
S参数的含义: 在低速设计时代,工程界普遍使用等效集总电路模型来描述互连通道的过孔、连接器等各部分。对于上升时间达到几个ns的低速数字信号,甚至可以使用一个0Ω电阻代替连接器,分析的结果也不会和实际情况有太大的差别。但是当…...
职场十二法则-马方
马方老师的《职场十二法则》,献给初入职场工作中迷茫的自己。 1.挣钱是能力的副产品,能力比挣钱重要,让自己值钱比有钱更重要。成长比赚钱重要,年轻时把成长放第一位,挣钱放第二位,通过提升能力实现长期收益。 2.成长…...
安装docker,在docker上安装mysql,docker上安装nginx
目录 一.安装docker 1.1查看Linux版本的命令这里推荐两种: 1.2查看内核版本有三种方式: 2.安装 2.1 如果之前安装了docker,先删除旧版本的doker 2.2 安装需要的软件包,yum-util提供yum-config-manager功能,另外两…...
Java基础第五章、面向对象程序设计
1.package包 如果引入的不同包里面有相同的类名时,需要对要使用的类进行完整的限定名指定。 2.访问修饰符 子类对父类的方法重写也遵循上面的原则 一个java文件中最多只能有一个public(和文件同名)的类。 3.初始化块 //Driver.java public class Driver {private lo…...
RD电子实验记录本选用贴士A-B-C
传统的实验记录本,令人又爱又恨本 如何挑选电子实验室记录本(ELN)的品牌/服务商/供应商? 电子实验记录本,又名为ELN,Electronic lab notebook,enotebook,研发电子管理系统…...
Python 第 11 节课 - string 与 random 的方法
- 第 93 篇 - Date: 2025 - 04 - 26 Author: 郑龙浩/仟墨 【Python 在校课堂笔记】 Python 第 11 节课 - string 与 random 的方法 上课时间: 2025-04-14 文章目录 Python 第 11 节课 - string 与 random 的方法一 string 的方法1 s.split()2 s.find()3 s.replace()4 s.strip…...
proxychains4系统代理for linux(加速国内github下载速度,pip安装)
1.proxychains4代理安装: sudo apt-get install proxychains42.找到配置文件/etc/proxychains4.conf在[ProxyList]后面添加以下内容: socks5 127.0.0.1 10808 配置如下: 3.使用proxychains4(git clone): proxychains4 git c…...
LLM基础之源码一
transformers 核心源码梳理 Trainer部分: __init__() 初始化函数: def __init__(xxx):if args is None:output_dir "tmp_trainer"args TrainingArguments(output_diroutput_dir) self.args argsself.compute_loss_func compute_loss_fun…...
蛮荒tv桌面永不升级版app下载-蛮荒桌面安卓电视版下载
蛮荒桌面是一款具有丰富桌面内容的生活应用软件,可以连接电视上使用,用户将需要的软件添加到桌面上,系统就会自动分类管理软件,小编今天为大家推荐一款功能更大强大的电视桌面应用——乐看家桌面。 乐看家桌面功能亮点: 1.官网下载刷入机顶盒…...
2025蓝桥省赛c++B组第二场题解
前言 这场的题目非常的简单啊,至于为什么有第二场,因为当时河北正在刮大风被迫停止了QwQ,个人感觉是历年来最简单的一场,如果有什么不足之处,还望补充。 试题 A: 密密摆放 【问题描述】 小蓝有一个大箱子࿰…...
c#简易超市充值卡程序充值消费查余额
开发背景 本软件是给克什克腾旗某公司开发的一个简易的超市充值卡程序 # 功能 1、充值卡的充值、消费、查询余额 github下载:https://github.com/oyangxizhe/cash.git...
使用FME生成Delaunay三角形
目录 背景与应用场景 前置条件与数据准备 操作流程详解 3.1 点要素矢量化3.2 设置坐标系3.3 生成Delaunay三角形...
三款实用工具推荐:图片无损放大+音乐格式转换+音视频格式转换!
在这个数字化时代,总有些工具能让人眼前一亮。今天李师傅大家精选三款实用工具,从图片优化到格式转换,解决日常创作中的痛点,让数字生活更得心应手。 在这个数字化时代,总有些工具能让人眼前一亮。今天为大家精选三款…...
文本预处理(NLTK)
1. 自然语言处理基础概念 1.1 什么是自然语言处理 自然语言处理( Natural Language Processing, NLP)是计算机科学领域与人工智能领域中的一个重要方向。它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法。自然语言处理是一门融语言学、计算机科学、数学于…...
泰迪杯实战案例学习资料:生产线的故障自动识别和人员配置优化
(西南石油大学,第十二届泰迪杯特等奖案例) (深度扩展版) 一、案例背景与目标 1.1 问题背景 在制造业中,生产线设备故障可能导致以下问题: 停机损失:每小时停机成本可达数万元(视行业而定)。 资源浪费:人工排班不合理导致高技能员工闲置或低效分配。 安全隐患:未及…...
dijkstra
open_set是当前正在计算的节点; 每次从当前open_set集合中找出cost最小的节点进行计算更新:从open_set中去除该节点,保存到close_set中; 运动更新可以根据运动模型选择合适的节点运动方式; 【代价的计算方式是&…...
【SSH 端口转发】通过SSH端口转发实现访问远程服务器的 tensorboard
SSH 连接远程服务器时的命令: ssh -L 8001:localhost:8001 usrnameserverip-L: 这是指定进行本地端口转发的选项。也就是说,要将本地机器的一个端口通过 SSH 隧道转发到远程服务器上的某个服务。 8001:localhost:8001: 第一个 80…...
w308汽车销售系统的设计与实现
🙊作者简介:多年一线开发工作经验,原创团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹赠送计算机毕业设计600个选题excel文…...
Spring 学习笔记之 @Transactional 异常不回滚汇总
使用springboot时,只要引入spring-jdbc/jpa相关的依赖后,在想要启用事务的方法上加上Transactional注解就能开启事务,碰到异常就能自动回滚。大大的提高了编码的便捷性性,同时也不侵入代码,保持了代码的简洁性。 默认情…...
Java 自定义TCP协议:【特点编码字符串<=>字节<=>特点编码16进制】16进制字符串和编码的转换 (各种编码通过字节向16进制的互转)| XOR计算
文章目录 引言I 各种编码通过字节向16进制的互转。字符串<=>字节<=>16进制 | Java验证微信小程序 JavaScript字符串转gb2312 字符编码,以16进制字符串传输。(接收蓝牙设备的信息,发送北斗终端消息)II xor校验码Java实现验证C# 实现引言 为了避免中文在传输过程…...
大模型的使用
以下是不同类型大模型及其适用场景: 对话模型 - 代表模型:GPT-3.5/4、Claude、LaMDA、ChatGLM等。 - 适用场景:客服机器人为用户解答常见问题,提供实时支持;个人助理帮助用户完成各种任务,如查询信息、设置…...
OSPF的不规则区域和特殊区域
目录 一、OSPF不规则区域类型 1、非骨干区域无法和骨干区域保持连通 2、骨干区域被分割 解决方案 1、使用虚连接 2、使用多进程双向重发布(路由引入) 二、特殊区域 1、STUB区域(末梢区域) 2、totally stub区域(…...
C++学习:六个月从基础到就业——STL:分配器与设计原理
C学习:六个月从基础到就业——STL:分配器与设计原理 本文是我C学习之旅系列的第三十篇技术文章,也是第二阶段"C进阶特性"的第九篇,主要介绍C STL中的分配器设计原理与实现。查看完整系列目录了解更多内容。 引言 在之前…...
QQMusic项目功能总结
QQMusic项目功能总结 一、核心功能分类 (一)界面交互功能 功能模块实现方式使用类(自定义/Qt库)核心类说明窗口布局Head区(图标、搜索框、控制按钮) Body区(左侧功能栏右侧页面区)…...
人形机器人:MCP与人形机器人的联系
MCP(Model Context Protocol)与人形机器人的结合,正在重构智能体与物理世界的交互范式。这种联系不仅体现在技术架构的深度融合,更体现在对机器人认知能力、协作模式和应用场景的全方位赋能。以下从技术整合、场景落地和未来趋势三…...
matplotlib画图工具使用(1) 画折线统计图python代码
Python 画折线统计图(line chart)最常用的是 matplotlib。 最基本的折线图代码如下: import matplotlib.pyplot as plt# 假设这是你的数据 x [1, 2, 3, 4, 5] y [2, 3, 5, 7, 11]# 创建折线图 plt.plot(x, y, markero) # markero 是在点…...
神经网络与深度学习第四章-前馈神经网络
前馈神经网络 在本章中,我们主要关注采用误差反向传播来进行学习的神经网络。 4.1 神经元 神经元是构成神经网络的基本单元。主要是模拟生物神经元的结构和特性,接收一组输入信号并产生输出。 现代神经元中的激活函数通常要求是连续可导的函数。 净输…...
TC3xx学习笔记-UCB BMHD使用详解(一)
文章目录 前言UCB BMHDPINDISHWCFGLSENA0-3LBISTENACHSWENABMHDIDSTADCRCBMHDCRCBMHD_NPW0-7 总结 前言 AURIX Tc系列Mcu启动过程,必须要了解BMHD,本文详细介绍BMHD的定义及使用过程 UCB BMHD UCB表示User Configuration Block,UCB是Dflash,存储的地址…...
C语言 函数递归
目录 1.什么是递归 2.递归的限制条件 3.递归的举例 1.递归与迭代 1.递归是什么 递归是学习C语言函数绕不开的一个话题,那什么是递归呢? 递归其实是一种解决问题的方法,在C语言中,递归就是函数自己调用自己。 写一个史上最简单的C语言…...
4月25日日记(补)
最近实在是忙的不行了,做不到一天一更,但是实际上只需要每天拿出十分钟就可以写一篇不错的随笔或者说日记,我还是有点倦怠了。 昨天是4月25,我的生日,但是依旧是很忙碌的一天。零点准时拆了朋友们送的礼物,…...
【股票系统】使用docker本地构建ai-hedge-fund项目,模拟大师炒股进行分析。人工智能的对冲基金的开源项目
股票系统: https://github.com/virattt/ai-hedge-fund 镜像地址: https://gitcode.com/gh_mirrors/ai/ai-hedge-fund 项目地址: https://gitee.com/pythonstock/docker-run-ai-hedge-fund 这是一个基于人工智能的对冲基金的原理验证项目。本项目旨在探讨利用人工智能进行…...
Ollama平替!LM Studio本地大模型调用实战
本文已收录在Github,关注我,紧跟本系列专栏文章,咱们下篇再续! 🚀 魔都架构师 | 全网30W技术追随者🔧 大厂分布式系统/数据中台实战专家🏆 主导交易系统百万级流量调优 & 车联网平台架构&a…...
2024江西ICPC部分题解
题目列表 A - Maliang Learning PaintingC - LiarG - Multiples of 5H - ConvolutionJ - Magic MahjongK - Magic Tree A - Maliang Learning Painting 题目来源:A - Maliang Learning Painting 思路分析 这是个签到题,直接输出abc即可 #include<b…...
RabbitMQ安装流程(Windows环境)
1.下载依赖,Downloads - Erlang/OTP 2.下载RabbitMQ安装包,Installing on Windows | RabbitMQ 3.下载的文件如下 4.安装ErLang依赖 5.安装RabbitMQ 6.RabbitMQ插件管理 6.1 进入Command Prompt命令行界面 6.2 输入rabbitmq-plugins.bat list 查看所有插…...
QT对话框及其属性
Qt中使用QDialog类表示对话框 对话框是一个顶层窗口,出现在程序最上层,用于实现短期任务或者简洁的用户交互 QDialog也是继承自QWidget,可以使用QWidget接口 Qt常用的内置对话框有: 对话框 说明 QFiledialog 文件对话框 QColorDialog 颜色对话框 …...
python怎么查看函数原型及变量是什么类型
python代码中看到一个变量或者对象名,怎么查看这个变量到底是个什么东西,是属性,还是函数,还是模块,还是个包,怎么去查看,要有一个查找流程: 1.可以先用print(变量名)和print(type(变量名)),确认变量是什么类型的参数 2.如果是模块或者类,可以通过dir()函数去查看模…...
住宅代理IP助力大规模数据采集实战
在数字化时代,数据就是燃料,而大规模数据采集则是从信息海洋中提炼价值的关键手段。面对目标网站的严格风控和地域限制,普通代理车轮战往往难以为继——流量一旦被识破,便可能付之东流。这时,住宅代理IP凭借来自真实家…...
【信息融合】卡尔曼滤波EKF毫米波雷达和红外数据信息融合
一、扩展卡尔曼滤波(EKF)的核心原理 EKF是针对非线性系统的改进卡尔曼滤波算法,其核心思想是通过一阶泰勒展开对非线性方程进行局部线性化,并利用雅可比矩阵(Jacobian Matrix)替换线性系统中的状态转移矩阵…...
一篇入门之-评分卡变量分箱(卡方分箱、决策树分箱、KS分箱等)实操例子
一、评分卡分箱-有哪些分箱方法 评分卡的分箱,是指将变量进行离散化。评分卡的分箱方法一般有:等频分箱、等距分箱、卡方分箱、决策树分箱、KS分箱等等。它们都属于自动分箱方法,其中,卡方分箱是实际中最常用的分箱方法。 1.1.等…...
【白雪讲堂】构建与优化企业知识图谱的实战指南
在GEO(生成式引擎优化)时代,知识图谱不仅是企业数据资产的“智慧大脑”,更是连接内容与AI理解之间的核心桥梁。一个高质量的知识图谱,能够显著提高AI平台对企业内容的识别度、相关性与推荐权重,从而在AI搜索…...
作为高速通道光纤传输模式怎么理解以及到底有哪些?
光纤的传输模式主要取决于光纤的结构(如纤芯直径和折射率分布),不同模式对应光波在光纤中传播的不同路径和电磁场分布。以下是光纤传输模式的主要分类及特点: 1. 单模光纤(Single-Mode Fiber, SMF) 核心特点: 纤芯直径极小(通常为 8-10微米),仅允许光以单一模式(…...
setup语法糖
为什么要有setup语法糖: 在选择式API中,一个模块涉及到的数据、方法、声明周期,会比较分撒,位置不集中,不利于解读代码,但是组合式API中的setup函数可以将他们组织在一起,提高了代码的可维护性…...