做网站都需要用到什么可以做t恤的网站

当前位置: 首页 > news >正文

做网站都需要用到什么,可以做t恤的网站,给我高清电影,建网站需要多少钱和什么条件有关一、mmap基础概念 mmap 即 memory map#xff0c;也就是内存映射。mmap 是一种内存映射文件的方法#xff0c;即将一个文件或者其它对象映射到进程的地址空间#xff0c;实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后#xff0c;…一、mmap基础概念 mmap 即 memory map也就是内存映射。mmap 是一种内存映射文件的方法即将一个文件或者其它对象映射到进程的地址空间实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后进程就可以采用指针的方式读写操作这一段内存而系统会自动回写脏页面到对应的文件磁盘上即完成了对文件的操作而不必再调用 read、write 等系统调用函数。相反内核空间对这段区域的修改也直接反映用户空间从而可以实现不同进程间的文件共享。 如下图所示 mmap的作用在应用这一层是让你把文件的某一段当作内存一样来访问。将文件映射到物理内存将进程虚拟空间映射到那块内存。这样进程不仅能像访问内存一样读写文件多个进程映射同一文件还能保证虚拟空间映射到同一块物理内存达到内存共享的作用。 mmap 具有如下的特点 mmap 向应用程序提供的内存访问接口是内存地址连续的但是对应的磁盘文件的 block 可以不是地址连续的mmap 提供的内存空间是虚拟空间虚拟内存而不是物理空间物理内存因此完全可以分配远远大于物理内存大小的虚拟空间例如 16G 内存主机分配 1000G 的 mmap 内存空间mmap 负责映射文件逻辑上一段连续的数据物理上可以不连续存储映射为连续内存而这里的文件可以是磁盘文件、驱动假造出的文件例如 DMA 技术以及设备mmap 由操作系统负责管理对同一个文件地址的映射将被所有线程共享操作系统确保线程安全以及线程可见性mmap 的设计很有启发性。基于磁盘的读写单位是 block一般大小为 4KB而基于内存的读写单位是地址虽然内存的管理与分配单位是 4KB。 换言之CPU 进行一次磁盘读写操作涉及的数据量至少是 4KB但是进行一次内存操作涉及的数据量是基于地址的也就是通常的 64bit64 位操作系统。mmap 下进程可以采用指针的方式进行读写操作这是值得注意的。 二. 虚拟内存虚拟空间 其实是一个概念前一篇对于这个词没有确切的定义现在定义一下 虚拟空间就是进程看到的所有地址组成的空间虚拟空间是某个进程对分配给它的所有物理地址已经分配的和将会分配的的重新映射。 而虚拟内存为啥叫虚拟内存是因为它就不是真正的内存是假的因为它是由地址组成的空间所以在这里使用虚拟空间这个词更加确切和易懂。不过虚拟内存这个词也不算错 虚拟空间原理 物理内存 首先物理地址实际上也不是连续的通常是包含作为主存的DRAM和IO寄存器 以前的CPU如X86是为IO划分单独的地址空间所以不能用直接访问内存的方式如指针IO只能用专门的方法in/read/out/write诸如此类。现在的CPU利用PCI总线将IO寄存器映射到物理内存所以出现了基于内存访问的IO。还有一点补充的就如同进程空间有一块内核空间一样物理内存也会有极小一部分是不能访问的为内核所用。 三个总线 这里再补充下三个总线的知识即地址总线、数据总线、控制总线 地址总线用来传输地址数据总线用来传输数据控制总线用来传输命令 比如CPU通过控制总线发送读取命令同时用地址总线发送要读取的数据虚地址经过MMU后到内存 内存通过数据总线将数据传输给CPU。虚拟地址的空间和指令集的地址长度有关不一定和物理地址长度一致比如现在的64位处理器从VA角度看来可以访问64位的地址但地址总线长度只有48位所以你可以访问一个位于2^52这个位置的地址。 虚拟内存地址转换虚地址转实地址 上面已经明确了虚拟内存是虚拟空间即地址的集合这一概念。基于此来说说原理。 如果还记得操作系统课程里面提到的虚地址那么这个虚地址就是虚拟空间的地址了虚地址通过转换得到实地址转换方式课程内也讲得很清楚虚地址头部包含了页号段地址和段大小看存储模式页存储、段存储段页式剩下部分是偏移量经过MMU转换成实地址。 存储方式 虚拟地址头部为页号通过查询页表得到物理页号假设一页时1K那么页号*偏移量就得到物理地址 虚拟地址头部为段号段表中找到段基地址加上偏移量得到实地址 二、mmap原理 mmap函数创建一个新的vm_area_struct结构并将其与文件/设备的物理地址相连。 vm_area_struct linux使用vm_area_struct来表示一个独立的虚拟内存区域一个进程可以使用多个vm_area_struct来表示不用类型的虚拟内存区域如堆栈代码段MMAP区域等。 vm_area_struct结构中包含了区域起始地址。同时也包含了一个vm_opt指针其内部可引出所有针对这个区域可以使用的系统调用函数。从而进程可以通过vm_area_struct获取操作这段内存区域所需的任何信息。 进程通过vma操作内存而vma与文件/设备的物理地址相连系统自动回写脏页面到对应的文件磁盘上或写入到设备地址空间实现内存映射文件。 内存映射文件的原理 首先创建虚拟区间并完成地址映射此时还没有将任何文件数据拷贝至主存。当进程发起读写操作时会访问虚拟地址空间通过查询页表发现这段地址不在物理页上因为只建立了地址映射真正的数据还没有拷贝到内存因此引发缺页异常。缺页异常经过一系列判断确定无非法操作后内核发起请求调页过程。 最终会调用nopage函数把所缺的页从文件在磁盘里的地址拷贝到物理内存。之后进程便可以对这片主存进行读写如果写操作修改了内容一定时间后系统会自动回写脏页面到对应的磁盘地址完成了写入到文件的过程。另外也可以调用msync()来强制同步这样所写的内存就能立刻保存到文件中。 mmap内存映射的实现过程总的来说可以分为三个阶段 一进程启动映射过程并在虚拟地址空间中为映射创建虚拟映射区域 进程在用户空间调用库函数mmap原型void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);在当前进程的虚拟地址空间中寻找一段空闲的满足要求的连续的虚拟地址为此虚拟区分配一个vm_area_struct结构接着对这个结构的各个域进行了初始化将新建的虚拟区结构vm_area_struct插入进程的虚拟地址区域链表或树中 二调用内核空间的系统调用函数mmap不同于用户空间函数实现文件物理地址和进程虚拟地址的一一映射关系 为映射分配了新的虚拟地址区域后通过待映射的文件指针在文件描述符表中找到对应的文件描述符通过文件描述符链接到内核“已打开文件集”中该文件的文件结构体struct file每个文件结构体维护着和这个已打开文件相关各项信息。通过该文件的文件结构体链接到file_operations模块调用内核函数mmap其原型为int mmap(struct file *filp, struct vm_area_struct *vma)不同于用户空间库函数。内核mmap函数通过虚拟文件系统inode模块定位到文件磁盘物理地址。通过remap_pfn_range函数建立页表即实现了文件地址和虚拟地址区域的映射关系。此时这片虚拟地址并没有任何数据关联到主存中。 三进程发起对这片映射空间的访问引发缺页异常实现文件内容到物理内存主存的拷贝 注前两个阶段仅在于创建虚拟区间并完成地址映射但是并没有将任何文件数据的拷贝至主存。真正的文件读取是当进程发起读或写操作时。 进程的读或写操作访问虚拟地址空间这一段映射地址通过查询页表发现这一段地址并不在物理页面上。因为目前只建立了地址映射真正的硬盘数据还没有拷贝到内存中因此引发缺页异常。缺页异常进行一系列判断确定无非法操作后内核发起请求调页过程。调页过程先在交换缓存空间swap cache中寻找需要访问的内存页如果没有则调用nopage函数把所缺的页从磁盘装入到主存中。之后进程即可对这片主存进行读或者写的操作如果写操作改变了其内容一定时间后系统会自动回写脏页面到对应磁盘地址也即完成了写入到文件的过程。 注修改过的脏页面并不会立即更新回文件中而是有一段时间的延迟可以调用msync()来强制同步, 这样所写的内容就能立即保存到文件里了。 三、mmap 的 I/O 模型 mmap 也是一种零拷贝技术其 I/O 模型如下图所示 代码语言javascript 复制 #include sys/mman.h void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) mmap 技术有如下特点 利用 DMA 技术来取代 CPU 来在内存与其他组件之间的数据拷贝例如从磁盘到内存从内存到网卡用户空间的 mmap file 使用虚拟内存实际上并不占据物理内存只有在内核空间的 kernel buffer cache 才占据实际的物理内存mmap() 函数需要配合 write() 系统调动进行配合操作这与 sendfile() 函数有所不同后者一次性代替了 read() 以及 write()因此 mmap 也至少需要 4 次上下文切换mmap 仅仅能够避免内核空间到用户空间的全程 CPU 负责的数据拷贝但是内核空间内部还是需要全程 CPU 负责的数据拷贝 利用 mmap() 替换 read()配合 write() 调用的整个流程如下 用户进程调用 mmap()从用户态陷入内核态将内核缓冲区映射到用户缓存区DMA 控制器将数据从硬盘拷贝到内核缓冲区可见其使用了 Page Cache 机制mmap() 返回上下文从内核态切换回用户态用户进程调用 write()尝试把文件数据写到内核里的套接字缓冲区再次陷入内核态CPU 将内核缓冲区中的数据拷贝到的套接字缓冲区DMA 控制器将数据从套接字缓冲区拷贝到网卡完成数据传输write() 返回上下文从内核态切换回用户态。 通过mmap实现的零拷贝I/O进行了4次用户空间与内核空间的上下文切换以及3次数据拷贝其中3次数据拷贝中包括了2次DMA拷贝和1次CPU拷贝 四、mmap 的优势 1.简化用户进程编程 在用户空间看来通过 mmap 机制以后磁盘上的文件仿佛直接就在内存中把访问磁盘文件简化为按地址访问内存。这样一来应用程序自然不需要使用文件系统的 write写入、read读取、fsync同步等系统调用因为现在只要面向内存的虚拟空间进行开发。 但是这并不意味着我们不再需要进行这些系统调用而是说这些系统调用由操作系统在 mmap 机制的内部封装好了。 1基于缺页异常的懒加载 出于节约物理内存以及 mmap 方法快速返回的目的mmap 映射采用懒加载机制。具体来说通过 mmap 申请 1000G 内存可能仅仅占用了 100MB 的虚拟内存空间甚至没有分配实际的物理内存空间。当你访问相关内存地址时才会进行真正的 write、read 等系统调用。CPU 会通过陷入缺页异常的方式来将磁盘上的数据加载到物理内存中此时才会发生真正的物理内存分配。 2数据一致性由 OS 确保 当发生数据修改时内存出现脏页与磁盘文件出现不一致。mmap 机制下由操作系统自动完成内存数据落盘脏页回刷用户进程通常并不需要手动管理数据落盘。 读写效率提高避免内核空间到用户空间的数据拷贝 简而言之mmap 被认为快的原因是因为建立了页到用户进程的虚地址空间映射以读取文件为例避免了页从内核空间拷贝到用户空间。 3.避免只读操作时的 swap 操作 虚拟内存带来了种种好处但是一个最大的问题在于所有进程的虚拟内存大小总和可能大于物理内存总大小因此当操作系统物理内存不够用时就会把一部分内存 swap 到磁盘上。 在 mmap 下如果虚拟空间没有发生写操作那么由于通过 mmap 操作得到的内存数据完全可以通过再次调用 mmap 操作映射文件得到。但是通过其他方式分配的内存在没有发生写操作的情况下操作系统并不知道如何简单地从现有文件中除非其重新执行一遍应用程序但是代价很大恢复内存数据因此必须将内存 swap 到磁盘上。 4.节约内存 由于用户空间与内核空间实际上共用同一份数据因此在大文件场景下在实际物理内存占用上有优势。

  1. mmap 不是银弹 mmap 不是银弹这意味着 mmap 也有其缺陷在相关场景下的性能存在缺陷 由于 MMAP 使用时必须实现指定好内存映射的大小因此 mmap 并不适合变长文件如果更新文件的操作很多mmap 避免两态拷贝的优势就被摊还最终还是落在了大量的脏页回写及由此引发的随机 I/O 上所以在随机写很多的情况下mmap 方式在效率上不一定会比带缓冲区的一般写快读/写小文件例如 16K 以下的文件mmap 与通过 read 系统调用相比有着更高的开销与延迟同时 mmap 的刷盘由系统全权控制但是在小数据量的情况下由应用本身手动控制更好mmap 受限于操作系统内存大小例如在 32-bits 的操作系统上虚拟内存总大小也就 2GB但由于 mmap 必须要在内存中找到一块连续的地址块此时你就无法对 4GB 大小的文件完全进行 mmap在这种情况下你必须分多块分别进行 mmap但是此时地址内存地址已经不再连续使用 mmap 的意义大打折扣而且引入了额外的复杂性
  2. mmap 的适用场景 mmap 的适用场景实际上非常受限在如下场合下可以选择使用 mmap 机制 多个线程以只读的方式同时访问一个文件这是因为 mmap 机制下多线程共享了同一物理内存空间因此节约了内存mmap 非常适合用于进程间通信这是因为对同一文件对应的 mmap 分配的物理内存天然多线程共享并可以依赖于操作系统的同步原语mmap 虽然比 sendfile 等机制多了一次 CPU 全程参与的内存拷贝但是用户空间与内核空间并不需要数据拷贝因此在正确使用情况下并不比 sendfile 效率差 6.mmap使用细节 使用mmap需要注意的一个关键点是mmap映射区域大小必须是物理页大小(page_size)的整倍数32位系统中通常是4k字节。原因是内存的最小粒度是页而进程虚拟地址空间和内存的映射也是以页为单位。为了匹配内存的操作mmap从磁盘到虚拟地址空间的映射也必须是页。内核可以跟踪被内存映射的底层对象文件的大小进程可以合法的访问在当前文件大小以内又在内存映射区以内的那些字节。也就是说如果文件的大小一直在扩张只要在映射区域范围内的数据进程都可以合法得到这和映射建立时文件的大小无关。映射建立之后即使文件关闭映射依然存在。因为映射的是磁盘的地址不是文件本身和文件句柄无关。同时可用于进程间通信的有效地址空间不完全受限于被映射文件的大小因为是按页映射。 在上面的知识前提下我们下面看看如果大小不是页的整倍数的具体情况 情形一一个文件的大小是5000字节mmap函数从一个文件的起始位置开始映射5000字节到虚拟内存中。 分析因为单位物理页面的大小是4096字节虽然被映射的文件只有5000字节但是对应到进程虚拟地址区域的大小需要满足整页大小因此mmap函数执行后实际映射到虚拟内存区域8192个 字节5000~8191的字节部分用零填充。映射后的对应关系如下图所示 此时 1读/写前5000个字节0~4999会返回操作文件内容。 2读字节50008191时结果全为0。写50008191时进程不会报错但是所写的内容不会写入原文件中 。 3读/写8192以外的磁盘部分会返回一个SIGSECV错误。 情形二一个文件的大小是5000字节mmap函数从一个文件的起始位置开始映射15000字节到虚拟内存中即映射大小超过了原始文件的大小。 分析由于文件的大小是5000字节和情形一一样其对应的两个物理页。那么这两个物理页都是合法可以读写的只是超出5000的部分不会体现在原文件中。由于程序要求映射15000字节而文件只占两个物理页因此8192字节~15000字节都不能读写操作时会返回异常。如下图所示 此时 1进程可以正常读/写被映射的前5000字节(0~4999)写操作的改动会在一定时间后反映在原文件中。 2对于5000~8191字节进程可以进行读写过程不会报错。但是内容在写入前均为0另外写入后不会反映在文件中。 3对于8192~14999字节进程不能对其进行读写会报SIGBUS错误。 4对于15000以外的字节进程不能对其读写会引发SIGSEGV错误。 情形三一个文件初始大小为0使用mmap操作映射了10004K的大小即1000个物理页大约4M字节空间mmap返回指针ptr。 分析如果在映射建立之初就对文件进行读写操作由于文件大小为0并没有合法的物理页对应如同情形二一样会返回SIGBUS错误。但是如果每次操作ptr读写前先增加文件的大小那么ptr在文件大小内部的操作就是合法的。例如文件扩充4096字节ptr就能操作ptr ~ [ (char)ptr 4095]的空间。只要文件扩充的范围在1000个物理页映射范围内ptr都可以对应操作相同的大小。这样方便随时扩充文件空间随时写入文件不造成空间浪费。 五、mmap映射 在内存映射的过程中并没有实际的数据拷贝文件没有被载入内存只是逻辑上被放入了内存具体到代码就是建立并初始化了相关的数据结构struct address_space这个过程有系统调用mmap()实现所以建立内存映射的效率很高。 既然建立内存映射没有进行实际的数据拷贝那么进程又怎么能最终直接通过内存操作访问到硬盘上的文件呢那就要看内存映射之后的几个相关的过程了。 mmap()会返回一个指针ptr它指向进程逻辑地址空间中的一个地址这样以后进程无需再调用read或write对文件进行读写而只需要通过ptr就能够操作文件。但是ptr所指向的是一个逻辑地址要操作其中的数据必须通过MMU将逻辑地址转换成物理地址这个过程与内存映射无关。 前面讲过建立内存映射并没有实际拷贝数据这时MMU在地址映射表中是无法找到与ptr相对应的物理地址的也就是MMU失败将产生一个缺页中断缺页中断的中断响应函数会在swap中寻找相对应的页面如果找不到也就是该文件从来没有被读入内存的情况则会通过mmap()建立的映射关系从硬盘上将文件读取到物理内存中如图1中过程3所示。这个过程与内存映射无关。 如果在拷贝数据时发现物理内存不够用则会通过虚拟内存机制swap将暂时不用的物理页面交换到硬盘上这个过程也与内存映射无关。 mmap内存映射的实现过程 进程启动映射过程并在虚拟地址空间中为映射创建虚拟映射区域调用内核空间的系统调用函数mmap不同于用户空间函数实现文件物理地址和进程虚拟地址的一一映射关系进程发起对这片映射空间的访问引发缺页异常实现文件内容到物理内存主存的拷贝 适合的场景 您有一个很大的文件其内容您想要随机访问一个或多个时间您有一个小文件它的内容您想要立即读入内存并经常访问。这种技术最适合那些大小不超过几个虚拟内存页的文件。页是地址空间的最小单位虚拟页和物理页的大小是一样的通常为4KB。您需要在内存中缓存文件的特定部分。文件映射消除了缓存数据的需要这使得系统磁盘缓存中的其他数据空间更大 当随机访问一个非常大的文件时通常最好只映射文件的一小部分。映射大文件的问题是文件会消耗活动内存。如果文件足够大系统可能会被迫将其他部分的内存分页以加载文件。将多个文件映射到内存中会使这个问题更加复杂。 不适合的场景 您希望从开始到结束的顺序从头到尾读取一个文件这个文件有几百兆字节或者更大。将大文件映射到内存中会快速地填充内存并可能导致分页这将抵消首先映射文件的好处。对于大型顺序读取操作禁用磁盘缓存并将文件读入一个小内存缓冲区该文件大于可用的连续虚拟内存地址空间。对于64位应用程序来说这不是什么问题但是对于32位应用程序来说这是一个问题该文件位于可移动驱动器上该文件位于网络驱动器上 示例代码 代码语言javascript 复制 // // ViewController.m // TestCode // // Created by zhangdasen on 2020/5/24. // Copyright © 2020 zhangdasen. All rights reserved. //#import ViewController.h #import sys/mman.h #import sys/stat.h interface ViewController ()endimplementation ViewController- (void)viewDidLoad {[super viewDidLoad];NSString *path [NSHomeDirectory() stringByAppendingPathComponent:test.data];NSLog(path: %, path);NSString *str test str2;[str writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];ProcessFile(path.UTF8String);NSString *result [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];NSLog(result:%, result); }int MapFile(const char * inPathName, void ** outDataPtr, size_t * outDataLength, size_t appendSize) {int outError;int fileDescriptor;struct stat statInfo;// Return safe values on error.outError 0;*outDataPtr NULL;*outDataLength 0;// Open the file.fileDescriptor open( inPathName, O_RDWR, 0 );if( fileDescriptor 0 ){outError errno;}else{// We now know the file exists. Retrieve the file size.if( fstat( fileDescriptor, statInfo ) ! 0 ){outError errno;}else{ftruncate(fileDescriptor, statInfo.st_size appendSize);fsync(fileDescriptor);*outDataPtr mmap(NULL,statInfo.st_size appendSize,PROT_READ|PROT_WRITE,MAP_FILE|MAP_SHARED,fileDescriptor,0);if( *outDataPtr MAP_FAILED ){outError errno;}else{// On success, return the size of the mapped file.*outDataLength statInfo.st_size;}}// Now close the file. The kernel doesn’t use our file descriptor.close( fileDescriptor );}return outError; } void ProcessFile(const char * inPathName) {size_t dataLength;void * dataPtr;char *appendStr append_key2;int appendSize (int)strlen(appendStr);if( MapFile(inPathName, dataPtr, dataLength, appendSize) 0) {dataPtr dataPtr dataLength;memcpy(dataPtr, appendStr, appendSize);// Unmap filesmunmap(dataPtr, appendSize dataLength);} }end