准备知识
虚拟地址空间
对32位cpu来说,它逻辑上可访问的地址空间是2的32次方Byte,也就是4GB
对64位cpu来说,它逻辑上可访问的地址空间是2的64次方Byte,4G*4GB=16GG=16M*TB
但是,因为其他硬件的限制,cpu并没有那么多的物理地址空间可以访问。以64位cpu为例,家用内存条就8g16g的,或者连接cpu的地址总线长度只有32位只能访问4个g
cpu理论上能访问的地址空间是虚拟地址空间,实际能访问的ram地址空间是物理地址空间。虚拟地址通过MMU翻译成物理地址空间
每个进程都有自己【独立】的虚拟地址空间,示意图如图:
共享内存
当不同进程所在的虚拟地址空间都映射到同一块物理地址空间上,就实现了共享内存,就这么简单。这是linux下进程间通信的最快方式,因为这块共享内存属于用户空间而无需受到内核空间的管理。如图,进程1/2的虚拟空间存在共同指向,指向了同一块Ram内存区
其他方式下如管道/消息队列都在内核空间,应用程序A需要将自己的数据从用户空间的内存区域copy到内核空间的内存区域,然后应用程序B再从内核空间的内存区域将这份数据copy到自己用户空间的内存区域中,非常麻烦
好了,现在引出了用户空间和内核空间的问题
用户空间和内核空间
为了让系统的数据和用户的数据互不干扰,保证系统的稳定性,用户进程不能直接操作内核。于是操作系统将虚拟地址空间划分为两部分,内核空间+用户空间。这样即使你用户的程序崩溃了,我内核还是稳稳的,操作系统还是正常工作的
内核空间 + 用户空间 = 虚拟地址空间
32位的linux操作系统的虚拟地址空间为4G,最高1G(从虚拟地址0xC0000000到0xFFFFFFFF)划供内核使用,内核空间可以执行任意命令,调用系统一切资源;较低的3G(从虚拟地址0x00000000到0xBFFFFFFF),划供各个进程使用,为用户空间
进程的内核态与用户态:
(1)当一个程序执行了系统调用或者触发某个异常(软中断),此时就会陷入内核空间(内核态)。此时处理器处于最高特权级0级。当进程处于内核态时,执行的内核代码会使用当前进程的内核栈。每个进程都有自己的内核栈
(2)当进程在执行用户自己的代码时,处于用户运行态(用户态)。处理器在特权级最低的(3级)用户代码中运行
内存中的堆栈
- 堆(Heap)是应用程序在运行时请求操作系统给自己分配内存(非数据结构里的堆[优先队列]),是申请->给予的过程,C/C++分别用malloc/New请求分配Heap,用free/delete销毁内存。由于是操作系统管理的内存分配,在分配和销毁时都要占用时间,所以堆分配的操作是偏重的!但是堆的好处是可以获得很大的内存
- 栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)主动为这个线程建立的存储区域,该区域具有FILO的特性,在编译的时候可以指定需要的Stack的大小。在编程中,例如C/C++中,所有的局部变量都是从栈中分配内存空间,实际上也不是什么分配,只是从栈顶向上用就行,在退出函数的时候,只是修改栈指针就可以把栈中的内容销毁,所以速度最快。栈其实是由寄存器ebp和esp指向的一片内存空间(ebp指向栈底,esp指向栈顶)
堆是程序运行时申请的动态内存,而栈只是指一种使用堆的方法(先进后出)
linux查看创建线程默认分配的栈大小
# 查看线程默认栈空间大小
ulimit -s
# 临时修改线程默认栈空间大小(单位KBytes)
ulimit -s 102400
// 修改为100M。另可以在/etc/rc.local 内 加入 ulimit -s 102400 则可以开机就设置栈空间大小