資訊人筆記

Work hard, Have fun, Make history!

使用者工具

網站工具


course:nctu-高等unix程式設計:chapter12

*Thread Control

0x00 Outline

  • Introduction
  • Thread limitations
  • Thread attributes
  • Synchronization attributes
  • Thread-specific data
  • Cancel options
  • Threads and signals
  • Threads and fork

0x01 Introduction

#include <pthread.h>
 
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
 
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
 
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
 
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
  • 許多 thread function 中有著 attr 參數,一般我們使用 NULL 表示使用預設屬性

0x02 Thread limitations

  • PTHREAD_DESTRUCTOR_ITERATIONS: max number of times an implementation will try to destroy the thread-specific data when a thread exits
  • PTHREAD_KEYS_MAX: max number of keys that can be created by a process
  • PTHREAD_STACK_MIN: min number of bytes that can be used for a thread's stack
  • PTHREAD_THREADS_MAX: max number of threads that can be created in a process
#include <unistd.h>
 
long sysconf(int name);
  • 透過 sysconf function,給予名稱,我們可以得到限制數量

0x03 Thread attributes

  • pthread_attr_t 資料型態
#include <pthread.h>
 
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
  • 編譯時加上 -pthread 參數

常見的 thread attributes

  • detachstate: detached thread attribute
  • guardsize: guard buffer size in bytes at end of thread stack
  • stackaddr: defines the base of the thread's stack
  • stacksize: size of the stack (in bytes) that the system will allocate

detachstate

#include <pthread.h>
 
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
/* Returns zero on success, or non-zero error codes */
  • detach state
    • PTHREAD_CREATE_DETACHED
    • PTHREAD_CREATE_JOINABLE
int makethread(void *(*fn)(void *), void *arg)
{
    int err;
    pthread_t tid;
    pthread_attr_t attr;
    err = pthread_attr_init(&attr);
 
    if (err != 0)
    {
        return(err);
    }
 
    err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    if (err == 0)
    {
        err = pthread_create(&tid, &attr, fn, arg);
    }
    pthread_attr_destroy(&attr);
    return(err);

Thread stack address and size

  • 因為 shared stack 可能不夠使用,我們會希望分配記憶體給 thread stack,可以使用 malloc 或 mmap
#include <pthread.h>
 
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
int pthread_attr_getstack(pthread_attr_t *attr, void **stackaddr, size_t *stacksize);
/* Returns zero on success, or non-zero error codes */
  • stackaddr 參數是分配的記憶體中最低可定址的位置,不一定要是 stack 的開頭
#include <pthread.h>
 
int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
int pthread_attr_setstacksize(pthread_attr_t *attr , size_t stacksize);
/* Return zero on success, or non-zero error codes */

guardsize

  • 為了避免單一 thread 引起的 stack overflow,在 stack 後面通常會有個 buffer,預設大小為 PAGESIZE bytes
  • 可以透過把 buffer 大小設為 0 取消這個特性
  • 如果 thread 發生了 stack overflow,process 會收到 error
#include <pthread.h>
 
int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize);
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
/* Return zero on success, or non-zero error codes */

0x04 Synchronization Attributes

  • 在多執行緒中我們常要探討的就是同步的問題
  • 常見的同步機制包含 mutexes(互斥鎖), reader-writer locks(讀寫鎖), 和 condition variables ,他們有相似的 initialization 和 destroy functions
#include <pthread.h>
 
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
 
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);
 
int pthread_condattr_init(pthread_condattr_t *attr);
int pthread_condattr_destroy(pthread_condattr_t *attr);
/* Returns zero on success, or non-zero error codes */

Mutex 互斥鎖

#include <pthread.h>
 
int pthread_mutex_lock(pthread_mutex_t *mutex);     // 請求鎖,如果當前 mutex 已經被鎖,那這個執行緒就會卡在這,直到mutex被釋放
int pthread_mutex_unlock(pthread_mutex_t *mutex);   // 解鎖
int pthread_mutex_trylock(pthread_mutex_t *mutex);  // 嘗試請求鎖,如果當前 mutex 已經被鎖或者不可用,這個函數就直接 return error,不會把執行緒卡住

常見的 Mutex Attributes

  • Process-Shared
  • Type

Mutex Attribute: Process-Shared

  • 一般預設只有同個 process 的 thread 可以共享可以共享同個 mutex
  • 透過開啟 process-shard attribute,mutex 可用在不同 process 間
#include <pthread.h>
 
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr, int *restrict pshared);
/* Returns zero on success and store pshared in pshared parameter, or non-zero error codes */
 
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
 
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);
/* Returns zero on success, or non-zero error codes */
  • pshared 參數
    • PTHREAD_PROCESS_PRIVATE: mutex 只允許被同個 process(init mutex 的 process) 產生的 thread 操作,這是預設值
    • PTHREAD_PROCESS_SHARED: 所有可以存取 mutex 被分配之記憶體的 thread 皆可操作 mutex

Mutex Attribute: Type

  • PTHREAD_MUTEX_NORMAL: 預設值,不會做 error checking 或 deadlock detection
  • PTHREAD_MUTEX_ERRORCHECK: 提供 error checking
  • PTHREAD_MUTEX_RECURSIVE: 允許同個 thread 多次重複 lock 同個 mutex,然而也要執行同樣次數的 unlock 釋放這個 mutex
  • PTHREAD_MUTEX_DEFAULT: 依據系統關聯自動去選擇 mutex type
Mutex type Reloack without unlock? Unlock when not owned? Unlock when unlocked?
PTHREAD_MUTEX_NORMAL deadlock undefined undefined
PTHREAD_MUTEX_ERRORCHECK returns error returns error returns error
PTHREAD_MUTEX_RECURSIVE allowed returns error returns error
PTHREAD_MUTEX_DEFAULT system dependent system dependent system dependent
#include <pthread.h>
 
int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict attr, int *restrict type);
/* Returns zero on success and store type in type parameter, or non-zero error codes */
 
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
/* Returns zero on success, or non-zero error codes */

Reader-Writter Lock 讀寫鎖

  • mutex 的缺點是鎖住之後,其他執行緒完全無法介入,連讀取也無法,但有些執行緒只需要讀取並不會改變數值,此時互斥鎖雖然仍可使用,但必須等待解鎖,執行較花時間
  • 讀寫鎖在有執行緒要去讀取時加上一個讀鎖,同時允許多個讀鎖
  • 在有執行緒要去寫入時會加上一個寫鎖,寫鎖同時間只允許一個,當讀鎖為 0 時才允許寫鎖上鎖
  • 多數系統的讀鎖優先權大於寫鎖,可能導致寫鎖發生飢餓狀況,不斷有讀鎖插隊上鎖導致寫鎖無法執行
  • 讀寫鎖適合讀多寫少的情況,且要配置適當的優先權
#include <pthread.h>
 
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
 
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
 
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

0x05 Thread-Specific Data

  • 又稱 thread-private data
  • 我們希望每個執行緒能保有一些各自的資料,這些資料不必做同步處理

How To

  • Create a pthread key: This should be done only ONCE for all threads in the same process
  • Get the data associated with with key for the current thread
  • If data is not available, allocate the data and associate the data with the key
  • If data is no longer required, it can be released and de-associated

pthread key

  • A pthread key should be created only once
#include <pthread.h>
 
int pthread_key_create(pthread_key_t *keyp, void (*destructor)(void *));
int pthread_key_delete(pthread_key_t *key);
/* 若這樣寫,在 if 那邊可能會產生 race conditions */
void destructor(void *);
 
pthread_key_t key;
int init_done = 0;
 
int threadfunc(void *arg)
{
    if(!init_done)
    {
        init_done = 1;
        err = pthread_key_create(&key, destructor);
    }
... 
}
pthread_once_t initflag = PTHREAD_ONCE_INIT;
int pthread_once(pthread_once_t *initflag, void (*initfn)(void));
 
void destructor(void *);
pthread_key_t key;
pthread_once_t init_done = PTHREAD_ONCE_INIT;
 
void thread_init(void)
{
    err = pthread_key_create(&key, destructor);
}
 
int threadfunc(void *arg)
{
    pthread_once(&init_done, thread_init);
    ...
}

get, associate and de-associate data

#include <pthread.h>
 
void *pthread_getspecific(pthread_key_t key);
/* Return non-NULL for the associated value, or NULL if no value has been associated with the key */
#include <pthread.h>
 
int pthread_setspecific(pthread_key_t key, const void *value);
/* Return zero on success, or non-error error codes */
  • Use a non-NULL value to associate the data
  • Use a NULL data to de-associate the data, previously associated data should be retrieved and released first
static pthread_key_t key;
static pthread_once_t init_done = PTHREAD_ONCE_INIT;
pthread_mutex_t env_mutex = PTHREAD_MUTEX_INITIALIZER;
 
extern char **environ;
 
static void thread_init(void)
{
    pthread_key_create(&key, free);
}
 
char * getenv(const char *name)
{
    int i, len;
    char *envbuf;
 
    pthread_once(&init_done, thread_init);
    pthread_mutex_lock(&env_mutex);
    envbuf = (char *) pthread_getspecific(key);
 
    if (envbuf == NULL)
    {
        if((envbuf = malloc(ARG_MAX)) == NULL)
        {
            pthread_mutex_unlock(&env_mutex);
            return(NULL);
        }
        pthread_setspecific(key, envbuf);
    }
 
    len = strlen(name);
    for (i = 0; environ[i] != NULL; i++)
    {
        if ((strncmp(name, environ[i], len) == 0) && (environ[i][len] == '='))
        {
            strcpy(envbuf, &environ[i][len+1]);
            pthread_mutex_unlock(&env_mutex);
            return(envbuf);
        }
    }
 
    pthread_mutex_unlock(&env_mutex); return(NULL);
}

0x06 Cancel Options

0x 參考資料

course/nctu-高等unix程式設計/chapter12.txt · 上一次變更: 127.0.0.1