6.7.2 管理线程的栈
管理线程的栈包括设置栈的大小以及确定栈的位置。线程的栈通常是由系统自动管理的。但是您应当清楚由默认的栈管理系统导致的系统特定限制。它们可能过于受限,这时就有必要进行一些栈管理了。如果应用程序有着大量的线程,则您可能不得不增加具有默认大小的栈的上限。如果应用程序利用递归或调用多个函数,则会需要很多栈帧。有些应用程序要求对地址空间进行精确的控制。例如,有着垃圾收集的应用程序必须跟踪内存的分配。
进程的地址空间分成代码段、静态数据段、堆和栈段。线程栈的位置和大小是从它所属的进程的栈中切分出来的。线程栈为线程调用且尚未退出的每个例程保存一个栈帧。栈帧包含临时变量、局部变量、返回地址以及线程回到之前执行的例程所需要的任何附加信息。一旦例程退出,该例程的栈帧会从栈中删除。图6-5显示了栈帧如何生成以及如何放置到栈中。
|
| (点击查看大图)图6-5 |
在图6-5中,ThreadA执行task1( )。task1()创建一些局部变量,进行一些处理,然后调用task2( )。会为task1( )创建一个栈帧并放置到栈中。task2( )创建局部变量,然后调用task3( )。task2( )的栈帧也被放置到栈中。当task3( )结束之后,控制流返回到task2( ),它从栈中弹出。当task2( )执行完之后,控制流返回到task1( ),它也从栈中弹出。每个栈必须足够大,以容纳所有对等线程的函数的执行以及它们将会调用的例程链。线程栈的大小和位置可以通过由属性对象定义的几种方法进行设置或检测。
1. 设置栈的大小
有两个属性方法同线程栈的大小有关。
调用形式
- #include <pthread.h>
-
- int pthread_attr_getstacksize(const pthread_attr_t *restrict attr,
- size_t *restrict stacksize);
- int pthread_attr_setstacksize(pthread_attr_t *attr, size_t *stacksize);
pthread_attr_getstacksize( )返回默认栈大小的最小值。attr是从中提取默认栈大小的线程属性对象。当函数返回时,默认栈大小保存在stacksize中(以字节为单位),且返回值为0。如果没有成功,则函数返回错误号。
pthread_attr_setstacksize( )设置栈大小的最小值。attr是设置栈大小的线程属性对象。stacksize是栈大小的最小值(以字节为单位)。如果函数成功,返回值为0。如果没有成功,函数返回一个错误号。如果stacksize小于PTHREAD_MIN_STACK或小于系统最小值,则函数会失败。PTHREAD_STACK_MIN很可能会是一个比由pthread_attr_getstacksize( )返回的默认栈最小值还要小的最小值。在增加线程栈的最小大小之前,需要考虑由pthread_attr_getstacksize( )返回的值。
在示例6-9中,线程的栈大小通过使用线程属性对象进行了更改。它从属性对象中提取默认值,然后判断默认值是否小于期望的最小栈大小。如果是,则将偏移量加到默认栈大小上,得到的结果成为线程新的最小栈大小。
示例6-9
- // Example 6-9 Changing the stack size of a thread using an offset.
-
- #include <limits.h>
- //...
-
- pthread_attr_getstacksize(&SchedAttr,&DefaultSize);
- if(DefaultSize < PTHREAD_STACK_MIN){
- SizeOffset = PTHREAD_STACK_MIN - DefaultSize;
- NewSize = DefaultSize + SizeOffset;
- pthread_attr_setstacksize(&Attr1,(size_t)NewSize);
- }
在设置大小时需要进行权衡。栈大小是固定的,较大的栈意味着发生栈溢出的可能性较小,但是另一方面,较大的栈意味着在栈的交换空间和实际内存方面占用得更多。
注意:
设置栈大小和栈的位置可能使得您的程序无法移植。您在一个平台上为程序设置的栈大小和位置可能无法匹配上另一个平台上的栈大小和位置。
2. 设置线程栈的位置
一旦您决定管理线程的栈,您可以通过使用这些属性对象方法来提取并设置栈的位置。
调用形式
- #include <pthread.h>
-
- int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr);
- int pthread_attr_getstackaddr(const pthread_attr_t *restrict attr,
- void **restrict stackaddr);
pthread_attr_setstackaddr( )将栈的基地址设置为stackattr指定的地址,该栈用于使用attr线程属性对象创建的线程。地址addr应当位于进程的虚拟地址空间中。栈的大小最少应当等于由PTHREAD_STACK_MIN所指定的最小栈大小。如果成功,函数返回0。如果不成功,函数返回一个错误号。
pthread_attr_getstackaddr( )获取线程的栈地址的基地址,该线程是通过使用由attr指定的线程属性对象创建的。地址返回并保存在stackaddr中。如果成功,则函数返回0。如果不成功,函数返回一个错误号。
3. 设置一个函数设置栈大小和位置
栈属性(大小和位置)可以通过使用一个函数来设置。
调用形式
- #include <pthread.h>
-
- int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr,
- size_t stacksize);
- int pthread_attr_getstack(const pthread_attr_t *restrict attr,
- void **restrict stackaddr,
size_t *restrict stacksize);
函数pthread_attr_setstack( )为使用指定的属性对象attr创建的线程设置栈大小和位置。栈的基地址设置为stackaddr,栈的大小设置为stacksize。pthread_attr_getstack( )提取使用指定属性对象attr创建的线程的栈大小和位置。如果提取成功,则栈的位置保存在stackaddr,栈的大小保存在stacksize。如果成功,这些函数会返回0。如果不成功,则返回错误号。如果stacksize小于PTHREAD_STACK_MIN或超出一些根据实现定义的限制,则pthread_attr_setstack( )会失败。