过期的保研复试
前言:昨天突然通知,学校的保研复试取消了,准备了近两个月的机试和面试没的了,加权差劲的我竟然还幻想着准备搏一把复试翻盘,结果今年直接给取消了23333,保研之路就此凉凉。之前准备了一些基础的板子还打算总结好再发上来复习。这下可好,面试的项目也白准备了5555
幸福来的就是这么突然,考研还有95天,比较仓促,有时间再补全这篇博客吧,先发上来,也算自己的保研之路告一段落,去准备考研了。
机试板子
数论
数学公式
三角形外接圆圆心
double A1=2*(x[1]-x[0]);
double B1=2*(y[1]-y[0]);
double C1=x[1]*x[1]+y[1]*y[1]-x[0]*x[0]-y[0]*y[0];
double A2=2*(x[2]-x[1]);
double B2=2*(y[2]-y[1]);
double C2=x[2]*x[2]+y[2]*y[2]-x[1]*x[1]-y[1]*y[1];
double ax=((C1*B2)-(C2*B1))/((A1*B2)-(A2*B1));
double ay=((A1*C2)-(A2*C1))/((A1*B2)-(A2*B1));
三角形内切圆圆心
double dis(double x1,double y1,double x2,double y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
double l1=dis(x[0],y[0],x[1],y[1]);
double l2=dis(x[1],y[1],x[2],y[2]);
double l3=dis(x[0],y[0],x[2],y[2]);
double sum=l1+l2+l3;
double ax=(x[2]*l1+x[0]*l2+x[1]*l3)/sum;
double ay=(y[2]*l1+y[0]*l2+y[1]*l3)/sum;
快速幂取模
typedef long long ll;
ll fpow(ll a,int p){
ll base=a%MOD;
ll res=1;
while (p){
if (p&1)res=res*base%MOD;
p>>=1;
base=base*base%MOD;
}
return res;
}
求逆元
//求b的逆元
int inv=fpow(b,MOD-2);
//方法2//扩展欧几里得算法
ll exgcd(ll a,ll b,ll &x,ll &y) {
if(b==0) {
x=1,y=0;
return a;
}
ll ret=exgcd(b,a%b,y,x);
y-=a/b*x;
return ret;
}
ll getInv(int a,int mod) {
//求a在mod下的逆元,不存在逆元返回-1
ll x,y;
ll d=exgcd(a,mod,x,y);
return d==1?(x%mod+mod)%mod:-1;
}
中国剩余定理
ll crt(ll a[], ll m[], ll n) {
ll M = 1;
for (int i = 0; i < n; i++){
M *= m[i];
}
ll ret = 0;
for (int i = 0; i < n; i++) {
ll x, y;
ll tm = M / m[i];
exgcd(tm, m[i], x, y);
ret = (ret + tm * x * a[i]) % M;
}
return (ret + M) % M;
}
扩展中国剩余定理
ll excrt(ll a[],ll b[],ll n) {
ll m1,r1,m2,r2,flag,i,d,x,y,c,t;
m1=a[0],r1=b[0];
//x==b1 mod a1
//x=a0*k0+b0
//x=a1*k1+b1
flag=0;
for(i=1; i<n; i++) {
m2=a[i],r2=b[i];
if(flag)
continue;
d=exgcd(m1,m2,x,y);//d=exGcd(m1,m2);x*m1+y*m2=d;
c=r2-r1;//gcd(a0,a1)|b1-b0
if(c%d) { //对于方程m1*x+m2*y=c,如果c不是d的倍数就无整数解
flag=1;
continue;
}
t=m2/d;
//对于方程m1x+m2y=c=r2-r1,
//若(x0,y0)是一组整数解,
//那么(x0+k*m2/d,y0-k*m1/d)也是一组整数解(k为任意整数)
//其中x0=x*c/d,y0=x*c/d;
x=(c/d*x%t+t)%t;
//保证x0是正数,因为x+k*t是解,
//(x%t+t)%t也必定是正数解(必定存在某个k使得(x%t+t)%t=x+k*t)
r1=m1*x+r1;
//新求的r1就是前i组的解,Mi=m1*x+M(i-1)=r2-m2*y
//(m1为前i个m的最小公倍数);对m2取余时,余数为r2;
//对以前的m取余时,Mi%m=m1*x%m+M(i-1)%m=M(i-1)%m=r
m1=m1*m2/d;
}
if(flag)
return -1;
if(n==1&&r1==0)
return m1;//结果不能为0
return r1;
}
gcd
int gcd(int a,int b){
return !b ? a : gcd(b,a%b);
}
素数筛
pos=0;
for (int i=2;i<MAXN;i++){
if (!prime[i]){
arr[pos++]=i;
}
for (int j=0;j<pos && i*arr[j]<MAXN;j++){
prime[i*arr[j]] = true;
if (i%arr[j]==0)break;
}
}
组合数公式
参考资料:常用组合数计算公式
图论
链式前向星
int head[MAXE];
struct node {
int nx,to,w;
}eage[MAXN];
int tot;
void init(){
tot=0;
memset(head,-1,sizeof(head));
}
void add(int x,int y,int w){
eage[++tot].nx=head[x];
eage[tot].to=y;
eage[tot].w=w;
head[x]=tot;
}
最短路
floyed
for (int k=0;k<n;k++){//枚举中间点在最外面
for (int i=0;i<n;i++){
for (int j=0;j<n;j++){
maps[i][j]=min(maps[i][j],maps[i][k]+maps[k][j]);
}
}
}
dijkstra
void dijkstra(int s,int n){
dis[s]=0;
int mark,mi;
for (int k=0;k<n;k++){
mi=INF;
for (int i=1;i<=n;i++){
if (!vis[i] && mi>dis[i]){
mi=dis[i];
mark=i;
}
}
vis[mark]=true;
for (int i=head[mark];i!=-1;i=eage[i].nx){
if (dis[mark]+eage[i].w<dis[eage[i].to]){
dis[eage[i].to]=dis[mark]+eage[i].w;
}
}
}
}
spfa
//spfa检测负环,入队次数大于n则存在负环
bool spfa(int s,int n){
queue<int>q;
q.push(s);
dis[s]=0;
inq[s]++;
vis[s]=true;
while (!q.empty()){
int t=q.front();
q.pop();
vis[t]=false;
for (int i=head[t];i!=-1;i=eage[i].nx){
int v=eage[i].to;
if (dis[t]+eage[i].w<dis[v]){
dis[v]=dis[t]+eage[i].w;
inq[v]++;
vis[v]=true;
if (inq[v]>n){
return false;
}
q.push(v);
}
}
}
return true;
}
线段树
注意:
- 区间长度 = r - l +1,一定要+1,
树状数组
typedef long long ll;
int lowbit(int x){
return (-x)&x;
}
int add(int x,int val){
while (x<MAXN){
tree[x]+=val;
x+=lowbit(x);
}
}
ll sum(int x){
ll res=0;
while (x>0){
res+=tree[x];
x-=lowbit(x);
}
return res;
}
差分数组+线段树
模板题:牛客网:小阳的贝壳
最近点对
double nearPoints(int l,int r){
//区间只剩两个点的时候
if (r-l==1){
return dis(arr[l],arr[r]);
}else if (r-l==2){//区间只剩三个点的时候
double mi=min( dis(arr[l],arr[r]), dis(arr[l+1],arr[r]) );
mi=min(dis(arr[l],arr[l+1]), mi );
return mi;
}
int mid=(l+r)>>1;
double dl=nearPoints(l,mid);
double dr=nearPoints(mid+1,r);
double d=min(dl,dr);
//找中线左边在范围d内的点
for (int i=mid-1;i>=l;i--){
if (arr[mid].x-arr[i].x>d){
l=i;
break;
}
}
//找右边边在范围d内的点
for (int i=mid;i<=r;i++){
if (arr[i].x-arr[mid-1].x>d){
r=i;
break;
}
}
int pos=0;
for (int i=l;i<=r;i++){
save[pos++]=arr[i];
}
//找到的点按照y轴排序
sort(save,save+pos,cmp2);
for(int i=0; i<pos; i++) {
for(int j=i+1; j<pos; j++) {
//剪枝!!!
//当前的(i,j)如果不满足,就没必要继续往后面找了,因为y是升序排列
if(save[j].y-save[i].y>d){
break;
}
if(dis(save[i],save[j])<d){
d=dis(save[i],save[j]);
}
}
}
return d;
}
#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
#define MAXN 101000
struct node{
double x,y;
}arr[MAXN],save[MAXN];
double dis(node xx,node yy){
return sqrt((xx.x-yy.x)*(xx.x-yy.x)+(xx.y-yy.y)*(xx.y-yy.y))/2;
}
bool cmp(node a,node b){
return a.x<b.x;
}
bool cmp2(node a,node b){
return a.y<b.y;
}
double nearPoints(int l,int r){
if (r-l==1){
return dis(arr[l],arr[r]);
}else if (r-l==2){
double mi=min( dis(arr[l],arr[r]), dis(arr[l+1],arr[r]) );
mi=min(dis(arr[l],arr[l+1]), mi );
return mi;
}
int mid=(l+r)>>1;
double dl=nearPoints(l,mid);
double dr=nearPoints(mid+1,r);
double d=min(dl,dr);
for (int i=mid-1;i>=l;i--){
if (arr[mid].x-arr[i].x>d){
l=i;
break;
}
}
for (int i=mid;i<=r;i++){
if (arr[i].x-arr[mid-1].x>d){
r=i;
break;
}
}
int pos=0;
for (int i=l;i<=r;i++){
save[pos++]=arr[i];
}
sort(save,save+pos,cmp2);
for(int i=0; i<pos; i++) {
for(int j=i+1; j<pos; j++) {
if(save[j].y-save[i].y>d){
break;
}// 这步优化很是重要
if(dis(save[i],save[j])<d){
d=dis(save[i],save[j]);
}
}
}
return d;
}
int main (){
int n;
while (scanf("%d",&n)!=EOF){
if (!n)break;
for (int i=0;i<n;i++){
scanf("%lf%lf",&arr[i].x,&arr[i].y);
}
sort(arr,arr+n,cmp);
printf("%.2f\n",nearPoints(0,n-1));
}
return 0;
}
参考资料:
字符串处理
C++
获取子字符串
string str="123";
//temp为从str[1]开始的两个字符
//temp="23"
string temp=str.substr(1,2);
反转字符串
string str="123";
reverse(str.begin(),str.end());
//str="321"
查找字符串的位置
返回第一次出现的位置,没有则返回-1(相当于string::npos)
string str="123";
int p1=str.find("4");
cout<<p1<<endl;
//p1=-1,123中没有字符串4
int p2=str.find('2');
cout<<p2<<endl;
//p2=1,字符串123中,字符2在第1位
p2=str.find('1',1);
cout<<p2<<endl;
//p2=-1,从第一位开始寻找字符1(字符1在第0位),所以返回-1
int p3=str.find("23");
cout<<p3<<endl;
//p3=1,从第0位开始寻找字符串23
关于nops的介绍可以参考:C++中string::npos的一些用法总结
带有空行的输入
//输入一个
string str;
getline(cin,str);
//输入n个,注意使用getchar消除回车
int n;
while (cin>>n){
getchar();
for (int i=0;i<n;i++){
string s;
getline(cin,s);
}
}
字符串分割
//以空格为例
string str="1 2 3 4";
int r=str.find(' ');
int l=0;
while (r!=-1){
string temp;
if (l==0){
temp=str.substr(l,r-l);
}else {
temp=str.substr(l+1,r-l-1);
}
//cout<<temp<<"??"<<endl;
l=r;
r=str.find(' ',r+1);
}
string temp=str.substr(l+1,str.size()-l-1);
//cout<<temp<<"??"<<endl;
追加字符串
string a="123";
a.append(2,'0');
//a="12300",追加2个字符0
string b="4567";
string a="123";
a.append(b);
//a="1234567",追加字符串b
string a="123";
string b="4567";
a.append(b,2,1);
//a="1236",追加b的下标2位置后的一个字符
字符串插入
在index位置插入count个字符ch
string a="123";
a.insert(1,2,'w');
//a=1ww23
KMP
void getNext(string p){
int len=p.size();
int j=0,k=-1;
nex[0]=-1;
while (j<len){
if (k==-1 || p[j]==p[k]){
if (p[++j]==p[++k]){
nex[j]=nex[k];
}else {
nex[j]=k;
}
}else {
k=nex[k];
}
}
}
int kmp(string s,string p){
int len=p.size(),len2=s.size();
int i=0,j=0;
while (i< len2 && j < len){
if (j==-1 || s[i]==p[j]){
i++;
j++;
}else {
j=nex[j];
}
}
if (j>=len){
return i-j+1;
}else {
return -1;
}
}
面试专业课
计网
-
MAC address = LAN address = Physical address 物理地址 = Ethernet address = 硬件地址
-
Internet提供的主要服务有万维网(WWW)、文件传输(FTP)、电子邮件(E-mail)、远程登录(Telnet)、手机 (3GHZ) 等。
-
NetBIOS服务则是在会话层的服务。
-
.mil是军用机构顶级域名。.com是商业机构顶级域名,.gov是政府机构顶级域名。.cn是中国顶级域名。
-
PPP是链路层协议
ICMP是TCP/IP协议簇的一个子协议,属于网络层协议
OSPF是路由协议
SMTP是应用层协议
-
虚电路是分组交换的两种传输方式中的一种。在通信和网络中,虚电路是由分组交换通信所提供的 面向连接的通信服务 。在两个节点或应用进程之间建立起一个逻辑上的连接或虚电路后,就可以在两个节点之间依次发送每一个分组,接收端收到分组的顺序必然与发送端的发送顺序一致,因此接收端无须负责在接收分组后重新进行排序。虚电路协议向高层协议隐藏了将数据分割 成段,包或帧的过程。
-
集线器的主要功能是: 对接收到的信号进行再生整形放大,以扩大网络的传输距离,同时把所有节点集中在以它为中心的节点上。
集线器在发送数据给下层设备时,不分原数据来自何处,将所得数据发给每一个端口,假如其中有端口需要来源的数据,就会处于接收状态,而不需要的端口就处于拒绝状态。
-
数据链路层,帧
网络层,数据报
运输层,报文段
应用层,报文
-
物理层:中继器、集线器
链路层:网桥、交换机
网络层:路由器
-
ISP(Internet Service Provider),互联网服务提供商
ICP(Internet Content Provider),互联网内容提供商
IDC (Internet Data Center),互联网数据中心
ASP(Active Server Pages),动态服务器页面
-
会话ID存在于Session中,关闭浏览器,Session仍然保持,直至会话过期。但是关闭浏览器并不意味着会话ID丢失,因为与Session建立联系的是cookie,默认的cookie是在浏览器关闭时删除的,但是可以设置expire属性让cookie在浏览器关闭时不删除
-
调制,就是把数字信号转换成电话线上传输的模拟信号;解调,即把模拟信号转换成数字信号,合称调制解调器。
OSI定义了网络互连的七层(物理层、数据链路层、网络层、传输层、会话层、表示层、应用层),即ISO开放互连系统参考模型。每一层实现各自的功能和协议,并完成与相邻层的接口通信。
-
IP服务的三个特点为不可靠、面向无连接和尽最大努力投递。
IP由IP协议控制传输的协议单元称为IP数据报。IP协议屏蔽下层各种物理网络的差异,向上层(主要是TCP层或UDP层)提供统一的IP数据报。作为一种互联网协议,运行于网络层,屏蔽各个物理网络的细节和差异,为其高层用户提供如下3种服务:- 不可靠的数据投递服务。数据报的投递没有任何品质保证,数据报可能被正确投递,也可能被丢弃;
- 面向无连接的传输服务。这种方式不管数据报的传输经过哪些结点,甚至可以不管数据报起始和终止的计算机。数据报的传输可能经过不同的传输路径,而且这些数据报在传输过程中有可能丢失,也有可能正确传输到目的结点;
- 尽最大努力投递服务。IP不会随意丢包,除非系统的资源耗尽、接收出现错误、或者网路出现故障的情况下,才不得不丢弃报文
-
异步传输模式(Asynchronous Transfer Mode,ATM),是一种面向连接的快速分组交换技术,建立在异步时分复用基础上,并使用固定长度的信元,支持包括数据、语音、图象在内的各种业务的传送,半导体和光纤技术为ATM的快速交换和传输提供坚实的保障
ATM是一种为了多种业务设计的通用的面向连接的传输模式。它适用于局域网和广域网。
它具有高速数据传输率和支持许多种类型如声音、数据、传真、实时视频、CD质量音频和图像的通信。 -
汇接局指的是端局不直接连接,而是依靠汇接局连在一起。在这里,端局相当于一个局域网,汇接局相当于一个路由。端局内的主机要想联网得走汇接局。这里说的是中型城市。
小城市直接端局全覆盖了
大城市比中型城市要复杂,还要在汇接局的上面再加上一层汇接局。
-
ARP协议:根据IP地址获取MAC地址
RAPR协议:根据MAC地址获取IP地址
DNS协议:域名(主句名)映射至IP地址,(端口53)DNS使用传输层的UDP而非TCP ,最主要是不需要发生错误时的自动重传功能,而且UDP采用面相无连接的协议,速度快
DHCP:为接入网络设备动态分配IP地址(内网IP)
-
静态路由需要人手工配置,是在路由器中设置的固定的路由表,不适应网络的变化更新;
动态路由它不需要手工配,是网络中的路由器之间相互通信,传递路由信息,利用收到的路由信息更新路由器表的过程,可以动态更新,能适应各种环境。当然,各种动态路由协议会不同程度地占用网络带宽和CPU资源。
-
在HTTP跟TCP中间多了一层加密层TLS(安全传输层协议)/SSL(安全套接字层)就是HTTPS。TLS是SSL的升级版。
-
四个使用的是TCP,其余的都是UDP,这四个是:文件传送FTP,电子邮件SMTP(simple mail transport protocol),远程终端接入Telnet,万维网http。大家一般记住这四个是实用的TCP
-
RIP协议是一种内部网关协议(IGP),是一种动态路由选择协议,用于自治系统(AS)内的路由信息的传递。RIP协议基于距离矢量算法(DistanceVectorAlgorithms),使用“跳数”(即metric)来衡量到达目标地址的路由距离。
RIP协议采用距离向量算法,在实际使用中已经较少适用。在默认情况下,RIP使用一种非常简单的度量制度:距离就是通往目的站点所需经过的链路数,取值为1~15,数值16表示无穷大。RIP进程使用UDP的520端口来发送和接收RIP分组。RIP分组每隔30s以广播的形式发送一次,为了防止出现“广播风暴”,其后续的的分组将做随机延时后发送。在RIP中,如果一个路由在180s内未被刷,则相应的距离就被设定成无穷大,并从路由表中删除该表项。RIP分组分为两种:请求分组和响应分组。 -
RIP几种避免环路的机制:
水平分割:一种避免路由环路的出现和加快路由汇聚的技术。
触发更新:网络无变化,按默认的30秒发送更新信息;网络有变化,立即发送更新信息。
毒化路由:通过修改metirc值为16,将路由标记为不可达。
毒性逆转:水平分割的一种变型,从某接口接收的更新信息再由该接口发送回去时修改metirc值为16。
抑制计时:一个路由无效时,一段时间内不启用该路由。
-
滑动窗口协议(Sliding Window Protocol),属于TCP协议的一种应用,用于网络数据传输时的流量控制,以避免拥塞的发生。该协议允许发送方在停止并等待确认前发送多个数据分组。由于发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据的传输,提高网络吞吐量。
例题:求网络地址?
172.16.100.5/255.255.255.252 的网络地址和主机号是多少?
答案:172.16.100.4 1
子网掩码是255.255.255.252
前三个字节是255,代表前三个字节是网络地址
最后一字节写成二进制形式为11111100
也就是说最后一字节中的前六位也是网络地址,后两位是主机地址
11111100与172.16.100.5最后一字节5按位与运算,5的二进制表示为101,与运算结果为4
即网络号为172.16.100.4,主机号为1
-
子网划分:IP地址分类 A类网络的IP地址范围为1.0.0.1-127.255.255.254;
B类网络的IP地址范围为:128.1.0.1-191.255.255.254;
C类网络的IP地址范围为:192.0.1.1-223.255.255.254。
私有IP地址范围:
A: 10.0.0.0~10.255.255.255 即10.0.0.0/8
B:172.16.0.0~172.31.255.255即172.16.0.0/12
C:192.168.0.0~192.168.255.255 即192.168.0.0/16 -
ICMP报文作为数据字段封装在IP分组中,因此,IP协议直接为ICMP提供服务。UDP和TCP都是传输层协议,为应用层提供服务。PPP协议是链路层协议,为网络层提供服务。
-
波特是设备(如调整解调器)每秒钟发生信号变化的度量。
它代表的是信号的变化,而不是传输数据的多少。
它表示每秒钟内通信线路状态改变的次数。
它是对符号传输速率的一种度量,1波特即指每秒传输1个符号,而通过不同的调制方式,可以在一个码元符号上负载多个bit位信息。
1波特即指每秒传输1个码元符号
1比特/秒是指每秒传输1比特
OSI七层+作用
1.物理层:通过媒介传输比特,确定机械和电气规范。(比特bit)
2.数据链路层:将比特组装成帧和点对点的传递。(帧frame)
3.网络层:负责数据包从源到宿的传递和网际互联。(包packet)
4.传输层:提供端对端的可靠的报文传递和错误恢复。(段seqment)
5.会话层:会话的创建,管理和销毁。(会话协议数据单元SPDU)
6.表现层:对数据进行解析,翻译,加密,压缩。(表示协议数据单元PPDU)
7.应用层:允许访问OSI环境操作。(应用协议数据单元APDU)
操作系统
-
进程状态反映进程执行过程的变化。这些状态随着进程的执行和外界条件的变化而转换。进程的三种基本状态包括有就绪状态,执行状态,阻塞状态。
-
空闲分区分配算法有以下三个:
- 首适应算法:当接到内存申请时,查找分区说明表,找到第一个满足申请长度的空闲区,将其分割并分配。此算法简单,可以快速做出分配决定,空闲区首地址递增顺序形成空闲分区链。
- 最佳适应算法:当接到内存申请时,查找分区说明表,找到第一个能满足申请长度的最小空闲区,将其进行分割并分配。此算法最节约空间,因为它尽量不分割到大的空闲区,其缺点是可能会形成很多很小的空闲分区,称为“碎片”,空闲区大小递增顺序形成空闲分区链。
- 最坏适应算法:当接到内存申请时,查找分区说明表,找到能满足申请要求的最大的空闲区。该算法的优点是避免形成碎片,而缺点是分割了大的空闲区后,在遇到较大的程序申请内存时,无法满足的可能性较大,空闲区大小递减顺序形成空闲分区链。
-
记录式文件的逻辑结构:
[I]有结构文件:记录的长度可分为定长和不定长两类:定长记录:变长记录。根据用户和系统管理上的需要,可采用多种方式来组织这些记录,形成下述的几种文件:顺序文件;索引文件;索引顺序文件。
[2]无结构文件:流式文件,其长度以字节为单位。
-
如果三个用户运行同一个程序(编译程序),则系统将创建3 个进程,但这3 个进程共享C 编译程序的1 个副本
-
进程的模式有系统态和用户态 两种,一般用户编写的程序是在用户态下工作,当程序中有中断或者调用系统函数 时,会切换到系统态下运行
-
在多道系统中,会频繁切换任务(task)。造成CPU的浪费,也增加了处理时间。
-
实时系统的特征:多路性、独立性、及时性、交互性和可靠性。但首先需要考虑的是实时性和可靠性。
-
处理器每执行完一条指令后,硬件的中断装置立即检查有无中断事件发生,若有中断事件发生,则暂停现行进程的执行,而让操作系统的中断处理程序占用处理器,这一过程称“中断响应”。
-
数据总线用于传送数据信息。数据总线是双向三态形式的总线,即它既可以把 CPU 的数据传送到存储器或输入输出接口等其他部件,也可以将其他部件的数据传送到 CPU 。数据总线的位数是微型计算机的一个重要指标,通常与微处理的字长相一致。例如, Intel 8086 微处理器字长 16 位,其数据总线宽度也是 16 位。
-
操作系统在分配内存时,有时候会产生一些空闲但是无法被正常使用的内存区域,这些就是内存碎片,或者称为内存零头,这些内存零头一共分为两类:内零头和外零头。
内零头是指进程在向操作系统请求内存分配时,系统满足了进程所需要的内存需求后,还额外还多分了一些内存给该进程,也就是说额外多出来的这部分内存归该进程所有,其他进程是无法访问的。
外零头是指内存中存在着一些空闲的内存区域,这些内存区域虽然不归任何进程所有,但是因为内存区域太小,无法满足其他进程所申请的内存大小而形成的内存零头。
页式存储管理是以页为单位(页面的大小由系统确定,且大小是固定的)向进程分配内存的,例如:假设内存总共有100K,分为10页,每页大小为10K。现在进程A提出申请56K内存,因为页式存储管理是以页为单位进程内存分配的,所以系统会向进程A提供6个页面,也就是60K的内存空间,那么在最后一页中进程只使用了6K,从而多出了4K的内存碎片,但是这4K的内存碎片系统已经分配给进程A了,其他进程是无法再访问这些内存区域的,这种内存碎片就是内零头。
段式存储管理是段(段的大小是程序逻辑确定,且大小不是固定的)为单位向进程进行内存分配的,进程申请多少内存,系统就给进程分配多少内存,这样就不会产生内零头,但是段式分配会产生外零头。
例如:假设内存总的大小为100K,现在进程A向系统申请60K的内存,系统在满足了进程A的内存申请要求后,还剩下40K的空闲内存区域;这时如果进程B向系统申请50K的内存区域,而系统只剩下了40K的内存区域,虽然这40K的内存区域不归任何进程所有,但是因为大小无法满足进程B的要求,所以也无法分配给进程B,这样就产生了外零头。请求段式存储管理是在段式存储管理的基础上增加了请求调段功能和段置换功能。
-
静态重定位:即在程序装入内存的过程中完成,是指在程序开始运行前,程序中的各个地址有关的项均已完成重定位,地址变换通常是在装入时一次完成的,以后不再改变。
动态重定位即在程序运行过程中要访问数据时再进行逻辑地址与物理地址的变换(即在逐条指令执行时完成地址映射。
动态内存分配是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。
内存的静态分配和动态分配的区别主要是两个:
一是时间不同。静态分配发生在程序编译和连接的时候。动态分配则发生在程序调入和执行的时候。
二是空间不同。堆都是动态分配的,没有静态分配的堆。 -
管道是 单向的、先进先出的 ,它把一个进程的输出和另一个进程的输入连接在一起。一个进程(写进程)在管道的 尾部写入数据 ,另一个进程(读进程)从管道的 头部读出数据 。
管道通信,是指用于连接一个读进程和一个写进程,以实现它们之间通信的一个文件,又称为pipe文件。向管道(共享文件)提供输入的发送进程(即写进程),以字符流形式将大量的数据送入管道;而接收管道输出的接收进程(读进程),则从管道中接收数据。由于发送进程和接收进程是利用管道进行通信的,故又称为管道通信。
为了协调双方的通信,管道进程必须提供以下三方的协调能力:
- 互斥:当一个进程正在对pipe进行读/写操作时,其它进程必须等待。
- 同步:当写进程把一定量数据写入pipe时,便去睡眠等待,直到读进程取走数据后,唤醒写进程。读进程读一空pipe时,也应该睡眠等待,直到写进程将数据写入管道,才将之唤醒。
- 确定对方是否存在,只有确定了对方已经存在时,才能进行通信。
-
直接寻址:在指令格式的地址的字段中直接指出操作数在内存的地址,而不需要经过某种变换。
立即寻址:将操作数紧跟在操作码后面,与操作码一起放在指令代码段中,在程序运行时,程序直接调用该操作数,而不需要到其他地址单元中去取相应的操作数。
间接寻址:间接寻址是相对于直接寻址而言的,指令地址字段的形式地址不是操作数的真正地址,而是操作数地址的指示器。
变址寻址:变址寄存器的内容(通常是首地址)与指令地址码部分给出的地址(通常是位移量)之和作为操作数的地址来获得所需要的操作数就称为变址寻址。
面试项目
项目1:
aelf
aelf 通过类GRPC的protobuf服务描述文件定义智能合约,实现了一个性能等价于GRPC Server的智能合约运行环境。
RPC(remote procedure call 远程过程调用)框架实际是提供了一套机制,使得应用程序之间可以进行通信,而且也遵从server/client模型。使用的时候客户端调用server端提供的接口就像是调用本地的函数一样。
ProtoBuf 是结构数据序列化方法,可简单类比于 XML,网络通信、通用数据交换等场景
gRPC可以通过protobuf来定义接口,从而可以有更加严格的接口约束条件。protobuf可以将数据序列化为二进制编码,这会大幅减少需要传输的数据量,从而大幅提高性能。
新增合约
1.修改proto文件中增加方法的定义和数据类型的定义
2.cs新增逻辑
3.重新编译启动链
4.然后js文件可以使用新的合约
针对问题
-
跨平台评论暂未实现
-
恶意评论和刷单乱象
-
奖惩机制不够完善:对于恶意评论者的处罚力度极其有限。
我们项目针对的问题及解决方案
1、评论作假、篡改。
通过数据上链、保证评论不可篡改、不可删除。
2、评论及信用不溯源,职业差评/好评师、用户对评价可以不负责,随意评论。
通过确认唯一用户id和区块地址,结合链上数据不可篡改的特性,做到评论及信用溯源。
3、各个平台之间信息及信用不互通。
通过公证人模式实现跨链,打破信息孤岛,通过跨链通信实现平台或业务之间的信息及信用互通。
4、优质内容贡献者不一定能获得激励
通过公开数据或者智能合约来约定激励和惩罚机制,减少垄断后,应用方对用户不负责的情况。
5、已有区块链系统搭建的业务资源不隔离
通过多链的模式,每个业务独立部署,互不影响。
项目2:分布式账本系统
数据字典
区块构成
- 区块体:
- 交易记录(当前交易的集合)
- 区块头:
- 区块索引(index)
- hash值(区块的唯一标识)
- 时间戳
- 前一个区块的hash
- 随机数(工作量证明)
钱包构成
- 地址 = MD5(SHA256公钥) 32位 (比特币做了一系列的转换)
- 公钥
- 私钥
- 公钥hash
交易构成
- 输入
- 前一次交易ID(引用多个交易的话是个list)
- 交易金额(引用多个交易的话是个list)
- 交易签名
- 发送方的公钥
- 输出
- 交易金额(找零的话还有自己的钱包,是个List)
- 接受方的钱包公钥和hash(找零的话还有自己的钱包,是个List)
- ID
功能模块
初始化:
创建空的区块链,生成创世区块
挖矿(生成新的区块)流程:
(打包之前的交易验证)
- 创建系统奖励的交易
- 获取区块链最后一个区块
- 计算hash值(hash=最后一个区块hash+交易记录+随机数)
- 创建新区块,打包交易
区块校验:别结点在我之前算出区块了,广播告知我了,本地区块链已经同步了,这是候会发现对不上了,所以还需要校验一遍。
工作量证明:挖矿所花费的时间,别的区块收到后会验证你的区块是否正确(hash以四个零开头??)
共识机制:
密码学
java.security包
-
对称加密
加密和解密使用一个密钥
-
非对称加密(RSA、ECC)
接收者的公钥加密
接收者的私钥解密
-
数字签名
私钥签名、公钥解签名
原文hash->摘要->发送方的私钥签名->发送方的公钥验证签名
解决发送方正确的
UTXO的产生:交易输出和挖矿
核心算法:
找到未花费的交易输出
在本地交易集合中挑选出已经花费的交易(输入),在block中挑出不属于这些交易的输出
交易发起具体流程:
-
发送和接受的钱包地址和交易金额
-
map映射中地址->钱包的映射 获取钱包的实例
-
钱包的公钥去找到未花费的交易输出
-
…(待补全)
应用:
电商的积分系统