中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

srs使用的開源c語言網絡協程庫state thread源碼是什么

發布時間:2021-10-12 16:24:25 來源:億速云 閱讀:196 作者:iii 欄目:編程語言

本篇內容主要講解“srs使用的開源c語言網絡協程庫state thread源碼是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“srs使用的開源c語言網絡協程庫state thread源碼是什么”吧!

state thread是一個開源的c語言網絡協程庫,它在用戶空間實現了協程調度
st最初是由網景(Netscape)公司的MSPR(Netscape Portable Runtime library)項目中剝離出來,后由SGI(Silicon Graphic Inc)和Yahoo!公司(前者是主力)共同開發維護。
2001年發布v1.0以來一直到2009年v1.9穩定版后未再變動

State Threads:回調終結者(必讀)
https://blog.csdn.net/caoshangpa/article/details/79565411

st-1.9.tar.gz 是原版, http://state-threads.sourceforge.net/
state-threads-1.9.1.tar.gz 是srs修改版, https://github.com/ossrs/state-threads

st源碼編譯
tar zxvf st-1.9.tar.gz
cd st-1.9
make linux-debug          // make命令可以查看支持的編譯選項
obj目錄有編譯生成的文件st.h, lib*.so,lib*.a
examples目錄有幾個例子lookupdns,proxy,server

需要的知識點
1 匯編語言(非必需)
2 線程的棧管理(非必需)
3 線程的調度和同步(必須)。線程不同步的測試代碼thread.c
4 setjmp/longjmp的使用(必須)。測試代碼setjmp.c
5 epoll原理和使用(必須)。測試代碼epoll_server.c 和 epoll_client.c

測試代碼以及文檔下載地址
鏈接: https://pan.baidu.com/s/1kQz3S1YIt6zUwMKScrnHaQ
提取碼: pu9z

分析state_thread源碼的目的,是為了正確的使用它
st中thread其實是協程的概念
st_xxx分為 io類 和 延遲類

一些重要的數據結構
_st_vp_t _st_this_vp;     virtual processor 虛擬處理器
_st_thread_t *_st_this_thread;
_st_clist_t  run_q, io_q, zombie_q, thread_q 
_st_thread_t  *idle_thread, *sleep_q

代碼分析
st庫自帶的example業務邏輯較為復雜,有興趣可以看下。
為了簡化問題,編寫了測試代碼st-1.9/examples/st_epoll.c,依據此代碼提出問題分析問題。
st_init()做了什么?
_st_idle_thread_start()做了什么?
st_thread_create()做了什么?
st_thread_exit()做了什么?
st_usleep()做了什么?
主業務邏輯(無限循環)協程是如何調度的?
監聽的文件描述符是如何調度的?
協程如何正常退出?
1 沒有設置終止條件變量(不可以被join)的協程直接return即可退出; 
2 設置了終止條件變量(可以被join)的協程退出時,先把自己加入到zombie_q中,然后通知等待的協程,等待的協程退出后,自己在退出。

協程的join(連接)是什么意思?
1 創建協程a的時候 st_thread_create(handle_cycle, NULL, 1, 0) 要設置為1, 表示該協程可以被join
2 協程b代碼里要掉用st_thread_join(thread, retvalp),表示我要join到協程a上
3 join的意思是 協程a和協程b 有一定關聯行,在協程退出時,要先退出協程b 才能退出協程a
4 st中一個協程只能被另一個協程join,不能被多個協程join
5 可以被join的協程a,在沒有其他協程join時,協程a無法正常退出

st里的mutex有什么用?
通常情況下st的多協程是不需要加鎖的,但是在有些情況下需要鎖來保證原子操作,下面會詳細說明。
st_mutex_new(void); 創建鎖
st_mutex_destroy(st_mutex_t lock); 等待隊列必須為空才能銷毀鎖
st_mutex_lock(st_mutex_t lock); 第一次掉用能獲得鎖,以后掉用會加入鎖的等待隊列中(FIFO)
st_mutex_unlock(st_mutex_t lock); 釋放鎖并激活等待隊列的協程
st_mutex_trylock(st_mutex_t lock); 嘗試獲得鎖不會加入到等待隊列

srs使用的開源c語言網絡協程庫state thread源碼是什么

st里的cond有什么用?
通常情況下st的多協程是不需要條件變量的,但是有些情況下需要條件變量來保證協程執行的先后順序,比如:協程a要先于協程b執行
st_cond_new(void); 創建條件變量
st_cond_destroy(st_cond_t cvar); 等待隊列必須為空才能銷毀條件變量
st_cond_timedwait(st_cond_t cvar, st_utime_t timeout); 限時等待條件變量,會加入條件變量的等待隊列中(FIFO),并加入到sleep_q隊列中(可能先于FIFO的順序被調度到)
st_cond_wait(st_cond_t cvar); 阻塞等待條件變量,會加入條件變量的等待隊列中(FIFO)
st_cond_signal(st_cond_t cvar); 喚醒阻塞在條件變量上的一個協程
st_cond_broadcast(st_cond_t cvar); 喚醒阻塞在條件變量上的全部協程

srs使用的開源c語言網絡協程庫state thread源碼是什么

這個圖要配合測試代碼 st-1.9/examples/st_epoll.c

st中與調度有關的函數
st的setjmp
#define _ST_SWITCH_CONTEXT(_thread)   \                協程切換的兩個宏函數之一,停止當前協程并運行其他協程
  ST_BEGIN_MACRO                      \
  ST_SWITCH_OUT_CB(_thread);          \                       協程切走時調用的函數,一般不管用
  if (!MD_SETJMP((_thread)->context)) \                         匯編語言實現 應該跟setjmp()一樣 首次掉用返回0
  {                                   \
    _st_vp_schedule();                \                                    核心調度函數
  }                                   \
  ST_DEBUG_ITERATE_THREADS();         \
  ST_SWITCH_IN_CB(_thread);           \                          協程切回時調用的函數,一般不管用
  ST_END_MACRO

st的longjmp
#define _ST_RESTORE_CONTEXT(_thread) \               協程切換的兩個宏函數之一,恢復線程運行
  ST_BEGIN_MACRO                     \
  _ST_SET_CURRENT_THREAD(_thread);   \                設置全局變量 _st_this_thread = _thread
  MD_LONGJMP((_thread)->context, 1); \                       匯編語言實現 應該跟longjmp()一樣, 返回值永遠為1
  ST_END_MACRO

MD_SETJMP的時候,會使用匯編把所有寄存器的信息保留下來,而MD_LONGJMP則會把所有的寄存器信息重新加載出來。兩者配合使用的時候,可以完成函數間的跳轉。

st的核心調度函數
void _st_vp_schedule(void)
{
  _st_thread_t *thread;
  printf("in _st_vp_schedule\n");
  printf("_st_active_count = %d\n", _st_active_count);
  if (_ST_RUNQ.next != &_ST_RUNQ)
  {
    printf("use runq\n");
    /* Pull thread off of the run queue */
    thread = _ST_THREAD_PTR(_ST_RUNQ.next);
    _ST_DEL_RUNQ(thread);
  }
  else
  {
    printf("use idle\n");
    /* If there are no threads to run, switch to the idle thread */
    thread = _st_this_vp.idle_thread;
  }
  ST_ASSERT(thread->state == _ST_ST_RUNNABLE);
  /* Resume the thread */
  thread->state = _ST_ST_RUNNING;
  _ST_RESTORE_CONTEXT(thread);
}

st輔助調度函數
void *_st_idle_thread_start(void *arg)
{
  printf("i'm in _st_idle_thread_start()\n");
  _st_thread_t *me = _ST_CURRENT_THREAD();

  while (_st_active_count > 0)
  {
    /* Idle vp till I/O is ready or the smallest timeout expired */
    printf("call _st_epoll_dispatch()\n");
    _ST_VP_IDLE();                                                       處理io類事件

    /* Check sleep queue for expired threads */
    _st_vp_check_clock();                                               處理延時類事件
    me->state = _ST_ST_RUNNABLE;
    _ST_SWITCH_CONTEXT(me);                                  從這里恢復運行,然后判斷_st_active_count的值
  }
/* No more threads */
  exit(0);                                                                        整個程序退出
  /* NOTREACHED */
  return NULL;
}

會觸發協程切換的函數有哪些?
sched.c:86: _ST_SWITCH_CONTEXT(me); 59 int st_poll(struct pollfd *pds, int npds, st_utime_t timeout)
sched.c:234: _ST_SWITCH_CONTEXT(me); 221 void *_st_idle_thread_start(void *arg)
sched.c:261: _ST_SWITCH_CONTEXT(thread); 244 void st_thread_exit(void *retval)
sched.c:276: _ST_SWITCH_CONTEXT(thread); 244 void st_thread_exit(void *retval)
sync.c:131: _ST_SWITCH_CONTEXT(me); 115 int st_usleep(st_utime_t usecs)
sync.c:198: _ST_SWITCH_CONTEXT(me); 180 int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout)
sync.c:315: _ST_SWITCH_CONTEXT(me); 290 int st_mutex_lock(_st_mutex_t *lock)

sched.c:134: _ST_RESTORE_CONTEXT(thread); 115 void _st_vp_schedule(void)

st中的interrupt
顯示調用void st_thread_interrupt(_st_thread_t *thread)會對協程設置interrupt狀態,interrupt狀態會中斷協程的本次運行(可能是個循環任務),是否導致協程退出,要看協程內部對interrupt返回值的處理。下面以st_usleep()函數為例進行說明。
[ykMac:st-1.9]# grep -nr "_ST_FL_INTERRUPT" *
common.h:311:#define _ST_FL_INTERRUPT 0x08        interrupt的宏定義
sched.c:68:  if (me->flags & _ST_FL_INTERRUPT)         59 int st_poll() ,調用函數時,判斷是否設置interrupt
sched.c:70:    me->flags &= ~_ST_FL_INTERRUPT;       如果設置就退出,退出前對interrupt取反
sched.c:107:  if (me->flags & _ST_FL_INTERRUPT)       59 int st_poll(),變為運行協程時,判斷是否設置interrupt
sched.c:109:    me->flags &= ~_ST_FL_INTERRUPT;     如果設置就退出,退出前對interrupt取反
sched.c:551:  thread->flags |= _ST_FL_INTERRUPT;     在545 void st_thread_interrupt()中設置為interrupt
sync.c:119:  if (me->flags & _ST_FL_INTERRUPT) {       115 int st_usleep(st_utime_t usecs),調用函數時
sync.c:120:    me->flags &= ~_ST_FL_INTERRUPT;
sync.c:133:  if (me->flags & _ST_FL_INTERRUPT) {       115 int st_usleep(st_utime_t usecs),變為運行協程時
sync.c:134:    me->flags &= ~_ST_FL_INTERRUPT;
sync.c:185:  if (me->flags & _ST_FL_INTERRUPT) {       180 int st_cond_timedwait(),調用函數時
sync.c:186:    me->flags &= ~_ST_FL_INTERRUPT;
sync.c:208:  if (me->flags & _ST_FL_INTERRUPT) {       180 int st_cond_timedwait(),變為運行協程時
sync.c:209:    me->flags &= ~_ST_FL_INTERRUPT;
sync.c:294:  if (me->flags & _ST_FL_INTERRUPT) {       290 int st_mutex_lock(),調用函數時
sync.c:295:    me->flags &= ~_ST_FL_INTERRUPT;
sync.c:319:  if ((me->flags & _ST_FL_INTERRUPT) && lock->owner != me) {       290 int st_mutex_lock(),變運行時
sync.c:320:    me->flags &= ~_ST_FL_INTERRUPT;

115 int st_usleep(st_utime_t usecs)
116 {
117   _st_thread_t *me = _ST_CURRENT_THREAD();
118
119   if (me->flags & _ST_FL_INTERRUPT) {
120     me->flags &= ~_ST_FL_INTERRUPT;     退出前對interrupt取反
121     errno = EINTR;
122     return -1;                                                如果不對errno或返回值做處理,循環還是會繼續的
123   }
124
125   if (usecs != ST_UTIME_NO_TIMEOUT) {
126     me->state = _ST_ST_SLEEPING;
127     _ST_ADD_SLEEPQ(me, usecs);
128   } else
129     me->state = _ST_ST_SUSPENDED;
130
131   _ST_SWITCH_CONTEXT(me);
132
133   if (me->flags & _ST_FL_INTERRUPT) {
134     me->flags &= ~_ST_FL_INTERRUPT;
135     errno = EINTR;
136     return -1;
137   }
138
139   return 0;
140 }

st的優缺點
優點:
1 用戶空間實現協程調度,降低了用戶空間和內核空間的切換,一定程度上提高了程序效率。
2 由于是在單核上的單線程多協程,同一時間只會有一個協程在運行,所以對于全局變量也不需要做協程同步。
   共享資源釋放函數只需做到可重入就行,所謂的可重入就是釋放之前先判斷是否為空值,釋放后要賦空值。
3 協程使用完,直接return即可,st會回收協程資源并做協程切換。
4 可以通過向run_q鏈表頭部加入協程,來實現優先調度。
5 st支持多個操作系統,比如 AIX,CYGWIN,DARWIN,FREEBSD,HPUX,IRIX,LINUX,NETBSD,OPENBSD,SOLARIS
缺點:
1 所有I/O操作必須使用st提供的API,只有這樣協程才能被調度器管理。
2 所有協程里不能使用sleep(),sleep()會造成整個線程sleep。
3 被調度到的協程不會被限制運行時長,如果有協程是cpu密集型或死循環,就會嚴重阻礙其他協程運行。
4 單進程單線程,只能使用單核,想要通過多個cpu提高并發能力,只能開多個程序(進程),多進程通信較麻煩。

補充知識點
1 線程為什么要同步?
線程由內核自動調度
同一個進程上的線程共享該進程的整個虛擬地址空間
同一個進程上的線程代碼區是共享的,即不同的線程可以執行同樣的函數
所以在并發環境中,多個線程同時對同一個內存地址進行寫入,由于CPU寄存器時間調度上的問題,寫入數據會被多次的覆蓋,會造成共享數據損壞,所以就要使線程同步。

2 什么情況下需要線程同步?
線程同步指的是 不同時發生,就是線程要排隊
1 多核,單進程多線程,不同線程會對全局變量讀寫,這種情況才需要對線程做同步控制
2 單核,單進程多線程,不同線程會對全局變量讀寫,這種情況不需要對線程做同步控制
3 多核,單進程多線程,不同線程不對全局變量讀寫,這種情況不需要對線程做同步控制
4 多核,單進程多線程,不同線程會對全局變量讀,這種情況不需要對線程做同步控制

問題:對于第2條,這個應該是有點兒片面,有依賴有優先級搶占也是要同步,除非像abcde這種幾個線程干一摸一樣的事情,而且項目之間不依賴。
有依賴 可以理解為 生產消費關系,雖然是 單核 單進程 多線程 但是同一時間只能有一個線程在運行,也就是說 生產和消費不會同時發生,同樣多個生產也不會同時發生,所以不需要鎖。
線程是有優先級控制,但是不管怎么控制,只要保證同一時間只能有一個線程在運行,就不需要鎖了。
問題:對于第2條,這個應該是有點兒片面,有原子操作且原子操作過程中有線程切換,這種是需要鎖的。
比如,線程a 第一次讀取全局變量x并做處理,然后發生線程切換(線程由內和自動調度)后切回,然后第二次讀取全局變量x并做處理,我們想確保兩次讀取
x值相同,但是發生了線程切換x值可能被改變。
如何 確保 第一次讀取并處理和第二次讀取并處理是原子操作呢? 使用st_mutex_t

3 accept()序列化
亦稱驚群效應,亦亦稱Zeeg難題
https://uwsgi-docs-zh.readthedocs.io/zh_CN/latest/articles/SerializingAccept.html
在多次fork自己之后,每個進程一般將會開始阻塞在 accept() 上
每當socket上嘗試進行一個連接,阻塞在 accept() 上的每個進程的 accept() 都會被喚醒。
只有其中一個進程能夠真正接收到這個連接,而剩余的進程將會獲得一個無聊的 EAGAIN 這導致了大量的CPU周期浪費,實際解決方法是把一個鎖放在 accept() 調用之前,來序列化它的使用

4 Internet Applications網絡程序架構
多進程架構 Multi-Process
    一個進程服務一個連接,要解決數據共享問題
單進程多線程架構 Multi-Threaded 
    一個線程服務一個連接,要解決數據同步問題
事件驅動的狀態機架構 Event-Driven State Machine 
    事件觸發回調函數(缺點是嵌套) 或 用戶空間實現協程調度
實際上 EDSM架構 用很復雜的方式模擬了多線程
st提供的就是EDSM機制,它在用戶空間實現協程調度

到此,相信大家對“srs使用的開源c語言網絡協程庫state thread源碼是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節
推薦閱讀:
  1. 進程線程協程
  2. GO協程

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

洮南市| 湛江市| 宁安市| 巧家县| 顺义区| 宽甸| 施甸县| 镇康县| 遂川县| 铜陵市| 商洛市| 宣汉县| 上饶县| 双城市| 怀化市| 宁武县| 东方市| 调兵山市| 富蕴县| 荣昌县| 客服| 仙桃市| 格尔木市| 长沙市| 禄丰县| 塘沽区| 友谊县| 大城县| 宿松县| 廊坊市| 西青区| 香格里拉县| 钦州市| 榆中县| 万山特区| 独山县| 汝南县| 五大连池市| 讷河市| 牟定县| 安吉县|