一 Pthread 并发编程——深入剖析线程基本元素和状态( 二 )

上面程序的执行结果如下所示:

一 Pthread 并发编程——深入剖析线程基本元素和状态

文章插图
可以看到函数参数已经做到了正确传递 。
深入理解参数 attr在深入介绍参数 attr 前,我们首先需要了解一下程序的内存布局,在64位操作系统当中程序的虚拟内存布局大致如下所示,从下往上依次为:只读数据/代码区、可读可写数据段、堆区、共享库的映射区、程序栈区以及内核内存区域 。我们程序执行的区域就是在栈区 。
一 Pthread 并发编程——深入剖析线程基本元素和状态

文章插图
根据上面的虚拟内存布局示意图,我们将其简化一下得到单个线程的执行流和大致的内存布局如下所示(程序执行的时候有他的栈帧以及寄存器现场,图中将寄存器也做出了标识):
一 Pthread 并发编程——深入剖析线程基本元素和状态

文章插图
程序执行的时候当我们进行函数调用的时候函数的栈帧就会从上往下生长,我们现在进行一下测试 , 看看程序的栈帧最大能够达到多少 。
#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>int times = 1;void* func(void* arg) {char s[1 << 20]; // 申请 1MB 内存空间(分配在栈空间上)printf("times = %d\n", times);times++;func(NULL);return NULL;}int main() {func(NULL);return 0;}上述程序的执行结果如下图所示:
一 Pthread 并发编程——深入剖析线程基本元素和状态

文章插图
从上面的程序我们可以看到在第 8 次申请栈内存的时候遇到了段错误 , 因此可以判断栈空间大小在 8MB 左右,事实上我们可以查看 linux 操作系统上,栈内存的指定大?。?
一 Pthread 并发编程——深入剖析线程基本元素和状态

文章插图
事实上在 linux 操作系统当中程序的栈空间的大小默认最大为 8 MB 。
现在我们来测试一下,当我们创建一个线程的时候,线程的栈的大小大概是多少:
#include <stdio.h>#include <pthread.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>int times = 1;void* func(void* arg) {printf("times = %d\n", times);times++;char s[1 << 20]; // 申请 1MB 内存空间(分配在栈空间上)func(NULL);return NULL;}int main() {pthread_t t;pthread_create(&t, NULL, func, NULL);pthread_join(t, NULL);return 0;}上面的程序执行结果如下图所示,可以看到当我们创建一个线程的时候栈的最大的大小也是 8MB 。
一 Pthread 并发编程——深入剖析线程基本元素和状态

文章插图
设置线程栈空间的大小现在如果我们有一个需求,需要的栈空间大于 8MB,我们应该怎么办呢?这就是我们所需要谈到的 attr,这个变量是一个 pthread_attr_t 对象 , 这个对象的主要作用就是用于设置线程的各种属性的,其中就包括线程的栈的大小 , 在下面的程序当中我们将线程的栈空间的大小设置成 24MB , 并且使用程序进行测试 。
#include <stdio.h>#include <pthread.h>#include <stdlib.h>#define MiB * 1 << 20int times = 0;void* stack_overflow(void* args) {printf("times = %d\n", ++times);char s[1 << 20]; // 1 MiBstack_overflow(NULL);return NULL;}int main() {pthread_attr_t attr;pthread_attr_init(&attr); // 对变量 attr 进行初始化操作pthread_attr_setstacksize(&attr, 24 MiB); // 设置栈帧大小为 24 MiB 这里使用了一个小的 trick 大家可以看一下 MiB 的宏定义pthread_t t;pthread_create(&t, &attr, stack_overflow, NULL);pthread_join(t, NULL);pthread_attr_destroy(&attr); // 释放线程属性的相关资源return 0;}上面的程序执行结果如下图所示:
一 Pthread 并发编程——深入剖析线程基本元素和状态

文章插图
从上面程序的执行结果来看我们设置的 24 MB 的栈空间大小起到了效果,我们可以通过线程的递归次数可以看出来我们确实申请了那么大的空间 。在上面的程序当中我们对属性的操作如下,这也是对属性操作的一般流程:
  • 使用 pthread_attr_init 对属性变量进行初始化操作 。
  • 使用各种各样的函数对属性 attr 进行操作,比如 pthread_attr_setstacksize,这个函数的作用就是用于设置线程的栈空间的大小 。
  • 使用 pthread_attr_destroy 释放线程属性相关的系统资源 。
自己为线程的栈申请空间在上一小节当中我们通过函数 pthread_attr_setstacksize 给栈空间设置了新的大?。?并且使用程序检查验证了新设置的栈空间大?。?在这一小节当中我们将介绍使用我们自己申请的内存空间也可以当作线程的栈使用 。我们将使用两种方法取验证这一点:

推荐阅读