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

模板下载免费网站谷歌优化技术

模板下载免费网站,谷歌优化技术,中企动力科技集团股份有限公司,如何做漂亮的网站首页知识回顾 在 C 语言的学习过程中#xff0c;我们知道内存是可以被划分为栈区#xff0c;堆区#xff0c;全局数据区#xff0c;字符常量区#xff0c;代码区的。他的空间排布可能是下面的样子#xff1a; 其中#xff0c;全局数据区#xff0c;可以划分为已初始化全局…知识回顾 在 C 语言的学习过程中我们知道内存是可以被划分为栈区堆区全局数据区字符常量区代码区的。他的空间排布可能是下面的样子 其中全局数据区可以划分为已初始化全局数据区和未初始化全局数据区 栈区向下增长。(表现形式之一同一个栈桢中先定义的变量地址更高后定义的变量地址更低)堆区向上增长。(随着堆区的使用变量的地址是向上增长的) 堆栈之间的区域叫做共享区这个会在动静态库的时候详解 内核空间这是操作系统的代码和数据 我们可以写一个简单的代码来验证一下 #includestdio.h #includestdlib.hint g_val 10;int main() {int a 1;int b 2;char* str hello linux;int* heap1 (int*)malloc(sizeof(int));printf(栈区先定义的变量: a: %p\n, a);printf(栈区后定义的变量: b: %p\n, b);printf(堆区定义的变量: heap1: %p\n, heap1);printf(全局数据: g_val_init: %p\n, g_val);printf(字符常量区: str: %p\n, str);return 0; }这里说明一下static 修饰的局部变量只能初始化一次作用域在局部但是生命周期和全局变量一样那是因为在编译的时候就将这个局部变量定义在了全局数据区你可以通过打印静态变量的地址看出来 进程地址空间引入 我们学习了进程地址空间之后就能解决在进程创建的那一节中提出的一个问题用 id 变量接收 fork 函数的返回值为什么一个变量能读出来两个不同的值 我们来看一段代码定义一个全局变量 g_val 100使用 fork 创建子进程子进程每隔一秒打印 pid ppid g_val g_val5 秒之后将 g_val 修改为 200父进程每隔一秒打印 pid g_val g_val。看看能观察到什么现象 #includestdio.h #includesys/types.h #includeunistd.hint g_val 100;int main() {pid_t id fork();if(id 0){//这是子进程int cnt 5;while(1){printf(I am child process, pid: %d, ppid: %d, g_val: %d, g_val: %p\n, getpid(), getppid(), g_val, g_val);sleep(1);if(cnt) cnt--;else{g_val 200;printf(child process change g_val: 100-200\n);cnt--;}}}else if(id 0){while(1){printf(I am parent, pid: %d, g_val: %d, g_val: %p\n, getpid(), g_val, g_val);sleep(1);}}else{perror(fork():);}return 0; }5 秒中之后子进程修改全局变量 g_val 子进程修改父子进程的共享数据发生写时拷贝为子进程的 g_val 重新开辟空间打印的时候子进程的 g_val 等于 200父进程的 g_val 等于 100。这没问题但是我们发现发生写时拷贝之后父子进程打印出来的 g_val 是一样的 怎么可能同一个变量同一个地址同时读取读到了不同的内容呢 因此我们得到一个结论这里打印出来的地址绝对不是物理地址我们把这个打印出来的地址称为虚拟地址或者线性地址。 我们在写 C/C 程序中使用的指针地址其实都不是物理地址 什么是进程地址空间 我们先不说什么是进程地址空间我们先来看子进程创建时父子进程是如何做到共享代码和数据的 首先父进程有自己的 task_struct 在这个结构体中有一个字段叫做 mm_struct*能通过这个字段找到父进程的虚拟地址(线性地址)。我们定义了一个全局变量 g_val 就会给这个变量分配一个虚拟地址在 Linux 操作系统中有一个叫做页表的东西你可以把他理解为一个 map 他存储的是虚拟地址到物理地址的映射关系也就是说通过页表我们能够通过虚拟地址访问物理地址(物理内存)fork() 创建子进程会为子进程创建 task_struct拷贝父进程的虚拟内存拷贝虚拟内存的页表 拷贝父进程的虚拟内存 那么这个全局变量的虚拟地址在父子进程中都是一样的拷贝父进程的页表那么通过相同的虚拟地址相同的页表就能做到访问相同的物理内存 上面的论述证明了父子进程数据是共享的代码共享的原理是一样的 我们可以画出示意图 这个示意图可以帮助大家理解虚拟地址和物理地址其中页表这么画是不对的我们后面会详解页表的真实结构不过页表以这种结构来理解是没有大问题的 子进程是如何做到 g_val 的写时拷贝的 父子进程代码和数据是共享的当我们在子进程中对 g_val 做出修改的时候操作系统就会为子进程重新开辟 g_val 类型大小的空降然后将 g_val 拷贝到这块新的空间并修改 g_val 的值最后修改 g_val 虚拟地址映射的物理地址完成写时拷贝 写时拷贝的本质就是重新开辟空间但是这个过程中左侧的虚拟地址是 0 感知的虚拟地址不关心也不会被影响 现在我们就能解决当初遗留的问题啦为啥 fork 之后的 id 有两个值 fork() 函数在 return 之前就将子进程创建好了return 的本质也是写入也就是说会发生写时拷贝父子进程虽然在访问同一个 id但是根据页表访问的是不同的物理地址(物理内存)这就是一个 id 变量能够读出两个值的原因 地址空间 以 32 位的 Linux 操作系统为例地址空间就是地址排列组合形成的地址范围 [ 0 , 2 32 ) [0, 2^{32}) [0,232)。 在 32 位操作系统中有 32 位的地址总线和数据总线CPU 和 内存通过系统总线相连每一根总线有 01 两种状态(高电平低电平那么 32 根总线就有 2 32 2^{32} 232 种组合通过 CPU 与 内存相连的总线CPU 能读取内存的数据 2 32 2^{32} 232 对应 4 G B 4GB 4GB 的内存大小因此想要映射出来 4 G B 4GB 4GB 的物理内存就需要同等范围的虚拟内存 如何理解地址空间的区域划分 这篇文章的开头我们看到的内存区域划分就是地址空间的区域划分我们又知道了地址空间就是地址排列组形成的地址范围地址空间的本质是内核的一个数据结构对象地址空间也是要被操作系统管理起来的 因此我们就能以一种较为简单的方式对地址空间进行区域划分 只要维护两个指针用来标识一个区域划分的起始地址和结束地址就是对地址空间做划分啦 对虚拟地址的空间做出这样的划分之后我们就能够粗略判断访问变量的时候是否发生越界访问啦 在地址空间中最小单位就是一个地址这个地址是可以被直接使用的 为了检验我们的结论正确性可以看看 Linux 内核的源代码是不是这个样子 我们可以看到在 task_struct 中有一个 struct mm_struct跳转到 mm_struct 内部我们看到了诸如 start_code end_code 这样的字段证明我们得出的结论是没有问题的 为什么要有进程地址空间 让所有的进程以统一的视角看待内存 任何一个进程都有自己的地址空间地址空间上的虚拟地址通过页表的映射访问物理内存 这就是任何一个进程访问物理内存的逻辑在访问内存的方式做到了统一 如果没有进程地址空间task_struct 中就得维护进程的代码和数据在内存中的位置一旦进程的状态发生切换就会修改 task_struct 中的内容太过麻烦 保护物理内存 增加进程地址空间后我们想要访问物理内存就必须通过页表的转换在这个转化的过程我们就可以对寻址进行审查一旦出现访问异常操作系统就能及时拦截使得请求不会到达内存能够有效地保护物理内存 事实上页表中还维护了一个字段表示对物理内存的读写权限 如图在代码区有一段代码虚拟地址是 0x11 物理地址是 0x22 通过页表他们之间就存在映射关系代码区的代码权限标志位都是 r。当我们尝试对代码区做修改时根据 CPU 中的 cr3 寄存器找到页表发现我们想要对权限标志位为 r 的物理内存做修改操作系统会直接拦截保证了物理内存的安全 根据上面的现象我们能够得出结论 物理内存根本没有权限管理的概念不然可执行程序是如何被加载到内存中的cr3 寄存器为何能够直接访问物理内存而不做权限的检查凭什么代码是只读的程序加载到内存中就是对物理内存进行写入物理内存本身并没有只读一说因此是虚拟地址通过页表映射到物理地址上时页表中权限标志位为 r即只读当我们对代码区做修改操作系统能够根据权限标志位进行拦截所以才说代码区是只读的 实现进程管理模块与内存管理模块的解耦 虚拟化 进程地址空间提供了虚拟化的抽象使得每个进程都以为它拥有整个系统的内存。这使得进程可以独立运行而无需关心其他进程的内存布局。独立的页表 每个进程都有自己的页表负责将其虚拟地址空间映射到物理地址。这样不同进程可以有不同的页表实现了内存空间的隔离。页表的管理成为内存管理模块的责任。惰性加载 Linux 操作系统可以采用懒加载的策略只在需要时将进程的部分地址空间加载到物理内存中。这种分页和惰性加载的方式使得内存管理更加灵活。 进程之间的地址空间是隔离的一个进程的内存操作不会直接影响其他进程。这有助于提高系统的安全性和稳定性。 换句话说进程之间可以有完全相同的虚拟地址但是能根据相同的虚拟地址访问到不同的物理地址 程序的惰性加载 不知道大家是否有一个疑问我们完的电脑游戏下载 下来可能就是几十个 GB但是我们的物理内存就那么一点儿大这是怎么做到将游戏运行起来的呢 在学习进程的状态时我们知道在操作系统学科中有挂起的状态进程处于挂起状态时他的代码和数据会被放在磁盘中只保留 PCB 在物理内存 可是 Linux 操作系统中并没有挂起状态啊如何知道当前进程的代码和数据在不在内存中呢 首先我们需要达成一个共识现代操作系统几乎不会做浪费时间和空间的事情假设我们有一个可执行程序他的代码和数据有 20 MB在加载可执行程序的时候我们加载了 5MB 的代码和数据于是这个进程跑起来了结果发现在这个进程被调度的过程中只用到了 1MB 的代码和数据可是当初加载可执行程序的时候加载了 5MB 哇于是就产生了内存空降的浪费这是不被允许的因为现代操作系统中几乎不会做浪费时间和空间的事情 事实上在页表中还有一个字段用来标识某个虚拟地址的代码和数据是否加载到内存比如该标志位为 1 代表代码和数据已经加载到内存中啦 当我们执行代码时CPU 通过 cr3 寄存器找到页表发现该虚拟地址并未加载代码就会引发缺页中断进程发生缺页中断就会再加载一部分代码和数据先分配内存空间。然后将物理地址填充到对应的虚拟地址就可以继续执行代码啦 正是因为有页表和虚拟内存的存在进程不需要关系内存的事儿当发生缺页中断的时候操作系统就会自动调用内存管理模块的相关功能也就实现了进程管理与内存管理的解耦 现在我们就可以回答上面的问题啦不管你的代码和数据有多少加载可执行程序的时候我就加载一点点剩下的代码和数据往页表中填充虚拟地址就行当我们要访问这些为加载的代码和数据就会引发缺页中断再次加载一部分代码和数据这也提高了内存的使用效率 知识巩固 什么是进程呢学到这里内核数据结构又多了一部分啦 进程 内核数据结构 ( t a s k s t r u c t m m s t r u c t 页表 ) 代码和数据 进程 内核数据结构(task_struct mm_struct 页表) 代码和数据 进程内核数据结构(tasks​tructmms​truct页表)代码和数据 进程切换因为 mm_struct 是被维护在 task_struct 中的cr3 寄存器指向当前进程的页表cr3 寄存器中的内容属于进程的上下文因此进程地址空间与页表都是自动切换的 进程之间具有独立性怎么做到的 每个进程都有自己独立的内核数据结构。每个进程的虚拟地址可以完全一样只要通过页表映射出来的物理地址不一样就行实现了代码和数据层面的解耦进程资源的释放并不会影响其他进程 因此进程的代码和数据加载到物理内存的位置并不重要 知识点总结 什么是进程地址空间为什么要有地址空间
http://www.dnsts.com.cn/news/80067.html

相关文章:

  • 厦门网站制作阳哥积分兑换商城网站建设
  • 阿里做网站怎么做人工智能在未来可以
  • 有哪些做婚礼平面设计的网站有哪些做网站要的带宽是什么
  • 网站前期设计app推广怎么联系一手代理
  • 北京市朝阳区住房建设网站wordpress公众号管理员
  • 高端网站建设 工业seo搜索引擎优化期末考试
  • 网站开发编程的工作方法自己做网站 教程
  • 彩票开奖网站开发wordpress制作网站教程
  • 网站负责人信息表平面设计网页设计专员
  • 有谁做彩票网站重庆市建设工程造价信息网公众号
  • 广州市企业网站制作公司做特产的网站
  • 控制面板网站新建网站推广
  • 网站制作排序北京建设工程建设交易信息网站
  • 网站建设前端和后端域名访问
  • 网站建设基本概述阿里云网站备案幕布
  • 企业多语言网站开源银行网站开发技术方案
  • 3d建模有前途吗做搜狗pc网站优化
  • 中企动力网站后台 好用吗广西电网公司建设年鉴
  • 云南营销型网站建设谷歌 wordpress 插件
  • 河南天元建设公司网站广告优化师的职业规划
  • 做俄罗斯外贸的网站设计wordpress 密码 算法
  • 黄页引流推广网站天河怎样优化网站建设
  • 电脑打不开建设银行网站私人ftp服务器
  • 服务网站排名咨询网站推广工具
  • 怎么能看出别人的网站是哪一家做沈阳网页设计培训
  • 兰州网站建设专家企业做网站哪家网站好
  • 网站流量15gps软件电脑版
  • 网站建设服务器租赁百度收录站长工具
  • 做变形记图网站营销型网站建设大千建站
  • 网站微信推广方案wordpress局域网