UNIX环境高级编程——线程属性

时间:2022-11-02 21:56:21
pthread_attr_t 的缺省属性值 

属性 结果
scope PTHREAD_SCOPE_PROCESS 新线程与进程中的其他线程发生竞争。
detachstate PTHREAD_CREATE_JOINABLE 线程退出后,保留完成状态和线程 ID
stackaddr NULL 新线程具有系统分配的栈地址。
stacksize 1M 新线程具有系统定义的栈大小。
priority 0 新线程的优先级为0。
inheritsched PTHREAD_EXPLICIT_SCHED 新线程不继承父线程调度优先级。
schedpolicy SCHED_OTHER 新线程对同步对象争用使用 Solaris 定义的固定优先级。线程将一直运行,直到被抢占或者直到线程阻塞或停止为止。
guardsize PAGESIZE(4M) 新线程具有系统定义的栈溢出保护区大小
1、初始化一个线程对象的属性
int pthread_attr_init(pthread_attr_t *attr);
返回值:若是成功返回0,否则返回错误的编号
形 参: attr 指向一个线程属性的指针
说 明:Posix线程中的线程属性pthread_attr_t主要包括scope属性、detach属性、堆栈地址、堆栈大小、优先级。
pthread_attr_init实现时为属性对象分配了动态内存空间。
线程属性结构如下:
typedef struct
{
int detachstate; 线程的分离状态
int schedpolicy; 线程调度策略
struct sched_param schedparam; 线程的调度参数
int inheritsched; 线程的继承性
int scope; 线程的作用域
size_t guardsize; 线程栈末尾的警戒缓冲区大小
int stackaddr_set;
void* stackaddr; 线程栈的位置
size_t stacksize; 线程栈的大小
}pthread_attr_t;

2、销毁一个线程属性对象
int pthread_attr_destroy(pthread_attr_t *attr);
返回值:若是成功返回0,否则返回错误的编号
形 参:attr 指向一个线程属性的指针
说 明:如果pthread_attr_init实现时为属性对象分配了动态内存空间,pthread_attr_destroy将会释放该内存空间。
经pthread_attr_destroy去除初始化之后的pthread_attr_t结构被pthread_create函数调用,将会导致其返回错误。

3、获取线程分离状态属性.
int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);
返回值:若是成功返回0,否则返回错误的编号
形 参: attr 指向一个线程属性的指针
detachstate 保存返回的分离状态属性,有两个取值:
  • PTHREAD_CREATE_DETACHED(分离)
  • PTHREAD_CREATE_JOINABLE(非分离)
说 明:获取线程分离状态属性

4、修改线程分离状态属性
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
     如果对现有的某个线程的终止状态不感兴趣的话,可以使用pthread_detach函数让操作系统在线程退出时收回它所占有的资源。如果在创建线程之前就知道不需要了解线程的终止状态,则可以修改pthread_attr_t结构中的detachstate线程属性,让线程以分离状态启动。其中pthread_detach函数时在线程创建之后设置分离属性的。
返回值:若是成功返回0,否则返回错误的编号
形 参:attr 指向一个线程属性的指针
detachstat 有两个取值:

  • PTHREAD_CREATE_DETACHED(分离)
  • PTHREAD_CREATE_JOINABLE(非分离)
说 明:所有的Pthreads系统都支持detachstate属性,该属性的值可以是PTHREAD_CREATE_DETACHED(分离)或PTHREAD_CREATE_JOINABLE(非分离)。默认的,线程被创建为非分离,即意味着由pthread_create创建的该线程ID能被用来与线程连接并获得它的返回值,或取消它。

5、获取线程的堆栈信息(栈地址和栈大小)
int pthread_attr_getstack(pthread_attr_t *attr, void **stackaddr, size_t *stacksize);
返回值:若是成功返回0,否则返回错误的编号
形 参:attr 指向一个线程属性的指针
stackaddr 返回获取的栈地址
stacksize 返回获取的栈大小
说 明:获取线程的堆栈地址和大小


6、设置线程的堆栈信息(栈地址和栈大小)
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
返回值:若是成功返回0,否则返回错误的编号
形 参:attr 指向一个线程属性的指针
stackaddr 线程的堆栈地址(低位地址):应该是可移植的,对齐页边距的可以用posix_memalign来进行获取。如果将 stackaddr 设置为非空值,而不是缺省的 NULL,则系统将在该地址初始化栈,假设大小为 stacksize
stacksize 线程的堆栈大小:应该是页大小的整数倍
说 明:设置堆栈区,将导致pthread_attr_setguardsize失效。

试用场合:对进程来说,虚拟地址空间的大小是固定的,进程中只有一个栈,所以它的大小通常不是问题。但对线程来说,同样大小的虚拟地址空间必须被所有的线程栈共享。如果应用程序使用了太多的线程,致使线程栈的累计大小超过了可用的虚拟地址空间,这时就需要减少线程默认的栈大小。另一方面,如果线程调用的函数分配了大量的自动变量或者调用的函数涉及很深的栈帧,那么这时需要的栈的大小可能要比默认的大。

注意:stackaddr  线程属性被定义为栈的内存单元的最低地址,但这并不是必然是栈的开始位置。对于某些处理器结构来说,栈是从高地址向低地址方向伸展的,那么stackaddr 线程属性就是栈的结尾而不是开始位置。
7、获取线程堆栈地址
int pthread_attr_getstackaddr(pthread_attr_t *attr, void **stackaddr);
返回值:若是成功返回0,否则返回错误的编号
形 参:attr 指向一个线程属性的指针
stackaddr 返回获取的栈地址
说 明:函数已过时,一般用pthread_attr_getstack来代替


8、设置线程堆栈地址
int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr);
返回值:若是成功返回0,否则返回错误的编号
形 参:attr 指向一个线程属性的指针
stackaddr 设置线程堆栈地址
说 明:函数已过时,一般用pthread_attr_setstack来代替。


9、获取线程堆栈大小
int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);
返回值:若是成功返回0,否则返回错误的编号
形 参:attr 指向一个线程属性的指针
stacksize 返回线程的堆栈大小
说 明:获取线程堆栈大小。设置堆栈大小不是可移植的。Pthreads定义PTHREAD_STACK_MIN标志,指定每个线程要求的最小栈大小,包含#include <limits.h>后可以通过打印其值查看。命令:#ulimit -s或者#ulimit -a其中stack size项表示堆栈大小。#ulimit -s value用来重新设置stack大小。默认为8M,最小16384字节
注意:通常,线程栈是从页边界开始的。任何指定的大小都被向上舍入到下一个页边界。不具备访问权限的页将被附加到栈的溢出端。大多数栈溢出都会导致将 SIGSEGV 信号发送到违例线程(注意可以参考《UNIX环境高级编程——存储映射I/O(mmap函数)》)。将直接使用调用方分配的线程栈,而不进行修改。指定栈时,还应用 PTHREAD_CREATE_JOINABLE 创建线程。在该线程的 pthread_join调用返回之前,不会释放该栈。在该线程终止之前,不会释放该线程的栈。一般情况下,不需要为线程分配栈空间。系统会为每个线程的栈分配 1 MB(对于 32 位系统)或 2 MB(对于 64 位系统)的虚拟内存,而不保留任何交换空间。系统将使用 mmap() 的 MAP_NORESERVE 选项来进行分配。极少数情况下需要指定栈和/或栈大小。甚至专家也很难了解是否指定了正确的大小。

10、设置线程堆栈大小
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
返回值:若是成功返回0,否则返回错误的编号
形 参:attr 指向一个线程属性的指针
stacksize 线程的堆栈大小(以字节为单位):应该是页大小的整数倍。(以字节为单位)。stacksize不应小于系统定义的最小栈大小。stacksize包含新线程使用的栈的字节数。如果 stacksize为零,则使用缺省大小。在大多数情况下,零值最适合。

11、获取线程的栈溢出保护区大小
int pthread_attr_getguardsize(pthread_attr_t *attr, size_t *guardsize);
返回值:若是成功返回0,否则返回错误的编号
形 参: attr 指向一个线程属性的指针
guardsize 返回获取的栈保护区大小
说 明:获取线程的栈保护区大小。 出于以下两个原因,为应用程序提供了 guardsize 属性:
  • 溢出保护可能会导致系统资源浪费。如果应用程序创建大量线程,并且已知这些线程永远不会溢出其栈,则可以关闭溢出保护区。通过关闭溢出保护区,可以节省系统资源。
  • 线程在栈上分配大型数据结构时,可能需要较大的溢出保护区来检测栈溢出。

guardsize 参数提供了对栈指针溢出的保护。如果创建线程的栈时使用了保护功能,则实现会在栈的溢出端分配额外内存。此额外内存的作用与缓冲区一样,可以防止栈指针的栈溢出。如果应用程序溢出到此缓冲区中,这个错误可能会导致 SIGSEGV 信号被发送给该线程。

12、设置线程的栈溢出保护区大小
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
返回值:若是成功返回0,否则返回错误的编号
形 参:attr 指向一个线程属性的指针
guardsize 线程的栈保护区大小
说 明:参数提供了对栈指针溢出的保护。

  • 如果 guardsize 为零,则不会为使用 attr 创建的线程提供溢出保护区。
  • 如果 guardsize 大于零,则会为每个使用attr 创建的线程提供大小至少为 guardsize 字节的溢出保护区。缺省情况下,线程具有实现定义的非零溢出保护区。将 guardsize 的值向上舍入为可配置的系统变量 PAGESIZE 的倍数。请参见 sys/mman.h 中的PAGESIZE(4096字节)

注意:线程属性guardsize 控制着线程栈末尾之后用以避免溢出的扩展内存的大小这个属性默认设置为PAGESIZE个字节。可以把guardsize线程属性设为0,从而不允许属性的这种属性的行为发生:在这种情况下不会提供警戒缓冲区。同样地,如果对线程属性stackaddr(pthread_attr_setstack/pthread_attr_setstackaddr 函数)作了修改,系统就会假设我们会自己管理栈,并使警戒缓冲机制无效,等同于把guardsize线程属性设为0.

本文来源于以前写的博客:《posix多线程有感--线程高级编程(线程属性函数总结)》,并做了一些修订。但是不够全面,没有写出全部的属性。