首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Linux 多線程基礎:線程專屬資料與 Shell 模擬實作

     編輯:關於C語言

一、線程專屬資料(Thread-Specific Data, TSD)

POSIX 線程提供 pthread_key_t 機制,讓同一進程內不同線程擁有同名但獨立儲存空間的資料。典型應用:線程局部緩衝區、錯誤碼、資料庫連線句柄等。

核心 API

函式用途
pthread_key_create(&key, destructor)建立鍵,指定銷毀回調
pthread_setspecific(key, value)設定當前線程該鍵對應的值
pthread_getspecific(key)取得當前線程該鍵對應的值
pthread_key_delete(key)刪除鍵(不釋放值記憶體,線程結束時呼叫 destructor)

完整範例:兩線程各自維護獨立結構體

#include 
#include 
#include 
#include 

struct mydata { int x; char c[4]; };

pthread_key_t datakey;
pthread_t th_a, th_b;

void cleanup(void *ptr) { free(ptr); }

void *thread_a(void *arg) {
    struct mydata *d = malloc(sizeof(*d));
    d->x = 1; d->c[0] = 'a'; d->c[1] = '\0';
    pthread_setspecific(datakey, d);
    sleep(1);
    struct mydata *gd = pthread_getspecific(datakey);
    printf("Thread A: %d %s\n", gd->x, gd->c);
    return NULL;
}

void *thread_b(void *arg) {
    struct mydata *d = malloc(sizeof(*d));
    d->x = 2; d->c[0] = 'b'; d->c[1] = '\0';
    pthread_setspecific(datakey, d);
    sleep(1);
    struct mydata *gd = pthread_getspecific(datakey);
    printf("Thread B: %d %s\n", gd->x, gd->c);
    return NULL;
}

int main() {
    pthread_key_create(&datakey, cleanup);
    pthread_create(&th_a, NULL, thread_a, NULL);
    pthread_create(&th_b, NULL, thread_b, NULL);
    sleep(3);
    pthread_join(th_a, NULL);
    pthread_join(th_b, NULL);
    pthread_key_delete(datakey);
    return 0;
}

輸出:

Thread A: 1 a
Thread B: 2 b

雖使用同一 datakey,兩線程存取的是完全獨立的記憶體區塊。

二、模擬 Shell:fork + execlp + waitpid

利用 execlp 在子進程執行外部命令,父進程 waitpid 回收,構建極簡互動式 Shell。

關鍵函式簽名

int execlp(const char *file, const char *arg0, ..., NULL);
// file: 命令名(會搜尋 PATH)
// arg0...argn: argv[0]...argv[n],必須以 NULL 結尾

完整 Shell 實作

#include 
#include 
#include 
#include 
#include 

#define MAXLINE 100

int main() {
    char buf[MAXLINE];
    pid_t pid;
    int status;

    printf("##myhaspl~~"); // 提示符
    while (fgets(buf, MAXLINE, stdin) != NULL) {
        size_t len = strlen(buf);
        if (len > 0 && buf[len-1] == '\n') buf[len-1] = '\0'; // 去換行

        if ((pid = fork()) < 0) {
            perror("fork error");
        } else if (pid == 0) { // 子進程
            if (strcmp(buf, "Q") == 0) exit(200); // 內建退出命令
            execlp(buf, buf, (char*)0);
            perror("execlp failed"); // 若返回必失敗
            exit(127);
        }

        // 父進程等待
        if (waitpid(pid, &status, 0) < 0) perror("waitpid error");
        if (WEXITSTATUS(status) == 200) { printf("退出....\n"); break; }
        printf("##myhaspl~~");
    }
    return 0;
}

執行示例

##myhaspl~~ls
Makefile  shell.c  test.txt
##myhaspl~~pwd
/home/user/demo
##myhaspl~~nonexist
execlp failed: No such file or directory
##myhaspl~~Q
退出....

waitpid 進階用法

pid_t waitpid(pid_t pid, int *stat_loc, int options);
// pid:  >0 特定進程, -1 任一子進程, 0 同進程組
// options: WNOHANG (非阻塞), WUNTRACED (報告停止狀態)

// 宏檢查退出狀態
WIFEXITED(status)    // 正常退出
WEXITSTATUS(status)  // 取得 exit code
WIFSIGNALED(status)  // 訊號終止
WTERMSIG(status)     // 終止訊號編號

總結

  • TSD 是線程隔離資料的標準方案,配合 destructor 自動回收記憶體。
  • Shell 核心迴路:讀入 → fork → 子進程 execlp → 父進程 waitpid,掌握此模式即可擴展內建命令、管道、重導向等功能。
  • execlp 失敗時務必 exit,避免子進程繼續執行父進程邏輯。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved