Intermediate
pthread_attr_t
TLPI – Ch 29.8
Every time you call pthread_create() with NULL as the second argument you are accepting a set of default thread behaviours. For most programs that is perfectly fine. But sometimes you need more control — a larger stack, a specific scheduling priority, or a thread that starts in the detached state. The pthread_attr_t object is the mechanism for controlling all of these at creation time.
1. What Is pthread_attr_t?
pthread_attr_t is an opaque object (like pthread_t) that bundles together all the configurable properties of a thread before it is created. You cannot inspect its internals directly — you use dedicated API functions to set and get each attribute.
The typical workflow is:
pthread_attr_init()
Set attributes
pthread_create()
pthread_attr_destroy()
Step ④ is important even though pthread_attr_destroy() may be a no-op on some implementations. On others it frees memory. Always call it for correctness and portability. The attribute object can be destroyed immediately after pthread_create() — modifying it afterwards has no effect on already-created threads.
2. Common Thread Attributes
| Attribute | Setter Function | Default Value | Description |
|---|---|---|---|
| Detach state | pthread_attr_setdetachstate() | PTHREAD_CREATE_JOINABLE | Whether thread is joinable or auto-cleans up |
| Stack size | pthread_attr_setstacksize() | System default (often 8 MB) | Size of the thread’s private stack |
| Stack address | pthread_attr_setstackaddr() | System-chosen | Override where the stack lives in memory |
| Guard size | pthread_attr_setguardsize() | System page size | Guard page to detect stack overflow |
| Scheduling policy | pthread_attr_setschedpolicy() | SCHED_OTHER | SCHED_FIFO, SCHED_RR, or SCHED_OTHER |
| Scheduling priority | pthread_attr_setschedparam() | Inherited from creator | Realtime priority level (for SCHED_FIFO/RR) |
| Scope | pthread_attr_setscope() | PTHREAD_SCOPE_SYSTEM | System-scope (competes with all processes) or process-scope |
3. Stack Size — When and Why to Change It
The default stack size on Linux is typically 8 MB per thread. For most threads this is more than enough. However, there are two scenarios where you would want to change it:
If you are creating many hundreds or thousands of threads, each holding 8 MB of virtual address space can exhaust the process’s address space (3 GB on 32-bit; much larger on 64-bit). Reducing the stack to e.g. 64 KB per thread lets you run far more concurrent threads. Embedded systems also often have tight memory constraints.
If a thread uses deep recursion or declares very large local arrays/structs on the stack, it might overflow the default 8 MB stack. Increasing the stack size prevents the stack overflow (which usually results in a SIGSEGV signal).
PTHREAD_STACK_MIN (from <limits.h>) which is the minimum allowed stack size. Never set the stack smaller than this.4. Scheduling Attributes — Realtime Threads
By default all threads use the SCHED_OTHER (normal time-sharing) policy. For realtime or high-priority work you can set SCHED_FIFO (first-in first-out) or SCHED_RR (round-robin).
Setting realtime scheduling policies on threads requires elevated privileges. Attempting it as a normal user will fail with EPERM. These are covered in detail in Sections 35.2–35.3 of TLPI.
5. Code Example: Custom Stack Size
This example creates a thread with a small stack (256 KB instead of the default 8 MB). Useful when spawning many threads in a memory-constrained environment.
/* compile: gcc -pthread -o custom_stack custom_stack.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h> /* PTHREAD_STACK_MIN */
#include <pthread.h>
#define STACK_SIZE (256 * 1024) /* 256 KB */
static void *worker(void *arg)
{
printf("Thread running with custom stack size\n");
/* Demonstrate: allocating a local array of 64 KB — fits in 256 KB stack */
char buf[64 * 1024];
buf[0] = 'A';
buf[(64*1024) - 1] = 'Z';
printf("Local buffer: first=%c last=%c\n", buf[0], buf[(64*1024)-1]);
return NULL;
}
int main(void)
{
pthread_t tid;
pthread_attr_t attr;
int s;
/* Verify our desired size is at least the minimum */
if (STACK_SIZE < PTHREAD_STACK_MIN) {
fprintf(stderr, "Stack size too small! Minimum is %d bytes.\n",
(int)PTHREAD_STACK_MIN);
exit(1);
}
s = pthread_attr_init(&attr);
if (s != 0) { fprintf(stderr, "attr_init: %s\n", strerror(s)); exit(1); }
/* Set custom stack size */
s = pthread_attr_setstacksize(&attr, STACK_SIZE);
if (s != 0) { fprintf(stderr, "setstacksize: %s\n", strerror(s)); exit(1); }
/* Verify the size was set correctly */
size_t actual_size;
pthread_attr_getstacksize(&attr, &actual_size);
printf("Requested stack: %d bytes, actual: %zu bytes\n",
STACK_SIZE, actual_size);
s = pthread_create(&tid, &attr, worker, NULL);
if (s != 0) { fprintf(stderr, "create: %s\n", strerror(s)); exit(1); }
pthread_attr_destroy(&attr); /* safe to destroy after create */
pthread_join(tid, NULL);
printf("Done.\n");
return 0;
}
6. Code Example: Combining Multiple Attributes
You can set several attributes on a single pthread_attr_t object before calling pthread_create(). This example sets both a custom stack size and the detached state.
/* compile: gcc -pthread -o multi_attr multi_attr.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <pthread.h>
#define STACK_SIZE (512 * 1024) /* 512 KB */
static void *background_task(void *arg)
{
int task_id = (int)(intptr_t) arg;
printf("BG Task %d: running (detached, 512KB stack)\n", task_id);
/* simulate some work */
for (volatile int i = 0; i < 100000000; i++);
printf("BG Task %d: complete\n", task_id);
return NULL;
}
int main(void)
{
pthread_t tids[3];
pthread_attr_t attr;
int s;
s = pthread_attr_init(&attr);
if (s) { fprintf(stderr, "attr_init: %s\n", strerror(s)); exit(1); }
/* Attribute 1: detached — no join needed */
s = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (s) { fprintf(stderr, "setdetachstate: %s\n", strerror(s)); exit(1); }
/* Attribute 2: custom stack size */
s = pthread_attr_setstacksize(&attr, STACK_SIZE);
if (s) { fprintf(stderr, "setstacksize: %s\n", strerror(s)); exit(1); }
/* Create 3 threads that all share the same attribute set */
for (int i = 0; i < 3; i++) {
s = pthread_create(&tids[i], &attr, background_task,
(void *)(intptr_t)(i + 1));
if (s) { fprintf(stderr, "create %d: %s\n", i, strerror(s)); exit(1); }
printf("Main: launched background task %d\n", i + 1);
}
/* Attribute object no longer needed after all creates */
pthread_attr_destroy(&attr);
printf("Main: all tasks launched. Exiting via pthread_exit.\n");
pthread_exit(NULL); /* let detached threads finish */
return 0;
}
All three threads share the same attribute configuration (detached + 512 KB stack). The attribute object is destroyed after all three pthread_create() calls — this is safe because the attributes are copied into each thread at creation.
7. Reading Attribute Values (Getters)
Every pthread_attr_set*() function has a corresponding pthread_attr_get*() function. These are useful for verifying that attributes were set correctly, or for reading the default values before modifying them.
pthread_attr_t attr;
pthread_attr_init(&attr);
/* Read detach state */
int detach_state;
pthread_attr_getdetachstate(&attr, &detach_state);
printf("Detach state: %s\n",
detach_state == PTHREAD_CREATE_JOINABLE ? "JOINABLE" : "DETACHED");
/* Read stack size */
size_t stack_size;
pthread_attr_getstacksize(&attr, &stack_size);
printf("Default stack size: %zu bytes (%zu MB)\n",
stack_size, stack_size / (1024 * 1024));
pthread_attr_destroy(&attr);
8. Interview Questions
pthread_attr_t is an opaque object that bundles thread creation attributes such as stack size, stack address, detach state, scheduling policy, and priority. It is initialised with pthread_attr_init(), configured with setter functions, passed to pthread_create(), and then destroyed with pthread_attr_destroy().pthread_attr_t object after pthread_create() has no effect on the already created thread.PTHREAD_STACK_MIN (defined in <limits.h>) is the minimum stack size allowed by the POSIX standard for a thread. Attempting to set a stack size smaller than this value will fail with EINVAL. Always check your desired size against this minimum.pthread_attr_t object to multiple pthread_create() calls. Each thread gets its own independent copy of the attribute values. Destroy the object only after the last pthread_create() call.SIGSEGV (segmentation fault) because the thread writes beyond its stack into a guard page or unmapped memory. This usually crashes the entire process. Prevention: either increase the thread’s stack size via pthread_attr_setstacksize(), or reduce recursive depth / large local allocations. Use heap (malloc()) for very large data instead of local arrays.When should you choose threads over processes? The advantages, disadvantages, and a full chapter summary with exercises.
