注:本文所涉及的环境为Linux,下文讨论的栈跟内核栈,没有任何的关系

  这里有如下几个问题,线程栈的空间是开辟在那里的? 线程栈之间可以互访吗?为什么在使用pthread_attr_setstack函数时,需要设置栈的大小,而进程task_struct的 mm_struct *mm 成员中却并没有却并没有stack_size这个成员项,怎么保存的栈大小呢?

  进程栈:

  进程用户空间的管理在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指针变量相同。