[关闭]
@JunQiu 2018-09-25T14:10:27.000000Z 字数 3509 阅读 1452

mmap内存映射、Js_this

summary_2018/08 language_js pocc(计组)


1、日常工作

1.1、js:this关键字
1.2、账号集群服务集成测试
1.3、mmap浅读

2、技术学习

2.1、js:this关键字
  1. ## 全局上下文
  2. console.log(this) // {}
  3. ## 在函数上下文(严格模式/非严格模式)
  4. // 正常模式,node环境下
  5. function f() {
  6. console.log(this === global);
  7. }
  8. f() // true
  9. // !!! there is a Tips
  10. const a = {
  11. a1 : function () {
  12. this.b1='b'
  13. function c1() {
  14. console.log(this === global) // true
  15. }
  16. c1() // 在严格模式下,如果this没有被执行上下文(execution context)定义,那它将保持为undefined。因为c1是被直接调用的,而不是作为对象的属性或方法调用的(如 window.f2())
  17. }
  18. }
  19. Tips:此时this绑定到全局对象,算是js的一个设计缺陷,实际应该绑定到外层对应的对象,可以采取that(约定)变量来代替this,传递(或者用严格模式来注意这个风险),比如还可以使用callapplybind方法绑定,而es6箭头函数也可以避免这个问题
  20. // call、apply、bind
  21. 1bind
  22. function f(){
  23. return this.a;
  24. }
  25. const g = f.bind({a:"azerty"});
  26. console.log(g()); // azerty
  27. const h = g.bind({a:'yoo'}); // 不能对绑定后的结果再次绑定
  28. console.log(h()); // azerty
  29. const o = {a:37, f:f, g:g, h:h};
  30. console.log(o.f(), o.g(), o.h()); // 37, azerty, azerty
  31. 2apply
  32. apply() 方法调用一个函数,其具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数。
  33. function f () {
  34. console.log(this.a)
  35. }
  36. f.apply({a:1}) // 1
  37. 3call
  38. call() 方法调用一个函数,其具有一个指定的this值和分别地提供的参数(参数的列表)。
  39. function Product(name, price) {
  40. this.name = name;
  41. this.price = price;
  42. }
  43. function Food(name, price) {
  44. Product.call(this, name, price);
  45. this.category = 'food';
  46. }
  47. new Food('cheese', 5).name
  48. // 严格模式
  49. function f() {
  50. 'use strict';
  51. console.log(this === undefined);
  52. }
  53. f() // true
  54. Tips:对构造函数十分有用,当未使用new关键字时,this对象会指向全局变量,而在严格模式下,会指向undefined
  55. ## 在对象中调用,指向当前对象(原型链上查找)
  56. Tip:在原型链上查找,即使使用自己继承的原型的方法,this关键字也是指向自己的
  57. const o = {
  58. f: function () {
  59. console.log(this)
  60. return this.a + this.b
  61. }
  62. }
  63. const p = Object.create(o)
  64. p.a = 1
  65. p.b = 4
  66. console.log(p.f()) // { a: 1, b: 4 } 5
2.2、mmap浅读
  1. mmap内存映射的实现过程,总的来说可以分为三个阶段:
  2. ### 进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域
  3. 1、进程在用户空间调用库函数mmap,原型:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
  4. 2、在当前进程的虚拟地址空间中,寻找一段空闲的满足要求的连续的虚拟地址
  5. 3、为此虚拟区分配一个vm_area_struct结构,接着对这个结构的各个域进行了初始化
  6. 4、将新建的虚拟区结构(vm_area_struct)插入进程的虚拟地址区域链表或树中
  7. ### 调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟地址的一一映射关系
  8. 5、为映射分配了新的虚拟地址区域后,通过待映射的文件指针,在文件描述符表中找到对应的文件描述符,通过文件描述符,链接到内核“已打开文件集”中该文件的文件结构体(struct file),每个文件结构体维护着和这个已打开文件相关各项信息。
  9. 6、通过该文件的文件结构体,链接到file_operations模块,调用内核函数mmap,其原型为:int mmap(struct file *filp, struct vm_area_struct *vma),不同于用户空间库函数。
  10. 7、内核mmap函数通过虚拟文件系统inode模块定位到文件磁盘物理地址。
  11. 8、通过remap_pfn_range函数建立页表,即实现了文件地址和虚拟地址区域的映射关系。此时,这片虚拟地址并没有任何数据关联到主存中。
  12. ### 进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝
  13. 注:前两个阶段仅在于创建虚拟区间并完成地址映射,但是并没有将任何文件数据的拷贝至主存。真正的文件读取是当进程发起读或写操作时。
  14. 9、进程的读或写操作访问虚拟地址空间这一段映射地址,通过查询页表,发现这一段地址并不在物理页面上。因为目前只建立了地址映射,真正的硬盘数据还没有拷贝到内存中,因此引发缺页异常。
  15. 10、缺页异常进行一系列判断,确定无非法操作后,内核发起请求调页过程。
  16. 11、调页过程先在交换缓存空间(swap cache)中寻找需要访问的内存页,如果没有则调用nopage函数把所缺的页从磁盘装入到主存中。
  17. 12、之后进程即可对这片主存进行读或者写的操作,如果写操作改变了其内容,一定时间后系统会自动回写脏页面到对应磁盘地址,也即完成了写入到文件的过程。
  18. 注:修改过的脏页面并不会立即更新回文件中,而是有一段时间的延迟,可以调用msync()来强制同步, 这样所写的内容就能立即保存到文件里了。
  1. // 常规文件系统操作(调用read/fread等类函数)中,函数的调用过程:
  2. 1、进程发起读文件请求。
  3. 2、内核通过查找进程文件符表,定位到内核已打开文件集上的文件信息,从而找到此文件的inode
  4. 3inodeaddress_space上查找要请求的文件页是否已经缓存在页缓存中。如果存在,则直接返回这片文件页的内容。
  5. 4、如果不存在,则通过inode定位到文件磁盘地址,将数据从磁盘复制到页缓存。之后再次发起读页面过程,进而将页缓存中的数据发给用户进程。
  6. ## 总的来说:两次拷贝(内核空间和用户空间)
  7. 常规文件操作为了提高读写效率和保护磁盘,使用了页缓存机制。这样造成读文件时需要先将文件页从磁盘拷贝到页缓存中,由于页缓存处在内核空间,不能被用户进程直接寻址,所以还需要将页缓存中数据页再次拷贝到内存对应的用户空间中。这样,通过了两次数据拷贝过程,才能完成进程对文件内容的获取任务。写操作也是一样:数据——流缓存区(用户层带缓存I/O)——内核缓存区(满了一次性写入磁盘)——磁盘
  1. 1、对文件的读取操作跨过了页缓存,减少了数据的拷贝次数,用内存读写取代I/O读写,提高了文件读取效率。
  2. 23种数据结构的高效,其页映射过程也很高效
  3. 3、如果更新文件的操作很多,mmap避免两态拷贝的优势就被摊还,最终还是落在了大量的脏页回写及由此引发的随机IO上.
  4. 4、对变长文件不适合.(mmap来写文件这种方式没办法增加文件的长度, 因为要映射的长度在调用mmap()的时候就决定了)
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注