apue讀書筆記 - 第12章 線程控制

用戶線程與內核線程的區別?


先補充下基礎知識:


進程&線程


進程是應用程序執行的“實例”,它維護着應用程序所需的各種資源;線程是應用程序執行的“實體”(我的理解是真正幹活的部分),所以要想讓進程完成一定的工作,其必須至少包含一個線程。


然而,一直以來, linux內核並沒有線程的概念.。每一個執行實體都是一個task_struct結構(進程)。通過系統調用clone創建子進程時,可以有選擇性地讓子進程共享父進程所引用的資源。這樣的子進程通常稱爲輕量級進程。


linux上的線程就是基於輕量級進程,由用戶態的pthread庫實現的。使用pthread以後,在用戶看來,每一個task_struct就對應一個線程,一組線程以及它們所共同引用的一組資源被視爲一個整體後就是一個進程。


Linux線程庫-pthread


在Linux內核版本2.6之前,pthread使用的是一個叫linuxthreads的庫,其通過輕量級進程來實現線程,特點是:

(1).通過在首次調用pthread_create時自動創建一個管理線程,並通過管理線程實現對其他子線程的創建與管理。

(2).創建與銷燬需要一次進程間通信,一次上下文切換之後才能被管理線程執行,並且多個請求會被管理線程串行地執行。


到了linux內核版本2.6, glibc中有了一種新的pthread線程庫--NPTL(Native POSIX Threading Library)


從Linux 2.6以後,內核有了線程組的概念,task_struct結構中增加了一個tgid(thread group id)字段。如果這個task是一個"主線程",則它的tgid等於pid,否則tgid等於其所屬進程的pid,在clone系統調用中, 傳遞CLONE_THREAD參數就可以把新進程的tgid設置爲父進程的tgid(否則新進程的tgid會設爲其自身的pid);getpid(獲取進程ID)系統調用返回的是tast_struct中的tgid,這樣解釋了實例11-1在Ubuntu12.04中的執行結果(詳見:apue讀書筆記-第11章 線程)


回到問題本身,上面所述的兩種線程庫實現使用的都是內核級線程(每個線程都對應內核中的一個調度實體),這種模型稱爲1:1模型(1個線程對應1個內核級線程);這裏要引入一種新的線程庫實現NGPT(Next Generation POSIX Threads),

其打算實現M:N模型(M個線程對應N個內核級線程),也就是說若干個線程可能是在同一個執行實體上實現的。這就要求

線程庫需要在一個內核提供的執行實體上抽象出若干個執行實體,並實現它們之間的調度.。這樣被抽象出來的執行實體稱爲用戶級線程用戶級線程的切換顯然要比內核級線程的切換快一些, 前者可能只是一個簡單的長跳轉, 而後者則需要保存/裝載寄存器,,進入然後退出內核態。但是用戶級線程不能享受多處理器, 因爲多個用戶級線程對應到一個內核級線程上, 一個內核級線程在同一時刻只能運行在一個處理器上。(當前Linux內核尚未實現)


參考:Linux線程淺析


習題12.2

實現putenv_r,即putenv的可重入版本。確保即是線程安全,也是異步信號安全的

#include <pthread.h>
#include <stdlib.h> // getenv
#include <stdio.h>
#include <string.h>
#include <unistd.h> //environ
extern char **environ; 
pthread_mutex_t mutex;
static pthread_once_t init_done = PTHREAD_ONCE_INIT;
static void thread_init(void)
{
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&mutex, &attr);
    pthread_mutexattr_destroy(&attr);
}
int putenv_r(char *string)
{
    int i, len;
    char *ptr_key = NULL; 
    ptr_key = strchr(string, '='); // 指向字符串中首次出現'='的地址
    if (NULL == ptr_key)
    {
        printf("Param illegal Usage: name=value\n");
        exit(0);
    }
    len = ptr_key - string; // 地址的差即name的長度
    pthread_once(&init_done, thread_init);
    pthread_mutex_lock(&mutex);
    for (i = 0; NULL != environ[i]; i++)
    {
        if (0 == strncmp(string, environ[i], len))
        {
            environ[i] = string; // 若已存在,覆蓋
            pthread_mutex_unlock(&mutex);
            return 0;
        }
    }
    environ[i] = string;
    pthread_mutex_unlock(&mutex);
    return 0;
}
int main(int argc, char const *argv[])
{
    const char *STRING = "EDITOR=vim";
    const char *KEY = "EDITOR";
    char *ptr = NULL;
    char *value = NULL;
    ptr = malloc(sizeof(char) * strlen(STRING) + 1);
    if (NULL == ptr)
    {
        printf("malloc failed!\n");
        exit(0);
    }
    strcpy(ptr, STRING);
    putenv_r(ptr);
    value = getenv(KEY);
    if (NULL != value)
    {
        printf("Set %s to %s\n", value, KEY);
    }
    return 0;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章