日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

协程库st(state threads library)原理解析

發(fā)布時間:2024/2/28 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 协程库st(state threads library)原理解析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

協(xié)程庫state threads library(以下簡稱st)是一個基于setjmp/longjmp實現(xiàn)的C語言版用戶線程庫或協(xié)程庫(user level thread)。

這里有一個基本的協(xié)程例子?http://www.csl.mtu.edu/cs4411.ck/www/NOTES/non-local-goto/coroutine.html, 可以了解setjmp和longjmp的基本用法。如還有不懂,請自行查閱其他資料。本文主要關注st基于setjmp和longjmp的實現(xiàn)原理及其程序結構。

?

st基本介紹?http://state-threads.sourceforge.net/docs/st.html。

從中可以看出,IA(Internet Application)架構演化歷史:

1.多進程MP

以Apache為代表的web server。創(chuàng)建進程服務新用戶,開銷過高。調度單位是進程。

2.多線程MT

創(chuàng)建新線程服務新用戶,線程間上下文切換,鎖競爭等等,增加了額外開銷。調度單位是線程。

3.事件驅動狀態(tài)機EDSM

基于IO復用機制,實現(xiàn)大量并發(fā)請求的處理。但程序是一體的,并不是基于線程,新程序需要從頭開始。該模式下主要使用回調和狀態(tài)參數來進行上下文切換,實際上是以一種非常艱難和痛苦的方式實現(xiàn)了類似線程和棧的思想。ESDM最大的問題是其“將線性思路分解成大量的回調所固有的復雜性”,導致程序難以實現(xiàn),擴展和維護。

傳統(tǒng)EDSM程序架構:

4.協(xié)程

調度單位減小到函數,上下文切換不需要內核參與,不存在系統(tǒng)調用。上下文切換開銷降到最低,系統(tǒng)調用降到最低,沒有鎖競爭,沒有信號處理。保留了程序對請求的線性處理邏輯,提高了程序的開發(fā)效率,可擴展性和可維護性。

基于st的ESDM程序模型:

?

基于setjmp和longjmp實現(xiàn)協(xié)程庫基本步驟(下述線程指用戶線程):

1.需要用jmpbuf變量保存每一個線程的運行時環(huán)境,稱為線程上下文context。

2.為每個線程分配(malloc/mmap)一個stack,用于該線程運行時棧,該stack完全等效于普通系統(tǒng)線程的函數調用棧。該stack地址是在線程初始化時設置,所以不需要考慮setjmp時保存線程的棧上frames數據的問題。

3.通過調用setjmp初始化線程運行時上下文,將context數據存放到jmpbuf結構中。然后修改其中的棧指針sp指向上一步分配的stack。根據當前系統(tǒng)棧的增長方向,將sp設置為stack的最低或最高地址。

4.線程退出時,需要返回到一個安全的系統(tǒng)位置。即,需要有一個主線程main thread或idle thread來作為其他線程最終的退出跳轉地址。需要為主線程保存一個jmpbuf。

5.設置過main thread的jmpbuf后,需要跳轉到其他線程開始執(zhí)行業(yè)務線程。

6.實現(xiàn)一個context交換函數,在多個線程之間進行跳轉:保存自己的jmpbuf,longjmp到另一個線程的jmpbuf。

?

st基于setjmp和longjmp的具體實現(xiàn):

線程初始化:

#define MD_INIT_CONTEXT(_thread, _sp, _main) \ST_BEGIN_MACRO \if (MD_SETJMP((_thread)->context)) \_main(); \MD_GET_SP(_thread) = (long) (_sp); \ST_END_MACRO

很明顯可以看到,setjmp(將jmpbuf存放到thread->context)之后,同時修改它的棧指針sp指向新分配的線程stack->sp地址。該sp指針用于該thread以后的棧frames數據存儲。

線程切換:

#define _ST_SWITCH_CONTEXT(_thread) \ST_BEGIN_MACRO \ST_SWITCH_OUT_CB(_thread); \if (!MD_SETJMP((_thread)->context)) { \_st_vp_schedule(); \} \ST_DEBUG_ITERATE_THREADS(); \ST_SWITCH_IN_CB(_thread); \ST_END_MACRO

其中主要時MD_SETJMP保存當前context,然后調用_st_vp_schedule()從_ST_RUNQ上取第一個可運行的thread,并調用_ST_RESTORE_CONTEXT將該thread恢復運行。

線程恢復:

#define _ST_RESTORE_CONTEXT(_thread) \ST_BEGIN_MACRO \_ST_SET_CURRENT_THREAD(_thread); \MD_LONGJMP((_thread)->context, 1); \ST_END_MACRO

起始線程primordial thread和休眠線程idle thread:

/** Initialize this Virtual Processor*/ int st_init(void) {_st_thread_t *thread;if (_st_active_count) {/* Already initialized */return 0;}/* We can ignore return value here */st_set_eventsys(ST_EVENTSYS_DEFAULT);if (_st_io_init() < 0)return -1;memset(&_st_this_vp, 0, sizeof(_st_vp_t));ST_INIT_CLIST(&_ST_RUNQ);ST_INIT_CLIST(&_ST_IOQ);ST_INIT_CLIST(&_ST_ZOMBIEQ); #ifdef DEBUGST_INIT_CLIST(&_ST_THREADQ); #endifif ((*_st_eventsys->init)() < 0)return -1;_st_this_vp.pagesize = getpagesize();_st_this_vp.last_clock = st_utime();/** Create idle thread*/_st_this_vp.idle_thread = st_thread_create(_st_idle_thread_start,NULL, 0, 0);if (!_st_this_vp.idle_thread)return -1;_st_this_vp.idle_thread->flags = _ST_FL_IDLE_THREAD;_st_active_count--;_ST_DEL_RUNQ(_st_this_vp.idle_thread);/** Initialize primordial thread*/thread = (_st_thread_t *) calloc(1, sizeof(_st_thread_t) +(ST_KEYS_MAX * sizeof(void *)));if (!thread)return -1;thread->private_data = (void **) (thread + 1);thread->state = _ST_ST_RUNNING;thread->flags = _ST_FL_PRIMORDIAL;_ST_SET_CURRENT_THREAD(thread);_st_active_count++; #ifdef DEBUG_ST_ADD_THREADQ(thread); #endifreturn 0; }

在st_init里面,創(chuàng)建primordial thread和idle thread。primordial thread作為起始線程當有其他線程加入運行隊列后從該線程切出,idle thread作為背景線程在沒有可運行線程的時候執(zhí)行io調度函數分發(fā)事件。

st_init里面調用st_thread_create并不會開始執(zhí)行idle線程,創(chuàng)建其他線程也一樣,只有在直接或間接調用_st_vp_schedule之后才會開始執(zhí)行RUNQ上面的線程。_st_vp_schedule函數在_ST_SWITCH_CONTEXT中被調用。

?

st程序結構:

st底層基于event-driven select/poll/kqueue/epoll等IO復用機制。下面以epoll為例說明st底層事件管理機制。

st中有IOQ,ZOMBIEQ,RUNQ,SLEEPQ等幾個隊列,用來存儲處于對應狀態(tài)的threads。

  • RUNQ中存儲的是可以被調度運行的threads,每次調用_st_vp_schedule即從該隊列取出一個thread去運行。
  • IOQ存儲處于IO等待狀態(tài)的threads,當上層調用st_poll時,將該thread放入IOQ中;當底層epoll有IO事件到達時,將該thread從IOQ中移除,并放入RUNQ中。
  • 當thread退出時,放入ZOMBIEQ中。
  • 當st_poll傳入超時參數>0或調用st_usleep和st_cond_timewait時,將thread加入SLEEPQ中。

總結

以上是生活随笔為你收集整理的协程库st(state threads library)原理解析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。