帮人做网站要怎么赚钱农业方面的网站建设升级

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

帮人做网站要怎么赚钱,农业方面的网站建设升级,网站建设与管理广东药学院,网站建设微信商城开发文章目录 一、 程序地址空间回顾1.程序地址空间各区域分布验证2.引入虚拟地址概念 二、进程地址空间#xff08;虚拟地址空间#xff09;的管理三、虚拟地址空间的作用 一、 程序地址空间回顾 1.程序地址空间各区域分布验证 #include stdio.h #include unistd.h… 文章目录 一、 程序地址空间回顾1.程序地址空间各区域分布验证2.引入虚拟地址概念 二、进程地址空间虚拟地址空间的管理三、虚拟地址空间的作用 一、 程序地址空间回顾 1.程序地址空间各区域分布验证 #include stdio.h #include unistd.h #include stdlib.hint g_unval; int g_val 100;int main(int argc, char *argv[], char *env[]) {const char *str helloworld;printf(code addr: %p\n, main);printf(read only string addr: %p\n, str);static int test 10; printf(init global addr: %p\n, g_val);printf(test static addr: %p\n, test); printf(uninit global addr: %p\n, g_unval);char heap_mem (char)malloc(10);char heap_mem1 (char)malloc(10);char heap_mem2 (char)malloc(10);printf(heap addr: %p\n, heap_mem); printf(heap addr: %p\n, heap_mem1); printf(heap addr: %p\n, heap_mem2); printf(stack addr: %p\n, heap_mem); printf(stack addr: %p\n, heap_mem1); printf(stack addr: %p\n, heap_mem2); printf(argv[0]: %p\n, argv[0]);printf(env[0]: %p\n, env[0]); return 0; }这是在linux系统gcc编译器中运行的结果在windows系统下的vs等编译器运行可能会有不同结果因为windows系统下编译器会优化代码执行过程从而影响结果 code addr: 0x40057d read only string addr: 0x400780 init global addr: 0x60103c test static addr: 0x601040 uninit global addr: 0x601048 heap addr: 0x63e010 heap addr: 0x63e030 heap addr: 0x63e050 stack addr: 0x7ffd840398c0 stack addr: 0x7ffd840398b8 stack addr: 0x7ffd840398b0 argv[0]: 0x7ffd8403b803 env[0]: 0x7ffd8403b80f各区域地址划分是符合规则的 代码区正文代码存放可执行的代码如函数体的二进制代码和 只读常量。它们的地址是最小的静态区初始化数据 和 未初始化数据存放全局变量 和 静态数据。它们的地址只比代码区大未初始化数据 地址高于 初始化数据堆区使用malloc、calloc、realloc等函数动态开辟的空间。地址高于静态区开辟空间时地址向上增长栈区栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。地址高于堆区申请空间时地址向下增长命令行参数 和 环境变量区域地址高于栈区 最后以一个问题收尾以上打印出来的所有地址都是真实对应的物理地址嘛 答这些地址全都不是真实的物理地址而是虚拟地址解释见下节 2.引入虚拟地址概念 从示例出发引出虚拟地址概念 #include stdio.h
#include unistd.h
#include stdlib.h int g_val 0; int main()
{ pid_t id fork(); if(id 0) { //child while(1) { printf(child[%d], g_val: %d, g_val: %p\n, getpid(), g_val, g_val); g_val; sleep(1); } } else { //parent while(1) { printf(parent[%d], g_val: %d, g_val: %p\n, getpid(), g_val, g_val); sleep(1); } } return 0;
} 同一个地址怎么可能会查出来不同的值 这侧面证明了 用户在语言层面中使用的地址根本就不是物理地址而是虚拟地址。 虚拟地址具体是指什么 进程的pcbtask_struct中存放着虚拟地址空间详见 “进程地址空间的管理” 的起始地址虚拟地址空间上的地址就是虚拟地址。 进程的代码和数据保存在内存中。进程在运行时先找到虚拟地址再通过页表把虚拟地址转换成物理地址然后通过物理地址访问内存中存放的代码和数据。 解释以上代码运行结果中的现象同一个地址查出来不同的值 我们自己写的可执行程序父进程运行时使用fork函数创建子进程 创建出的子进程是以父进程为模板的它的虚拟地址空间 和 页表是父进程的拷贝它的pcb中内容与父进程大体相同只修改了pid 和 ppid等少量属性。 因为子进程的虚拟地址空间 和 页表是父进程的拷贝所以页表转换的物理地址 指向 父进程的代码和数据刚创建出的子进程共享父进程的代码 和 数据。 子进程和父进程共享数据直到发生数据写入一方要修改数据时为了维护进程间数据的独立性一方修改数据不能影响另一方该数据会进行写实拷贝对子进程和父进程的该数据进行分离这样一方修改数据就不会影响另一方了。 比如上述代码中子进程刚创建出来的时候共享父进程的数据子进程共享父进程的g_val变量当子进程对g_val变量进行修改的时候为了维护进程间数据的独立性要对子进程和父进程的该数据进行分离也就是对该数据进行写实拷贝具体步骤就是 在内存开辟一个新空间然后把该内容拷贝到新空间最后将子进程页表中对应的物理地址修改成新空间的地址。 子进程的g_val变量 和 父进程的g_val变量分离子进程对拷贝到新空间的g_val变量修改不会影响父进程的g_val变量。 需注意的是写实拷贝的整个过程中都不会对虚拟地址进行修改修改的是虚拟地址对应的物理地址。 这就是同一个地址虚拟地址能查出来不同的值的原因 子进程的页表是父进程的拷贝子进程 和 父进程的g_val变量的虚拟地址是一样的。子进程修改g_val时发生写实拷贝修改的是虚拟地址对应的物理地址这时子进程和父进程的g_val变量的 虚拟地址仍然是一样的但它们的虚拟地址对应的物理地址是不同的所以同一个地址虚拟地址能查出来不同的值数据存在物理地址对应的空间中 二、进程地址空间虚拟地址空间的管理 理解虚拟地址空间 大富翁有很多的私生子这些私生子彼此都不知道对方的存在。大富翁有十个亿的资产他和每一个私生子都说“我有10个亿的资产等我去世之后就由你继承这些资产”。大富翁给每一个私生子画大饼于是每一个私生子都认为自己以后能独自拥有这10个亿的资产。 大富翁平时对每一个私生子的要钱申请基本有求必应当然这些请求得在合理范围比如一次要个几千、几万、 甚至几十万之类。如果私生子的要钱请求过于高比如一次要几千万、一亿、十亿之类大富翁会直接数落他一顿“我还没死呢你就想掏空我的财产”然后驳回他的请求。 在linux系统下其实大富翁就相当于操作系统进行内存管理私生子就相当于进程操作系统给每一个进程画的大饼就叫虚拟地址空间虚拟地址空间的地址编号 是和 内存的物理地址编号一样多的相当于操作系统跟每一个进程都说“你独自享有整个内存空间其实是给每一个进程配一个虚拟地址空间来哄骗它们”。实际上进程运行过程中每一次申请内存空间都不能过多否则空间申请不会成功每一个进程的代码和数据其实都只占据了内存空间中的一小部分。 虚拟地址空间的实质 虚拟地址空间本质其实就是一个结构体不够全面后面补充虚拟地址空间本质struct mm_struct 进程pcbtask_struct中存放了指向 struct mm_struct 的指针 struct task_struct { …struct mm_struct *mm; //对于普通的⽤⼾进程来说该字段指向他的虚拟地址空间的⽤⼾空间部分… }struct mm_struct {…struct vm_area_struct *mmap; // 指向虚拟区间(VMA)链的开头…// 代码段、数据段、堆栈段、参数段及环境段的起始和结束地址。 unsigned long start_code, end_code, start_data, end_data;unsigned long start_brk, brk, start_stack, end_stack;unsigned long arg_start, arg_end, env_start, env_end; }虚拟地址空间中划分了很多分区要想描述虚拟地址空间就得描述出其中的每一个分区struct mm_struct 就采取了记录每一个分区的起始和结束地址的方式 来划分出虚拟地址空间中的各个分区 如下 但实际上只使用 struct mm_struct 是无法全面描述虚拟地址空间的。 注意到 struct mm_struct 中的struct vm_area_struct *mmap 指针变量还未被使用要想全面描述虚拟地址空间还得把这个指针变量使用起来。 先介绍一下 struct vm_area_struct
struct vm_area_struct {unsigned long vm_start; // 虚拟内存区域的起始unsigned long vm_end; // 虚拟内存区域的结束struct vm_area_struct *vm_next, *vm_prev; // 前后指针struct mm_struct *vm_mm; // 回指所属的 mm_structpgprot_t vm_page_prot; // 所属分区的权限… }linux内核使用 vm_area_struct 结构来表示⼀个独立的虚拟内存区域(VMA)。由于虚拟地址空间不同分区的虚拟内存区域功能和内部机制都不同因此⼀个进程要使用多个vm_area_struct结构来分别表示不同类型的虚拟内存区域。使用双链表管理多个vm_area_struct结构描述一个进程不同类型的虚拟内存区域struct mm_struct中的mmap指针指向这个双链表结构 所以最终结论是虚拟地址空间 struct mm_struct 内核数据结构由 struct vm_area_struct 组成的双链表 struct mm_struct 描述了虚拟地址空间的整理情况虚拟地址空间各个区域的划分由 struct vm_area_struct 组成的双链表 描述了 虚拟地址空间各个区域的详细信息功能和内部机制它们共同构建了虚拟地址空间。 三、虚拟地址空间的作用 增加了虚拟地址空间虚拟地址就必须通过页表转换成物理地址才能去访存在地址转换过程中进行安全审核比如地址、权限检查变相保护了物理内存的安全 假如代码中使用的地址都是物理地址那么如果进程A的代码中错误的使用了一个野指针而这个野指针又指向另一个进程的数据对野指针指向的数据进行修改。这样会出大问题本来只是你一个进程出问题又影响到其它进程这不符合进程间的独立性原则。 所以用户在代码中使用的地址绝对不可以是物理地址而是要使用虚拟地址在页表进行虚拟地址向物理地址的转换过程中对这些不在页表中的野指针地址进行警告报错处理。
实际上页表还有一列权限栏 1char * str “hello world” // hello world是字符串常量保存在代码区代码区的权限是只读str指向该字符串常量起始地址 *str ‘c’ // 执行这句代码会报错而且报的是运行时出错。解释运行到这句代码时要进行访存修改数据虚拟地址 向 物理地址转换的过程中发现该地址对应的权限是只读而此时要进行的操作是写操作没有写权限页表阻止这次地址转换并进行报错处理。 2const char * str “hello world” // 如果定义字符串常量时加了const修饰再进行 *str ‘c’ 操作时编译器就会报编译时出错。解释用const修饰是告诉编译器这是不能修改的数据编译器知道这个信息后就会对这个数据的写入操作进行语法检查编译器就能发现 *str ‘c’ 操作的语法错误语法检查是编译过程做的事所以报的是编译时出错。 进程看待自己的代码和数据全部都是有序看待这得益于虚拟地址空间的有序性 可执行程序执行时它的代码和数据理论上可以加载到内存上的任意位置这也就意味着实际上可执行程序的代码和数据在内存上的排布是随机的、无序的。 但其实有了虚拟地址空间之后进程运行时根本就不需要关心它的代码和数据到底存放在内存中的哪一个位置进程只需要和虚拟地址空间打交道就行而虚拟地址空间的排版是非常有规律的代码地址存在代码区局部变量地址存在栈区动态开辟空间的地址存在堆区得到这些虚拟地址后后续操作由操作系统自动完成页表将虚拟地址转换成物理地址再进行访存。