一直以來建立線程的時候,都将線程屬性參數 pthread_attr_t 設定為 NULL. 通常來說這都能滿足需求。本文将簡要介紹 pthread_attr_t 中包含的屬性。
1. pthread_attr_t
pthread_create 函數其中一個參數為此類型,表示将建立指定類型的線程,通常,該類型是一個結構體,包含下面這些屬性:
- Detach state: 線程的分離屬性
- Scope: 線程競争域
- Inherit scheduler: 排程繼承性
- Scheduling policy: 排程政策
- Scheduling priority: 線程優先級
- Guard size: 警界緩沖區大小,預設是一個 PAGE_SIZE(4096KB)
- Stack address: 棧起始位址(棧最低記憶體位址)
- Stack size: 棧大小
我隻是把這些東西列出來而已,是以各位同學請不要拍磚,我知道大多數屬性你是看不懂的。
實際上,這裡大多數屬性你最好不要去更改,我們能夠使用的,一般就是線程的分離屬性,對于其它的東西,隻有在你了解了線程運作機制以及排程政策之後才能夠看的懂。
不過,下面還是簡單介紹一下不同屬性要幹什麼事吧。
2. 不同屬性的作用
2.1 Detach state
這個屬性是線程的分離屬性,它表示線程結束的時候,是否回收資源(比如它所使用的棧、線程結構體、線程過程函數的傳回值等等)。
分離屬性有兩種情況:
-
: 這種情況是預設的,表示結束的時候不回收資源,需要調用 pthread_join 函數顯式回收,這種情況我們早已經學會了。PTHREAD_CREATE_JOINABLE
-
: 表示線程結束了,直接釋放所占用的資源,這種情況下不可再使用 pthread_join 函數去 wait 它了。PTHREAD_CREATE_DETACHED
2.2 Scope
線程競争域,該屬性有兩種情況:
-
:表示線程和整個作業系統中的線程進行競争,比如 CPU (在排程的時候,考慮系統中所有的線程).PTHREAD_SCOPE_SYSTEM
-
: 表示線程隻和目前程序内的線程進行競争,比如線程排程的時候,隻考慮目前程序中的線程。PTHREAD_SCOPE_PROCESS
在 Linux 中,隻支援
PTHREAD_SCOPE_SYSTEM
,因為 Linux 中實作的線程,實際上就是輕量級程序。
用于設定和擷取 scope 值的函數如下:
int pthread_attr_setscope(pthread_attr_t *attr,int scope);
int pthread_attr_getscope(const pthread_attr_t *attr,int *scope);
2.3 Inherit scheduler
排程繼承性,該屬性有兩種情況:
-
: 繼承程序的排程政策PTHREAD_INHERIT_SCHED
-
: 不使用繼承的排程政策,而是使用自己提供的排程政策。PTHREAD_EXPLICIT_SCHED
用于設定和擷取 inherit scheduler 值的函數如下:
int pthread_attr_getinheritsched(const pthread_attr_t *attr,int *inheritsched);
int pthread_attr_setinheritsched(pthread_attr_t *attr,int
2.4 Scheduling policy
排程政策,該屬性有以下幾個值:
-
SCHED_OTHER
-
SCHED_BATCH
-
SCHED_IDLE
-
SCHED_FIFO
-
SCHED_RR
這些排程政策可分成兩大類:
- 普通排程政策(或者叫非實時排程政策),主要包括
、SCHED_OTHER
和SCHED_BATCH
這三種。SCHED_IDLE
- 實時排程政策,主要包括
和SCHED_FIFO
。SCHED_RR
關于這些排程政策的含義,本文不詳細讨論。
用于設定和擷取 policy 值的函數如下:
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int
2.5 Scheduling priority
排程優先級,通常該值通過下面的函數設定和擷取。
pthread_attr_setschedparm(pthread_attr_t *attr,
const struct sched_param *param)
pthread_attr_getschedparm(pthread_attr_t *attr,
struct sched_param *param)
struct sched_param {
...
int sched_priority;
...
對于普通排程政策來說,該值隻能是 0. 對于動态優先級來說,該值的範圍是 [1,99],但是 POSIX 規定,該範圍需要通過 sched_get_priority_min 和 sched_get_priority_max 函數來擷取。在具體實作中,最小是 32.
2.6 Guard size
警界緩沖區大小,預設是一個 PAGE_SIZE(4096KB). 用來設定和得到線程棧末尾的警戒緩沖區大小。如果線程棧運作到了警界區,就會收到信号。
可以使用下面兩個函數來對其進行設定和擷取:
int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize);
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
2.7 Stack address and Stack size
Stack address 表示棧起始位址(棧最低記憶體位址),一般來說在我們的 x86 或 x64 處理器上,棧都是往低位址的方向生長,是以該值一般表示棧的末尾(棧頂)。
Stack size 表示棧的大小。
可以使用下面兩個函數來對其進行設定和擷取:
int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize);
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
對于棧的大小,還可以使用下面的函數:
int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
3. 實驗
程式 threadattr 可以用來檢視線程屬性。該程式可以通過指令行傳入一個參數,表示棧大小。另外,帶參數的情況下,還會修改線程很多預設屬性。
3.1 程式清單
// threadattr.c
#define _GNU_SOURCE /* To get pthread_getattr_np() declaration */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
static void display_pthread_attr(pthread_attr_t *attr, char *prefix)
{
int s, i;
size_t v;
void *stkaddr;
struct sched_param sp;
s = pthread_attr_getdetachstate(attr, &i);
if (s != 0)
handle_error_en(s, "pthread_attr_getdetachstate");
printf("%sDetach state = %s\n", prefix,
(i == PTHREAD_CREATE_DETACHED) ? "PTHREAD_CREATE_DETACHED" :
(i == PTHREAD_CREATE_JOINABLE) ? "PTHREAD_CREATE_JOINABLE" :
"???");
s = pthread_attr_getscope(attr, &i);
if (s != 0)
handle_error_en(s, "pthread_attr_getscope");
printf("%sScope = %s\n", prefix,
(i == PTHREAD_SCOPE_SYSTEM) ? "PTHREAD_SCOPE_SYSTEM" :
(i == PTHREAD_SCOPE_PROCESS) ? "PTHREAD_SCOPE_PROCESS" :
"???");
s = pthread_attr_getinheritsched(attr, &i);
if (s != 0)
handle_error_en(s, "pthread_attr_getinheritsched");
printf("%sInherit scheduler = %s\n", prefix,
(i == PTHREAD_INHERIT_SCHED) ? "PTHREAD_INHERIT_SCHED" :
(i == PTHREAD_EXPLICIT_SCHED) ? "PTHREAD_EXPLICIT_SCHED" :
"???");
s = pthread_attr_getschedpolicy(attr, &i);
if (s != 0)
handle_error_en(s, "pthread_attr_getschedpolicy");
printf("%sScheduling policy = %s\n", prefix,
(i == SCHED_OTHER) ? "SCHED_OTHER" :
(i == SCHED_FIFO) ? "SCHED_FIFO" :
(i == SCHED_RR) ? "SCHED_RR" :
"???");
s = pthread_attr_getschedparam(attr, &sp);
if (s != 0)
handle_error_en(s, "pthread_attr_getschedparam");
printf("%sScheduling priority = %d\n", prefix, sp.sched_priority);
s = pthread_attr_getguardsize(attr, &v);
if (s != 0)
handle_error_en(s, "pthread_attr_getguardsize");
printf("%sGuard size = %d, prefix, v);
s = pthread_attr_getstack(attr, &stkaddr, &v);
if (s != 0)
handle_error_en(s, "pthread_attr_getstack");
printf("%sStack address = %p\n", prefix, stkaddr);
printf("%sStack size = 0x%x, prefix, v);
}
static void * thread_start(void *arg)
{
int s;
pthread_attr_t gattr;
/* pthread_getattr_np() is a non-standard GNU extension that
* retrieves the attributes of the thread specified in its
* first argument */
s = pthread_getattr_np(pthread_self(), &gattr);
if (s != 0)
handle_error_en(s, "pthread_getattr_np");
printf("Thread attributes:\n");
display_pthread_attr(&gattr, "\t");
exit(EXIT_SUCCESS); /* Terminate all threads */
}
int main(int argc, char *argv[])
{
pthread_t thr;
pthread_attr_t attr;
pthread_attr_t *attrp; /* NULL or &attr */
int s;
attrp = NULL;
/* If a command-line argument was supplied, use it to set the
* stack-size attribute and set a few other thread attributes,
* and set attrp pointing to thread attributes object */
if (argc > 1) {
int stack_size;
void *sp;
attrp = &attr;
s = pthread_attr_init(&attr);
if (s != 0)
handle_error_en(s, "pthread_attr_init");
s = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (s != 0)
handle_error_en(s, "pthread_attr_setdetachstate");
s = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
if (s != 0)
handle_error_en(s, "pthread_attr_setinheritsched");
stack_size = strtoul(argv[1], NULL, 0);
s = posix_memalign(&sp, sysconf(_SC_PAGESIZE), stack_size);
if (s != 0)
handle_error_en(s, "posix_memalign");
printf("posix_memalign() allocated at %p\n", sp);
s = pthread_attr_setstack(&attr, sp, stack_size);
if (s != 0)
handle_error_en(s, "pthread_attr_setstack");
}
s = pthread_create(&thr, attrp, &thread_start, NULL);
if (s != 0)
handle_error_en(s, "pthread_create");
if (attrp != NULL) {
s = pthread_attr_destroy(attrp);
if (s != 0)
handle_error_en(s, "pthread_attr_destroy");
}
pause(); /* Terminates when other thread calls exit() */
3.2 編譯和運作
- 編譯
$ gcc threadattr.c -o threadattr -lphtread
- 不帶參數運作
$ ./threadattr
圖1 不帶參數運作
- 帶參數運作
$ ./threadattr 0x3000000
4. 總結
- 知道線程屬性包含哪些内容
- 了解分離屬性的作用