进程空间简介

进程空间简介

进程空间分为:内核空间和用户空间。进程空间的分布如下图所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uAqqeQex-1592141032908)(C:\Users\36262\AppData\Roaming\Typora\typora-user-images\image-20200603162443715.png)]

  • 32位windows下,一个进程空间4G:内核占2G,用户占2G。一个线程默认栈是1M,所以一个进程最大开2048个线程。

    当然内存不会完全拿来做线程的栈,所以最大线程数实际值要小于2048,大概2000个。

  • 32位Linux下,一个进程空间4G:内核占1G,用户占3G。一个线程默认8M,所以最多380个左右线程。

  • 堆的大小理论上约等于进程虚拟空间大小-内核虚拟内存大小:

    windows下,进程的高位2G留给内核,低位2G留给用户,所以进程堆的大小小于2G。

    Linux下,进程的高位1G留给内核,低位3G留给用户,所以进程堆大小小于3G。

  • 进程虚拟空间分布:

    • 程序段(Text):程序代码在内存中的映射,存放函数体的二进制代码。

    • 静态存储区:
      (1)初始化过的数据(Data):在程序运行初已经对变量进行初始化的数据。
      (2)未初始化过的数据(BSS):在程序运行初未对变量进行初始化的数据。

    • 栈 (Stack):存储局部、临时变量,函数调用时,存储函数的返回指针,用于控制函数的调用和返回。

      在程序块开始时自动分配内存,结束时自动释放内存,其操作方式类似于数据结构中的栈。

    • 堆 (Heap):存储动态内存分配,需要程序员手工分配,手工释放。

      注意它与数据结构中的堆是两回事,分配方式类似于链表。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jH09QJHy-1592141032913)(C:\Users\36262\AppData\Roaming\Typora\typora-user-images\image-20200603145808396.png)]

内核空间和用户空间

  • Linux的虚拟地址空间范围为0~4G,Linux内核将这4G字节的空间分为两部分。

    将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF)供内核使用,称为“内核空间”。

    而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF)供各个进程使用,称为“用户空间。

    因为每个进程可以通过系统调用进入内核,因此,Linux内核由系统内的所有进程共享。

    于是,从具体进程的角度来看,每个进程可以拥有4G字节的虚拟空间。

  • linux使用两级保护机制:0级供内核使用,3级供用户程序使用,每个进程有各自的私有用户空间(0~3G)。

    这个空间对系统中的其他进程是不可见的,最高的1GB字节虚拟内核空间则为所有进程以及内核所共享。

  • 空间中存放的是内核代码和数据,而进程的用户空间中存放的是用户程序的代码和数据。不管是内核空间还是用户空间,它们都处于虚拟空间中。 虽然内核空间占据了每个虚拟空间中的最高1GB字节,但映射到物理内存却总是从最低地址(0x00000000)。

    另外,使用虚拟地址可以很好的保护内核空间被用户空间破坏,虚拟地址到物理地址转换过程有操作系统和CPU共同完成(操作系统为CPU设置好页表,CPU通过MMU单元进行地址转换)。

  • 多任务操作系统中的每一个进程都运行在一个属于它自己的内存沙盒中,这个沙盒就是虚拟地址空间(virtual address space),在32位模式下,它总是一个4GB的内存地址块。这些虚拟地址通过页表(page table)映射到物理内存,页表由操作系统维护并被处理器引用。每个进程都拥有一套属于它自己的页表。

  • 这里是32位内核地址空间划分,64位内核地址空间划分是不同的。

  • 现代的操作系统都处于32位保护模式下。每个进程一般都能寻址4G的物理空间。但是我们的物理内存一般都是几百M,进程怎么能获得4G 的物理空间呢?这就是使用了虚拟地址的好处,通常我们使用一种叫做虚拟内存的技术来实现,因为可以使用硬盘中的一部分来当作内存使用 。

 内核空间和用户空间中最顶部的段是栈,大多数编程语言将之用于存储函数参数和局部变量。
 调用一个方法或函数会将一个新的栈帧(stack frame)压入到栈中,这个栈帧会在函数返回时被清理掉。
 由于栈中数据严格的遵守FIFO的顺序,这个简单的设计意味着不必使用复杂的数据结构来追踪栈中的内容,只需要一个简单的指针指向栈的顶端即可。因此压栈(pushing)和退栈(popping)过程非常迅速、准确。
 进程中的每一个线程都有属于自己的栈。

  通过不断向栈中压入数据,超出其容量就会耗尽栈所对应的内存区域,这将触发一个页故障(page fault),而被Linux的expand_stack()处理,它会调用acct_stack_growth()来检查是否还有合适的地方用于栈的增长。
  如果栈的大小低于RLIMIT_STACK(通常为8MB),那么一般情况下栈会被加长,程序继续执行,感觉不到发生了什么事情。这是一种将栈扩展到所需大小的常规机制。
  然而,如果达到了最大栈空间的大小,就会栈溢出(stack overflow),程序收到一个段错误(segmentation fault)。
  注:动态栈增长是唯一一种访问未映射内存区域而被允许的情形,其他任何对未映射内存区域的访问都会触发页错误,从而导致段错误。一些被映射的区域是只读的,因此企图写这些区域也会导致段错误。

  与栈一样,堆用于运行时内存分配。但不同的是,堆用于存储那些生存期与函数调用无关的数据。大部分语言都提供了堆管理功能。
  在C语言中,堆分配的接口是malloc()函数。如果堆中有足够的空间来满足内存请求,它就可以被语言运行时库处理而不需要内核参与。否则,堆会被扩大,通过brk()系统调用来分配请求所需的内存块。
  堆管理是很复杂的,需要精细的算法来应付我们程序中杂乱的分配模式,优化速度和内存使用效率。处理一个堆请求所需的时间会大幅度的变动。
  实时系统通过特殊目的分配器来解决这个问题。堆在分配过程中可能会变得零零碎碎,如下图所示。
  
  一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式类似于链表。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8558NDhz-1592141032916)(C:\Users\36262\AppData\Roaming\Typora\typora-user-images\image-20200603155427796.png)]

img

静态存储区

 BSS和data段保存的都是静态变量。区别在于:
 
 	BSS段保存的是未被初始化的静态变量,他们的值不是直接在程序的源码中设定的。BSS内存区域是匿名的,它不映射
 	到任何文件。如果你写static intcntActiveUsers,则cntActiveUsers的内容就会保存到BSS中去。
 	
    data段保存的是在源代码中已经初始化的静态变量。它不是匿名的,它映射了一部分的程序二进制镜像,也就是源代码
    中指定了初始值的静态变量。所以,如果你写static int cntActiveUsers=10,则cntActiveUsers的内容就保存在了数据段
    中,而且初始值是10。尽管数据段映射了一个文件,但它是一个私有内存映射,这意味着更改此处的内存不会影响被映
    射的文件。
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值