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

Siege 源碼深度剖析:C 語言多線程編程的教科書級實戰

     編輯:關於C語言

項目概覽

  • 定位:純 C 實現的 HTTP/HTTPS/FTP 壓測工具,GNU/Linux 為主,高可移植性。
  • 核心價值:多線程編程百科全書——互斥鎖、條件變量、線程池、信號處理、線程取消、清理函數、同步機制一應俱全。
  • 代碼規模:~13,000 行,模塊化極強(Boolean、Array、Work 隊列等自定義類型)。
  • 作者:Jeff Fulmer (joedog.org),1999 年起維護至今。

架構設計:三類線程協作模型

主線程
├─ 創建 n 個「壓測線程」 (worker threads)
├─ 創建 1 個「計時線程」 (timer thread)
└─ 創建 1 個「控制線程」 (controller thread)

共享數據結構:CREW (線程池管理) + CLIENT[n] (每線程統計)

關鍵數據結構

/* CREW: 線程池與任務隊列管理 */
struct CREW_T {
    int size, maxsize, cursize, total;
    WORK *head, *tail;              // 任務鏈表
    BOOLEAN block, closed, shutdown;
    pthread_t *threads;             // 線程 ID 數組
    pthread_mutex_t lock;           // 保護 CREW 所有字段
    pthread_cond_t not_empty;       // cursize > 0
    pthread_cond_t not_full;        // cursize < maxsize
    pthread_cond_t empty;           // cursize == 0
};

/* CLIENT: 單線程運行時統計 */
typedef struct {
    int id;
    unsigned long hits, bytes;
    unsigned int code, fail, ok200;
    ARRAY urls;                     // 目標 URL 列表
    float time, himark, lomark;     // 總/最慢/最快響應時間
    unsigned int rand_r_SEED;       // 隨機數種子
} CLIENT;

核心流程源碼走讀

1. 計時線程:pthread_cond_timedwait 精準定時

void siege_timer(pthread_t handler) {
    pthread_mutex_t timer_mutex = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t timer_cond = PTHREAD_COND_INITIALIZER;
    struct timespec timeout = { .tv_sec = time(NULL) + my.secs };

    pthread_mutex_lock(&timer_mutex);
    for (;;) {
        int err = pthread_cond_timedwait(&timer_cond, &timer_mutex, &timeout);
        if (err == ETIMEDOUT) {
            pthread_kill(handler, SIGTERM);  // 通知控制線程
            break;
        }
    }
    pthread_mutex_unlock(&timer_mutex);
}

利用條件變量的超時返回特性實現定時器,避免忙等待。

2. 控制線程:信號屏蔽 + sigwait 同步處理異步信號

void sig_handler(CREW crew) {
    sigset_t sigs; sigemptyset(&sigs);
    sigaddset(&sigs, SIGHUP); sigaddset(&sigs, SIGINT); sigaddset(&sigs, SIGTERM);
    sigprocmask(SIG_BLOCK, &sigs, NULL);  // 必須先屏蔽

    int gotsig; sigwait(&sigs, &gotsig);  // 同步等待信號
    crew_cancel(crew);                    // 設置 shutdown 標誌、廣播條件變量
    pthread_usleep_np(501125);            // 微調時間誤差
    pthread_exit(NULL);
}

關鍵點:sigprocmask + sigwait 將異步信號轉為同步處理,避免信號處理函數中的非安全操作。

3. 壓測線程:經典生產者-消費者模式

void *crew_thread(void *arg) {
    CREW this = (CREW)arg;
    while (TRUE) {
        pthread_mutex_lock(&this->lock);
        while (this->cursize == 0 && !this->shutdown)
            pthread_cond_wait(&this->not_empty, &this->lock);  // 等待任務

        if (this->shutdown) { pthread_mutex_unlock(&this->lock); pthread_exit(NULL); }

        WORK *workptr = this->head;
        this->cursize--;
        /* 更新 head/tail、廣播 not_full/empty ... */
        pthread_mutex_unlock(&this->lock);

        (*workptr->routine)(workptr->arg);  // 執行壓測任務
        xfree(workptr);
    }
}

嚴格的鎖保護共享隊列,任務執行在鎖外,最大化並發度。

4. 壓測任務實現:start_routine 核心邏輯

void *start_routine(CLIENT *client) {
    CONN *C = xcalloc(sizeof(CONN), 1);
    pthread_cleanup_push(clean_up, C);           // 線程清理函數 (finally 語義)
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &type); // 異步取消
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &state);      // 允許取消

    for (...) {
        URL tmp = array_get(client->urls);
        if (tmp && __request(C, tmp, client) == FALSE)
            __increment_failures();
        if (my.failures > 0 && my.failed >= my.failures) break;
    }
    pthread_cleanup_pop(0);
    if (C->sock >= 0) socket_close(C);
    xfree(C);
    return NULL;
}
  • pthread_cleanup_push/pop:構建棧式清理處理器,線程退出(正常/取消)自動調用。
  • PTHREAD_CANCEL_ASYNCHRONOUS:任意點可被取消,配合系統調用取消點(read/write/connect 等)實現響應性。

5. 主函數:統籌全局

int main() {
    /* 信號屏蔽、參數解析 */
    crew = new_crew(my.cusers, my.cusers, FALSE);
    /* 初始化 URLs、CLIENT 數組 */
    pthread_create(&cease, NULL, sig_handler, crew);      // 控制線程
    pthread_create(&timer, NULL, siege_timer, cease);     // 計時線程 (傳遞 cease ID)

    for (x=0; x<my.cusers; x++)
        crew_add(crew, start_routine, &client[x]);        // 分發任務

    crew_join(crew, TRUE, &status);                       // 等待所有 worker 結束
    /* 聚合統計、輸出報告、釋放資源 */
}

優缺點客觀評價

優點缺點
多線程開銷小,單機可支撐高並發 壓力控制粗糙,無法精確控制 QPS/發送間隔
支持到時/到量雙停止條件、多 URL 隨機/順序、HTTP/HTTPS/FTP、GET/POST/HEAD 計時精度有限(如設 5s 實測 4.89s),基於 times() 系統時鐘
完善的跨平台兼容層、認證、Cookie、代理支持 線程數受進程限制(ulimit -u、pthread_create 失敗),過多線程切換開銷大
代碼模塊化極佳,學習 C 面向對象風格、線程同步的最佳範本 同步阻塞模型,單線程處理一請求,高延遲場景下吞吐受限

總結

Siege 不僅是優秀的壓測工具,更是C 語言多線程編程的活教材。通過研讀其源碼,可系統掌握:

  1. 線程池與任務隊列的鎖/條件變量實現。
  2. 信號與線程交互的安全模式(sigprocmask + sigwait)。
  3. 線程取消機制(異步/延遲、清理處理器、取消點)。
  4. 生產者-消費者模式在高併發服務中的工程化落地。

建議配合《UNIX 環境高級編程》(APUE)第 11-12 章線程章節對照閱讀,收穫加倍。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved