CCF-CSP第36次认证第三题 --《缓存模拟》20分思路

题目背景

西西艾弗岛半导体制造厂近期正在研发一种新型的处理器产品。该处理器的缓存,计划采用组相连的方式。 为了选定合适的组相连参数,我们需要对缓存的工作过程进行模拟,进而推算其性能。

处理器的缓存,存储着内存中的部分数据。当处理器的运行需要访问内存中的数据时,如果所需数据已经存储在缓存中,则可以用更为快捷的缓存访问代替内存访问,来提高处理器性能。

处理器的缓存包含若干缓存行,每个缓存行存储特定大小的数据。为了便于叙述,我们认为处理器对内存的访问,也是以缓存行为单位进行的。 以缓存行的大小为单位,将全部内存空间划分为若干块(编号从 00 开始),这样每个内存块的数据便可以恰好存储在一个缓存行中。

nn-路组相联是这样的一种缓存结构:每 nn 个缓存行划分为一组。假设共有 NN 个这样的(编号从 00 到 N−1N−1),那么编号为 kk 的内存块仅可以被存储在编号为 (k÷n)(k÷n) 的这 nn 个缓存行的任意一个中。其中,÷÷ 表示忽略余数的整除运算, 表示整除取余数运算。一般而言,nn 和 NN 是 22 的幂次。例如,当 n=4n=4、N=8N=8 时,编号为 0、1、2、3、32、33、34、35 的内存块可以被存储在 0 的任意缓存行中;编号为 4、5、6、7、36、37、38、39 的内存块可以被存储在 1 的任意缓存行中。

img

题目描述

具体而言,给定要读取写入的内存块编号,即可确定该内存块可能位于的缓存行组的编号。此时,可能存在的情况有两种:

  • 该缓存行组的某个缓存行已经存储了该内存块的数据,即命中
  • 该缓存行组的所有缓存行都没有存储该内存块的数据,即未命中

当发生命中时,处理器可以直接使用或修改该缓存行中的数据,而不需要实际读写内存。 当发生未命中时,处理器需要从内存中读取数据,并将其存储到该缓存行组中的一个缓存行中,然后再使用或修改该缓存行中的数据。这个将内存中的数据读入到缓存的过程称为载入

当执行载入操作时,如果该缓存行组中有尚未存储数据的缓存行,那么将数据存储到其中一个尚未存储数据的缓存行中,并在缓存行中记录所存储的数据块的编号;否则,按照一定方法,选择该组中的一个缓存行,并将数据存储到其中,这一过程称为替换

当发生替换时,需要考虑被替换的缓存行是否发生过修改,即执行过操作。如果发生过修改,则需要先将缓存行中的数据写入内存中的对应位置;然后忽略该缓存行中的数据、将新读入的数据存入其中,并记录所存储数据块的编号。

在一个缓存行组中选择被替换的缓存行的方法有很多种,该种处理器采用的是最近最少使用(LRU)方法。该方法将一个缓存行组中存有数据的缓存行排成一队,每次读或写一个缓存行时,都将该缓存行移动到队列的最前端。当需要替换缓存行时,选择队列的最后一个缓存行(最久没被读写)进行替换。

本题目中,将给出一系列的处理器运行时遇到的对内存的读写指令,并假定初始时处理器的缓存为空。你需要根据上文描述的缓存工作过程,给出处理器对内存的实际读写操作序列。

输入格式

从标准输入读入数据。

输入的第一行包含空格分隔的三个整数 n,N,qn,N,q,分别表示组相联的路数 nn 和组数 NN,以及要处理的读写指令的数量 qq。

接下来 qq 行,每行包含空格分隔的两个整数 oo 和 aa。其中,oo 表示读写指令的类型,aa 表示要读写的内存块的编号。oo 为 00 或 11,分别表示读和写操作。

输出格式

输出到标准输出。

输出若干行,每行包含空格分隔的两个整数 o′o′ 和 a′a′,表示实际处理器对内存的读写操作。o′o′ 为 00 或 11,分别表示读和写操作;a′a′ 表示要读写的内存块的编号。

样例输入

4 8 8
0 0
0 1
1 2
0 1
1 0
0 32
1 33
0 34

样例输出

0 0
0 1
0 2
0 32
1 2
0 33
0 34

样例解释

该处理器的缓存为 4 路组相联,共有 8 组。初始时,处理器的缓存为空。共需要处理 8 条指令:

  • 第 1 条指令为读取内存块 0,未命中,要实际读取内存块 0,并存储到组 0 的一个缓存行;
  • 第 2 条指令为读取内存块 1,未命中,要实际读取内存块 1,并存储到组 0 的另一个缓存行;
  • 第 3 条指令为写入内存块 2,未命中,要实际读取内存块 2,并存储到组 0 的第三个缓存行,然后根据指令在缓存中对其进行修改;
  • 第 4 条指令为读取内存块 1,命中,直接从缓存中调取数据;
  • 第 5 条指令为写入内存块 0,命中,直接修改缓存中的数据;
  • 第 6 条指令为读取内存块 32,未命中,要实际读取内存块 32,并存储到组 0 的第四个缓存行;
  • 第 7 条指令为写入内存块 33,未命中,此时组 0 中的 4 个缓存行都保存了数据:最近使用的是保存有内存块 32 的缓存行,其次是保存有内存块 0 的缓存行,然后是 1,最后是 2,因此选择替换保存有内存块 2 的缓存行。注意到该缓存行在执行第 3 条指令时被修改过,因此需要先将其写入内存,然后再读取内存块 33 的数据存储到该缓存行中;
  • 第 8 条指令为读取内存块 34,未命中,此时组 0 中的 4 个缓存行都保存了数据,按照同样的办法,选择保存有内存块 1 的缓存行替换。注意到该缓存行未被修改过,因此可以直接读取内存块 34 的数据存储到该缓存行中。

子任务

对于 20% 的数据,有 n=1,N=1n=1,N=1;

对于 40% 的数据,有 n=1n=1;

另外 20% 的数据,有 N=1,q≤nN=1,q≤n;

对于 100% 的数据,有 1≤n,N,n×N≤655361≤n,N,n×N≤65536,且 n,Nn,N 为 2 的幂次;1≤q≤1051≤q≤105,0≤a<2300≤a<230。

题解

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n, N, q;
    cin>>n>>N>>q; //默认只考虑n=1, N=1的样例。所以只有一个缓存
    
    bool flag = false;// 标记修改
    int memory = -1; // 缓存的内存块编号
    
    while(q--){
        int o, a;
        cin>>o>>a;
        if(a == memory){ //如果缓存为a
            if(o == 1) flag = true; //写操作 标记 修改
        }
        else{
            if(memory == -1){
                if(o == 0){
                    memory = a;
                    flag = false;
                    cout<<0<<" "<<a<<endl;
                }
                else{
                    memory = a;
                    flag = true;
                    cout<<0<<" "<<a<<endl;
                }
            }
            else{
                if(flag){
                    cout<<1<<" "<<memory<<endl;
                    if(o == 0){
                        memory = a;
                        flag = false;
                        cout<<0<<" "<<a<<endl;
                    }
                    else{
                        memory = a;
                        flag = true;
                        cout<<0<<" "<<a<<endl;
                    }
                }
                else{
                    memory = a;
                    if(o == 0){
                        flag = false;
                    }
                    else{
                        flag = true;
                    }
                    cout<<0<<" "<<a<<endl;
                }
            }
        }
    }
    return 0;
}

 复习

#include <bits/stdc++.h>
using namespace std;

int main()
{
	int n,N,q;
	cin>>n>>N>>q;
	int flag = 0;//修改
	int x = -1;//缓存行数据
	while(q--){
		int o,a;
		cin>>o>>a;
		if(x == -1){
			cout<<0<<" "<<a<<endl;
			x = a;
			if(o == 1){
				flag = 1;
			}
			if(o == 0){
				flag = 0;
			}
		}
		else if(x != -1){
			if(a == x){
				if(o == 1){
					flag = 1;
				}
//				if(o == 0){
//					flag = 0;
//				}
				//此处为什么不要?
				continue;
			}
			else{
				if(flag == 1){
					cout<<1<<" "<<x<<endl;
				}
				cout<<0<<" "<<a<<endl;
				x = a;
				if(o == 1){
					flag = 1;
				}
				if(o == 0){
					flag = 0;
				}
			}
		}
	}
	return 0;
}

### CSP缓存行为的模拟与测试 为了理解并模拟CSP(内容安全策略)中的缓存机制,特别是针对`nonce-{random}`实现方式下的攻击面,可以采取以下几种技术手段: #### 构建测试环境 创建一个支持自定义HTTP响应头设置的Web服务器实例。这可以通过多种编程语言和技术栈完成,例如Node.js配合Express框架或者Python搭配Flask库。 ```javascript // 使用 Node.js 和 Express 设置具有特定 CSP 头部的服务端代码片段 const express = require('express'); const app = express(); app.use((req, res, next) => { const nonceValue = 'some-random-nonce-value'; // 这里应该是一个随机生成器产生的唯一值 res.setHeader( 'Content-Security-Policy', `default-src 'self'; script-src 'self' 'nonce-${nonceValue}'` ); req.nonce = nonceValue; next(); }); app.get('/', (req, res) => { res.send(` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CSP Test Page</title> </head> <body> <!-- 正常脚本 --> <script nonce="${req.nonce}"> console.log("This is a valid inline script."); </script> <!-- 尝试注入恶意脚本 --> <img src=x onerror=alert(1)> </body> </html>` ); }); ``` 此段代码展示了如何配置一个简单的Web应用来发送带有指定`nonce`参数的内容安全策略头部[^1]。 #### 利用浏览器开发者工具监控网络请求 现代浏览器提供了强大的开发人员工具集,允许查看页面加载期间发出的所有HTTP请求及其对应的响应头信息。通过这些工具能够观察到实际生效的安全策略以及任何潜在绕过的迹象。 #### 缓存管理实验 尝试不同的场景组合以探索缓存对于CSP的影响: - 修改服务端逻辑使得每刷新都会返回不同版本的HTML文档连同更新后的`nonce`; - 记录下第一访问时获取到的具体`nonce`值,并将其硬编码进后续几重访过程中提交的数据包中; - 对比前后两渲染的结果差异,验证是否存在因缓存而导致的安全漏洞被触发的情况。 上述过程有助于加深对基于浏览器存储特性的新型XSS威胁的认识,同时也提醒开发者们在设计应用程序时需更加谨慎地处理动态资源的身份验证令牌等问
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值