@JunQiu
2018-09-25T06:10:27.000000Z
字数 3509
阅读 1881
summary_2018/08 language_js pocc(计组)
## 全局上下文console.log(this) // {}## 在函数上下文(严格模式/非严格模式)// 正常模式,node环境下function f() {console.log(this === global);}f() // true// !!! there is a Tipsconst a = {a1 : function () {this.b1='b'function c1() {console.log(this === global) // true}c1() // 在严格模式下,如果this没有被执行上下文(execution context)定义,那它将保持为undefined。因为c1是被直接调用的,而不是作为对象的属性或方法调用的(如 window.f2())}}Tips:此时this绑定到全局对象,算是js的一个设计缺陷,实际应该绑定到外层对应的对象,可以采取that(约定)变量来代替this,传递(或者用严格模式来注意这个风险),比如还可以使用call、apply和bind方法绑定,而es6箭头函数也可以避免这个问题// call、apply、bind1、bind:function f(){return this.a;}const g = f.bind({a:"azerty"});console.log(g()); // azertyconst h = g.bind({a:'yoo'}); // 不能对绑定后的结果再次绑定console.log(h()); // azertyconst o = {a:37, f:f, g:g, h:h};console.log(o.f(), o.g(), o.h()); // 37, azerty, azerty2、applyapply() 方法调用一个函数,其具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数。function f () {console.log(this.a)}f.apply({a:1}) // 13、callcall() 方法调用一个函数,其具有一个指定的this值和分别地提供的参数(参数的列表)。function Product(name, price) {this.name = name;this.price = price;}function Food(name, price) {Product.call(this, name, price);this.category = 'food';}new Food('cheese', 5).name// 严格模式function f() {'use strict';console.log(this === undefined);}f() // trueTips:对构造函数十分有用,当未使用new关键字时,this对象会指向全局变量,而在严格模式下,会指向undefined## 在对象中调用,指向当前对象(原型链上查找)Tip:在原型链上查找,即使使用自己继承的原型的方法,this关键字也是指向自己的const o = {f: function () {console.log(this)return this.a + this.b}}const p = Object.create(o)p.a = 1p.b = 4console.log(p.f()) // { a: 1, b: 4 } 5

mmap内存映射的实现过程,总的来说可以分为三个阶段:### 进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域1、进程在用户空间调用库函数mmap,原型:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);2、在当前进程的虚拟地址空间中,寻找一段空闲的满足要求的连续的虚拟地址3、为此虚拟区分配一个vm_area_struct结构,接着对这个结构的各个域进行了初始化4、将新建的虚拟区结构(vm_area_struct)插入进程的虚拟地址区域链表或树中### 调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟地址的一一映射关系5、为映射分配了新的虚拟地址区域后,通过待映射的文件指针,在文件描述符表中找到对应的文件描述符,通过文件描述符,链接到内核“已打开文件集”中该文件的文件结构体(struct file),每个文件结构体维护着和这个已打开文件相关各项信息。6、通过该文件的文件结构体,链接到file_operations模块,调用内核函数mmap,其原型为:int mmap(struct file *filp, struct vm_area_struct *vma),不同于用户空间库函数。7、内核mmap函数通过虚拟文件系统inode模块定位到文件磁盘物理地址。8、通过remap_pfn_range函数建立页表,即实现了文件地址和虚拟地址区域的映射关系。此时,这片虚拟地址并没有任何数据关联到主存中。### 进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝注:前两个阶段仅在于创建虚拟区间并完成地址映射,但是并没有将任何文件数据的拷贝至主存。真正的文件读取是当进程发起读或写操作时。9、进程的读或写操作访问虚拟地址空间这一段映射地址,通过查询页表,发现这一段地址并不在物理页面上。因为目前只建立了地址映射,真正的硬盘数据还没有拷贝到内存中,因此引发缺页异常。10、缺页异常进行一系列判断,确定无非法操作后,内核发起请求调页过程。11、调页过程先在交换缓存空间(swap cache)中寻找需要访问的内存页,如果没有则调用nopage函数把所缺的页从磁盘装入到主存中。12、之后进程即可对这片主存进行读或者写的操作,如果写操作改变了其内容,一定时间后系统会自动回写脏页面到对应磁盘地址,也即完成了写入到文件的过程。注:修改过的脏页面并不会立即更新回文件中,而是有一段时间的延迟,可以调用msync()来强制同步, 这样所写的内容就能立即保存到文件里了。
// 常规文件系统操作(调用read/fread等类函数)中,函数的调用过程:1、进程发起读文件请求。2、内核通过查找进程文件符表,定位到内核已打开文件集上的文件信息,从而找到此文件的inode。3、inode在address_space上查找要请求的文件页是否已经缓存在页缓存中。如果存在,则直接返回这片文件页的内容。4、如果不存在,则通过inode定位到文件磁盘地址,将数据从磁盘复制到页缓存。之后再次发起读页面过程,进而将页缓存中的数据发给用户进程。## 总的来说:两次拷贝(内核空间和用户空间)常规文件操作为了提高读写效率和保护磁盘,使用了页缓存机制。这样造成读文件时需要先将文件页从磁盘拷贝到页缓存中,由于页缓存处在内核空间,不能被用户进程直接寻址,所以还需要将页缓存中数据页再次拷贝到内存对应的用户空间中。这样,通过了两次数据拷贝过程,才能完成进程对文件内容的获取任务。写操作也是一样:数据——流缓存区(用户层带缓存I/O)——内核缓存区(满了一次性写入磁盘)——磁盘
1、对文件的读取操作跨过了页缓存,减少了数据的拷贝次数,用内存读写取代I/O读写,提高了文件读取效率。2、3种数据结构的高效,其页映射过程也很高效3、如果更新文件的操作很多,mmap避免两态拷贝的优势就被摊还,最终还是落在了大量的脏页回写及由此引发的随机IO上.4、对变长文件不适合.(mmap来写文件这种方式没办法增加文件的长度, 因为要映射的长度在调用mmap()的时候就决定了)
