分类: 系统运维
2012-03-31 23:01:32
在十一章里我们调用过pthread_create的所有例子里,我们传递一个空指针而不是一个pthread_attr_t结构体的指针。我们可 以使用pthread_attr_t结构体来修改默认行为,并把这些属性和我们创建的线程关联起来。我们使用pthread_attr_init函数来初 始化pthread_attr_t结构体。在调用pthread_attr_init后,pthread_attr_t结构体包含了实现支持的所有线程属 性的默认值。要改变单个属性,我们需要调用其它函数,本节后面就描述。
为 了反初始化一个pthread_attr_t结构体,我们调用pthread_attr_destroy。如果pthread_attr_init的一个 实现为属性对象分配了任何动态内存,那么pthread_attr_destroy会释放那个内存。此外,pthread_attr_destroy将会 用无效值初始化属性对象,所以如果它被误用,pthread_create会返回一个错误。
pthread_attr_t结构体对应用透明。这意味着应用不被支持知道任何它内部的结构,因此促进应用的移植性。根据这个模型,POSIX.1定义了独立的函数来查询和设置每个属性。
POSIX.1定义的线程属性在下表中汇总。POSIX.1定义了实现线程选项里的补充属性,但是我们不在这里讨论。在下表,我们也展示了哪些平台支持各个线程属性。如果属性通过废弃的接口可访问,我们在表项里显示ob。
名字 | 描述 | FreeBSD 5.2.1 | Linux 2.4.22 | Mac OS X 10.3 | Solaris 9 |
---|---|---|---|---|---|
detachstate | 分离的线程属性 | * | * | * | * |
guardsize | 在线程栈末尾的保卫缓冲的字节尺寸 | * | * | * | |
stackaddr | 线程栈的最低地址 | ob | * | * | ob |
stacksize | 线程栈的字节尺寸 | * | * | * | * |
在11.5节,我们介绍了分离线程的概念。如果我们不再对已有线程的终止状态感兴趣,那么我们可以使用pthread_detach来允许操作系统在线程退出时回收这个线程的资源。
如 果我们在创建这个线程的时候知道我们不需要线程的终止状态,那么我们可以安排线程以分离状态启动,通过修改pthread_attr_t结构体里的 detachstate属性。我们可以使用pthread_attr_setdetachstate函数来设置detachstate线程属性为两个合法 值中的某一个:PTHREAD_CREATE_DETACHED来以分离状态启动线程,或PTHREAD_CREATE_JOINABLE来正常启动线 程。所以它的终止状态可以被应用得到。
我 们可以调用pthread_attr_getdetachstate来得到当月的detachstate属性。被第二个参数指向的整型被设置为 PTHREAD_CREATE_DETACHED或PTHREAD_CREATE_JOINABLE,取决于给定的pthread_attr_t结构体的 这个属性的值。
下面的代码展示了一个可以用来以分离状态创建一个线程的函数:
对于线程栈属性的支持对于POSIX操作系统是可选的,但是如果系统 遵守XSI则是必需的。在编译器,你可以检查你的系统是否支持每个线程栈属性,使用_POSIX_THREAD_ATTR_STACKADDR和 _POSIX_THREAD_ATTR_STACKSIZE符号。如果某个被定义了,那么系统支持对应的线程栈属性。你也可以在运行时检查,通过使用 _SC_THREAD_ATTR_STACKADDR和_SC_THREAD_ATTR_STACKSIZE参数给sysconf函数。
POSIX.1 定义了几个操作线程栈属性的接口。两个更老的函数,pthread_attr_getstackaddr和 pthread_attr_setstackaddr,在SUS的版本3里被标记为废弃的,尽管许多pthreads实现仍提供它们。查询和修改线程栈属 性的最好方法是使用更新的函数pthread_attr_getstack和pthread_attr_setstack。这些函数去除了更老的接口定义 里表现中的歧义。
这两个函数被用来同时管理stackaddr和stacksize线程属性。
对 于一个进程,虚拟地址空间量是固定的。因为只有一个栈,它的尺寸通常不是问题。对于线程,同一个虚拟地址空间的量必须被所有线程栈共享。你可能必须减少你 默认的线程栈尺寸,如果你的应用使用很多线程,以至栈的总尺寸超过可用的虚拟地址空间。另一方面,如果你的线程调用分配巨大自动变量的函数或调用很深的栈 框架的函数,那么你要比需要比默认栈尺寸更多。
如果你为线程栈用完了虚拟进程空间,那么你调用malloc或mmap(14.9节)来为一 个代替的栈分配空间,并使用pthread_attr_setstack来改变你创建的线程的栈位置。stackaddr指定的地址是用于线程栈的最低可 寻址地址,根据处理器架构对齐到合适的边界。
stackaddr线程属性被定义为栈的最低内存地址。然而,这不必是栈的开始。如果对于给定的处理器架构栈从高位地址向低位地址增长,那么stackaddr线程属性将是栈的末尾而不是开头。
pthread_attr_getstackaddr 和pthread_attr_setstackaddr的缺陷是stackaddr参数是未详细说明的。它可能已经被解释为栈的开头或作为栈使用的内存扩 展的最低内存地址。在栈从高位内存地址向低位地址住下增长的架构上,如果stackaddr参数是栈的最低内存地址,那么你需要知道栈的尺寸来决定栈的开 头。
pthread_attr_getstack和pthread_attr_setstack函数更正了这个缺点。
一个应用也可以用pthread_attr_getstacksize和pthread_attr_setstacksize函数来得到和设置stacksize线程属性。
当你想改变默认栈大小而不想自己处理分配线程栈时,pthread_attr_setstacksize函数很有用。
guardsize 线程属性控制了线程栈末尾之后的内存扩展的尺寸,来保护栈溢出。默认情况下,这被设置为PAGESIZE字节。我们可以设置guardsize线程属性为 0来禁止这个特性:在这种情况下不会有保卫缓冲被提供。同样,如果你改变stackaddr线程属性,那么系统假定我们将管理自己的栈并禁止栈保卫缓冲, 就好像我们设置guardsize线程属性为0一样。
如果guardsize线程属性被修改,操作系统可以把它四舍五入为页尺寸的倍数的一个整型。如果线程的栈指针溢出到保卫区域,那么应用将会收到一个错误,可能是一个信号。
SUS定义了几种其它的线程属性作为实时线程选项的一部份。我们将不在这里讨论。
更多的线程属性
线程有其它不被pthread_attr_t结构体表示的属性。
1、可取消状态(12.7节);
2、可取消类型(12.7节);
3、并发等级。
并 发等级控制了内核线程或进程的数量,在它们之上用护级线程被映射。如果一个实现在内核级线程和用户级线程之间保持一对一的映射,那么改变并发等级将不会有 效果,因为所用用户级线程都可能被调度。但是,如果实现在内核进程或线程之上多元化用户级线程,那么你可能可以通过增加在给定时间可以运行的用户级线程数 量,来提升性能。pthread_setconcurrency函数可以用来提供给系统所需并发等级的提示。
pthread_getconcurrency函数返回当前并发等级。如果操作系统正控制着并发级数(也就是说,之前没有调用过pthread_setconcurrency),那么pthread_getconcurrency将返回0。
由 pthread_setconcurrency指定的并发级数只是对系统的一个提示。没有保证所请求的并发级数会被满足。你可以告诉系统你想它自己决定使 用的并发级数,通过传递一个0的级数。因此,一个应用可以撤消前一个非0值的pthread_setconcurrency调用的影响,通过0的级数再次 调用它。