操作系统程序作业

1、用C语言写一个大规模矩阵遍历的程序,在不同规模的数据上运行,比较按行遍历快还是按列遍历快,解释原因。

#include <stdio.h>
#include <stdlib.h>
#include<time.h>
int main()
{
	const int MAX_ROW = 2048;
	const int MAX_COL = 2048;
	int (*a)[MAX_COL]=(int(*)[MAX_COL])malloc(sizeof(int)*MAX_ROW*MAX_COL);
	clock_t start, finish;

	//先行后列
	start = clock();
	for (int i = 0; i<MAX_ROW; i++)
	for (int j = 0; j<MAX_COL; j++)
		a[i][j] = 1;
	finish = clock();
	printf("The first travel time is %lf ms\n",(double)finish-start);
	//先列后行
	start = clock();
	for (int i = 0; i<MAX_COL; i++)
	for (int j = 0; j<MAX_ROW; j++)
		a[j][i] = 1;
	finish = clock();
	printf("The second travel time is %lf ms\n",(double)finish-start);
    return 0;
}

运行结果:

2048*2048
The first travel time is 9.000000 ms
The second travel time is 53.000000 ms
10000*10000
The first travel time is 239.000000 ms
The second travel time is 543.000000 ms
100000*100000
The first travel time is 43368.000000 ms
The second travel time is 624336.000000 ms

在这里插入图片描述

可以明显看出,按行遍历比按列遍历快。而且矩阵规模越大,按行遍历与按列遍历时间差异越显著。
理论解释:

  1. 首先,可以肯定我的电脑操作系统是按行存储数据的;
  2. CPU高速缓存:维基百科中有以下的内容:CPU高速缓存(英语:CPUCache,在本文中简称缓存)是用于减少处理器访问内存所需平均时间的部件。在金字塔式存储体系中它位于自顶向下的第二层,仅次于CPU寄存器。其容量远小于内存,但速度却可以接近处理器的频率。当处理器发出内存访问请求时,会先查看缓存内是否有请求数据。如果存在(命中),则不经访问内存直接返回该数据;如果不存在(失效),则要先把内存中的相应数据载入缓存,再将其返回处理器。
    缓存从内存中抓取一般都是整个数据块,所以它的物理内存是连续的,几乎都是同行不同列的,而如果内循环以列的方式进行遍历的话,将会使整个缓存块无法被利用,而不得不从内存中读取数据,而从内存读取速度是远远小于从缓存中读取数据的。随着数组元素越来越多,按列读取速度也会越来越慢。
  3. 分页调度:物理内存是以页的方式进行划分的,当一个二维数组很大是如int[128][1024],假设一页的内存为4096个字节,而每一行正好占据内存的一页,如果以列的形式进行遍历,就会发生128*1024次的页面调度,而如果以行遍历则只有128次页面调度,而页面调度是有时间消耗的,因而调度次数越多,遍历的时间就越长。

2、下列程序是否会出现死循环?为什么?在你的PC上,调用f(60)所需要的时间大概是多少?

int f(int x){
    int s = 0;
    while(x++>0) s+=f(x);
    return fmax(s,1);
}

程序是否会出现死循环?

调用f(60)时,入口参数x=60。从数学的角度理解while中的判断表达式“x++ >0”,会认为x在增量后永远大于0,这是一个永真式,从而做出错误结论:程序死循环。在计算机中数值是有范围的,int型数据用补码表示,占4个字节,能表示的最大正数是231-1 = 7FFF FFFFH。231的机器数是8000 0000H,其值为int型,能表示的最小负数-2147483648,因此当x = 8000 0000H 时,x > 0的值为假,程序退出while循环,因此,若不考虑栈溢出,则程序能执行结束。
f(60)在64位系统中的实际执行情况

假设在Intel x86+windows+VC+C语言环境中执行f(60)。VC中默认分配栈的大小是1MB,虽然用户可以调整栈大小,但栈的容量是有限的。按2MB的栈空间、栈大小按80字节计算:2MB÷80B≈26214,因此f(x)递归调用的次数不会超过26214-1=26213次。从图中可以看出,栈溢出时,f(x)函数体最多执行26213次。栈溢出时每个f(x)函数体只在while语句中执行,假设每个f(x)函数体执行100条指令,即使指令平均CPI为3,时钟频率为2.4GHz,f(60)的执行时间也只有26213×100×3÷2.4GHz ≈3.2 ms左右时间。
在这里插入图片描述
对f(60)的执行做了测试,在栈大小是1MB时,递归调用11244次后栈溢出;在栈设置为2MB时,递归调用22642次后栈溢出,显然运行时间只有几毫秒。在Microsoft VisualStudio 2019环境中运行,出现如图所示结果,表明出现了栈溢出(Stack overflow)。

3、写一个自启动程序,输出你的学号、姓名。要求代码有详细注释。

第一种方式

emm,写了个开机自启动程序,其实跟代码没什么关系,代码就几行:

#include <stdio.h> 
  int main() 
  { 
    printf("学号:2007040128 姓名:张涵"); 
   return 0; 
  } 

那么是如何实现自启动的呢?将该程序生成的exe文件直接放入开机自启动的位置就可以了
在这里插入图片描述
首先,win+R,将运行的对话框打开,在运行对话框输入shell:startup
在这里插入图片描述
确定之后,就会进入C:\Users\lennovo\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup,就是你的开机自启动程序位置
然后将上面的.exe文件拖入到这个目录下,就可以了

第二种方式

将程序写入注册表设置自启动的代码:

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>

void ComputerStart(char *pathName)
{
	//找到系统的启动项
	char *szSubKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
	HKEY hKey;

	//打开注册表启动项
	int k = RegOpenKeyExA(HKEY_CURRENT_USER, szSubKey, 0, KEY_ALL_ACCESS, &hKey);
	if (k == ERROR_SUCCESS)
	{
		//添加一个子Key,并设置值,MyStart为启动项名称,自定义设置;
		RegSetValueEx(hKey, "MyStart", 0, REG_SZ, (BYTE *)pathName, strlen(pathName));
		//关闭注册表
		RegCloseKey(hKey);
		printf("设置成功\n");
	}
	else
	{
		printf("设置失败  error:%d\n", k);
	}
}

int main()
{
	char pathName[MAX_PATH];//文件名字最大260个字符  MAX_PATH  260
	GetCurrentDirectory(MAX_PATH, pathName);//设置字符集为多字节字符集  获取当前文件路径

	sprintf(pathName, "%s\\", pathName);
	strcat(pathName, "print.exe");//找到需要开机自启动的程序

	ComputerStart(pathName);


	system("pause");
	return 0;
}

可以在任务管理器中查看启动项已经添加:
在这里插入图片描述

4、比较以下两个函数的性能

函数一依次使用两个循环,遍历两个数组并进行累加;
函数二仅使用一个循环,遍历两个数组并进行累加。

int f1(int* p, int* q, int n)
{
	int sum = 0;
	for (int i = 0; i < n; i++)
	{
		sum += p[i];
	}
	for (int i = 0; i < n; i++)
	{
		sum += q[i];
	}
	return sum;
}

int f2(int* p, int* q, int n)
{
	int sum = 0;
	for (int i = 0; i < n; i++)
	{
		sum += p[i];
		sum += q[i];
	}
	return sum;
}

运行结果:

数组大小100000000
函数一运行时间为0.415000s
函数二运行时间为0.234000s
数组大小1000000
函数一运行时间为0.004000s
函数二运行时间为0.002000s

在这里插入图片描述

在这里插入图片描述

如果仅从按行访问原理来看,对于连续遍历一个一维数组,程序在访问了第一个元素之后,接下来的连续多个元素都被存放在了cache中,因此接下来的访问命中率会很高的,这就大大加快了程序运行的速度,这样的话函数一的运行时间应小于函数二运行的时间。
但是,实验结果表明,函数一的运行时间是大于函数二的运行时间的。原因模块一使用了两个循环,模块二使用了一个循环,而循环涉及到大量指令的跳转,因此消耗时间较长,导致实际运行函数一的运行时间大于函数二运行的时间。

5、编译原理的应用介绍

编译原理,说得通俗易懂一些就是:让机器通过某种机制和规则,将一种由人们书写的高级程序代码,经过若干步骤,最终翻译成机器可理解执行的二进制代码。
编译原理技术的具体应用,例如:
(1)我们用户通常编写的 C/C++ 程序源代码(.C/.CPP),通过 Microsoft Visual C++ 编译器,将由人工书写的 C/C++ 语言程序源代码(.C/.CPP),最终翻译成机器可执行的二进制代码(*.EXE);
(2)人工智能领域中的自然语言处理、机器翻译技术(例如:英/汉翻译、日/汉翻译系统等)等,都需要使用到编译原理技术。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 多道程序作业调度是操作系统中的重要内容,它的主要任务是合理地安排和调度多个作业的执行顺序,以提高计算机系统的效率和吞吐量。 在CSDN操作系统实验中,多道程序系统的作业调度主要包括几个方面: 1. 批处理作业调度:批处理作业调度是指按照一定的算法和策略,根据作业的属性和优先级,合理安排作业的执行顺序。常见的批处理作业调度算法有先来先服务(FCFS)、短作业优先(SJF)、优先级调度等。这些算法通过优化作业的执行顺序,可以减少作业的等待时间、提高系统的运行效率。 2. 资源分配和回收:多道程序系统中,各个作业需要共享计算机的资源,如内存、CPU等。作业调度需要合理地分配这些资源,并在作业执行完毕后进行回收。通过优化资源的分配和回收,可以最大限度地提高计算机系统的资源利用率,避免资源的浪费。 3. 死锁避免和处理:多道程序系统在调度作业时,可能会出现死锁的情况。作业调度需要具备避免和处理死锁的能力,及时检测和解决死锁问题,以保证系统的正常运行。 总之,在csdn操作系统实验中,多道程序系统作业调度是一个复杂而重要的任务。通过合理地安排作业的执行顺序、优化资源的分配和回收以及避免和处理死锁等措施,可以提高计算机系统的效率和吞吐量,从而更好地满足用户的需求。 ### 回答2: CSDN操作系统实验中的多道程序系统作业调度是指在一个计算机系统中同时执行多个作业的调度方式。在多道程序系统中,多个作业可以同时进入内存并执行,从而提高了系统的资源利用率和作业处理的效率。 作业调度是指在多道程序系统中选择下一个要执行的作业的过程。作业调度的目标是提高作业的响应时间、提高系统的吞吐量和优化系统的资源利用率。常见的作业调度算法有先来先服务(FCFS)、最短作业优先(SJF)、优先级调度、时间片轮转等。 先来先服务是最简单的作业调度算法,在队列中的作业按照到达顺序依次执行,没有考虑作业的执行时间。这种调度算法对于长作业而言响应时间较长,但是对于短作业而言,吞吐量较高。 最短作业优先算法是根据作业的执行时间来决定下一个执行的作业,执行时间短的作业有更高的优先级。这种调度算法能够提供较短的响应时间和较高的吞吐量,但是可能会导致长作业的饥饿。 优先级调度是根据作业的优先级来决定下一个执行的作业,优先级较高的作业被优先执行。这种调度算法比较灵活,可以根据系统的需要对作业进行调度,较高的优先级可以保障较短作业的响应时间。 时间片轮转调度算法是将CPU的执行时间划分为多个时间片,每个作业在一个时间片内轮流执行,如果某个作业没有完成,则切换到下一个作业执行。这种调度算法可以公平地分配CPU资源,并且可以较好地平衡响应时间和吞吐量。 总之,CSDN操作系统实验中的多道程序系统作业调度通过选择合适的调度算法,可以提高作业的响应时间、提高系统的吞吐量和优化系统的资源利用率。不同的调度算法有着不同的特点和适用场景,需要根据具体的需求来选择合适的调度策略。 ### 回答3: 多道程序系统作业调度是操作系统中的重要任务,旨在合理地分配系统资源,提高系统的利用率和性能。 在CSDN的操作系统实验中,多道程序系统作业调度主要包括进程队列管理和调度策略的设计。进程队列管理主要负责管理和维护系统中所有进程的状态,以及将进程按照一定的规则组织为不同的队列。调度策略则决定了系统对这些队列中的进程进行调度的顺序和方式。 在多道程序系统中,通常会将进程划分为不同的优先级队列,例如高优先级队列、中优先级队列和低优先级队列。通过设置不同优先级的队列,并根据进程的优先级将其放入相应的队列中,可以实现对进程的优先调度。 在作业调度时,可以采用不同的调度算法,如先来先服务(FCFS)、短作业优先(SJF)、高响应比优先(HRRN)等。这些算法根据作业的特性和系统的需要,决定了进程是否会等待以及如何进行调度。 此外,多道程序系统作业调度还可以考虑进程的并发执行情况,即在一个时间片内,是否应该启动多个进程并让它们并发执行。这涉及到进程的抢占和非抢占调度,以及调度算法的选择。 综上所述,CSDN的操作系统实验中的多道程序系统作业调度涉及进程队列管理、调度策略的设计、优先级调度、调度算法的选择以及进程的并发执行等方面。这些内容都是为了合理利用系统资源,提高系统的性能和效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

.别拖至春天.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值