Unix进程的堆和栈的区别

 我来答
greystar_cn
2018-02-26 · 知道合伙人软件行家
greystar_cn
知道合伙人软件行家
采纳数:16407 获赞数:17261
本人主要从事.NET C#方向的技术开发工作,具有10多年的各类架构开发工作经验。

向TA提问 私信TA
展开全部
进程用户空间的管理在task_struct 的mm_struct *mm成员中体现, mm中的成员定义了用户空间的布局情况如图一。 用户空间的栈起始于STACK_TOP, 如果设置了PF_RANDOMIZE,则起始点会减少一个小的随机量,每个体系结构都必须定义STACK_TOP, 大多数都设置为TASK_SIZE, 在32位机上该值为0XC0000000。经过随机处理后,进程栈的起始地址将存放在mm->start_stack中,可以通过cat /proc/xxx/stat 查看。
如图一,栈从上而下扩展,而用于内存映射的区域起始于mm->mmap_base, mm->mmap_base通过调用mmap_base函数来初始化,为了确保栈不与mmap区域不发生冲突,两者之间设置了一个安全间隙。mmap_base函数源代码如下:

#define MIN_GAP (128*1024*1024) #define MAX_GAP (TASK_SIZE/6*5)static inline unsigned long mmap_base(struct mm_struct *mm){ unsigned long gap = current->signal->rlim[RLIMIT_STACK].rlim_cur; // rlim_cur 默认为8388608,及8M, 可以使用 getrlimit(RLIMIT_STACK, &limit) 查看
unsigned long random_factor = 0; if (current->flags & PF_RANDOMIZE)
random_factor = get_random_int() % (1024*1024); if (gap < MIN_GAP) // 通过MIN_GAP来保证,进程栈的大小至少为128MB
gap = MIN_GAP; else if (gap > MAX_GAP) // 栈的最大空间为TASK_SIZE/6*5, 及2.5G
gap = MAX_GAP; return PAGE_ALIGN(TASK_SIZE - gap - random_factor); // 通过保留random_factor空间大小的间隙来防止栈溢出}

图 一 IA-32计算机上虚拟地址空间的布局
线程栈:
线程包含了表示进程内执行环境必需的信息,其中包括进程中标示线程的线程ID,一组寄存器值,栈,调度优先级和策略, 信号屏蔽字,errno变量以及线程私有数据。进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本,程序的全局内存和堆内存,栈以及文件描述符,所以线程的mm_struct *mm指针变量和所属进程的mm指针变量相同。
在创建线程的时候,可以通过pthread_attr_t来初始化线程的属性,包括线程的栈布局信息,如栈起始地址stackaddr, 栈大小stacksize。 具体需要通过方法
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);// 注:stackaddr 指向为该线程开辟的空间,该空间可以使用malloc或者mmap来开辟,而不能来自进程的栈区。开辟的stackaddr所指向的动态空间需要自己负责释放。

当然也可将线程栈的空间管理交给系统,如果想改变系统默认的栈大小8MB,可以通过
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);// 注:stacksize最小值为16384,单位为字节

由上面的API接口,可以得到,线程栈的stacksize是保存在pthread_attr_t中的,可以通过人为的指定,也可以通过在创建线程的时候读取系统的配置文件来初始化stacksize,当初始化完栈的起始地址,和大小后,便可以通过
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);

来初始化线程栈末尾之后用以避免栈溢出的缓冲区的大小,如果应用程序溢出到此缓冲区中,这个错误可能会导致 SIGSEGV 信号被发送给该线程, 从而造成段错误,缓冲区默认设置为PAGESIZE个字节。因为线程的mm->start_stack和所属进程相同,所以线程栈的起始地址并没有存放在task_struct中,应该只是使用attr中的stackaddr,来初始化task_struct->thread-> sp(sp指向struct pt_regs对象,该结构体用于保存用户进程或者线程的寄存器现场)。
总结:线程栈的空间开辟在所属进程的堆区,线程与其所属的进程共享进程的用户空间,所以线程栈之间可以互访。线程栈的起始地址和大小存放在pthread_attr_t 中,栈的大小并不是用来判断栈是否越界,而是用来初始化避免栈溢出的缓冲区的大小(或者说安全间隙的大小)
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式