網絡IO解決方案 — 協程框架的實現


BAT, 滴滴,今日頭條,美圖,美團等一線內推 技術崗位內推 

QQ羣935760465


協程這個概念很久了,好多程序員是實現過這個組件的,網上關於協程的文章,博客,論壇都是汗牛充棟,在知乎,github上面也有很多大牛寫了關於協程的心得體會。突發奇想,我也來實現一個這樣的組件,並測試了一下性能。借鑑了很多大牛的思想,閱讀了很多大牛的代碼。於是把整個思考過程寫下來。實現代碼 https://github.com/wangbojing/NtyCo

代碼簡單易讀,如果在你的項目中,NtyCo能夠爲你解決些許工程問題,那就榮幸之至。


下面將部分的NtyCo的代碼貼出來。


NtyCo 支持多核多進程。

int process_bind(void) {

	int num = sysconf(_SC_NPROCESSORS_CONF);

	pid_t self_id = syscall(__NR_gettid);
	printf("selfid --> %d\n", self_id);

	cpu_set_t mask;

	CPU_ZERO(&mask);
	CPU_SET(self_id % num, &mask);

	sched_setaffinity(0, sizeof(mask), &mask);

	mulcore_entry(9096 + (self_id % num) * 10);

}


NtyCo 上下文切換

首先來回顧一下x86_64寄存器的相關知識。x86_64 的寄存器有1664位寄存器,分別是:%rax, %rbx, %rcx, %esi, %edi, %rbp, %rsp, %r8, %r9, %r10, %r11, %r12,

%r13, %r14, %r15

%rax 作爲函數返回值使用的。

%rsp 棧指針寄存器,指向棧頂

%rdi, %rsi, %rdx, %rcx, %r8, %r9 用作函數參數,依次對應第1參數,第2參數。。。

%rbx, %rbp, %r12, %r13, %r14, %r15 用作數據存儲,遵循調用者使用規則,換句話說,就是隨便用。調用子函數之前要備份它,以防它被修改

%r10, %r11 用作數據存儲,就是使用前要先保存原值。

 

上下文切換,就是將CPU的寄存器暫時保存,再將即將運行的協程的上下文寄存器,分別mov到相對應的寄存器上。此時上下文完成切換。如下圖所示:


image.png



代碼如下

__asm__ (
"    .text                                  \n"
"       .p2align 4,,15                                   \n"
".globl _switch                                          \n"
".globl __switch                                         \n"
"_switch:                                                \n"
"__switch:                                               \n"
"       movq %rsp, 0(%rsi)      # save stack_pointer     \n"
"       movq %rbp, 8(%rsi)      # save frame_pointer     \n"
"       movq (%rsp), %rax       # save insn_pointer      \n"
"       movq %rax, 16(%rsi)                              \n"
"       movq %rbx, 24(%rsi)     # save rbx,r12-r15       \n"
"       movq %r12, 32(%rsi)                              \n"
"       movq %r13, 40(%rsi)                              \n"
"       movq %r14, 48(%rsi)                              \n"
"       movq %r15, 56(%rsi)                              \n"
"       movq 56(%rdi), %r15                              \n"
"       movq 48(%rdi), %r14                              \n"
"       movq 40(%rdi), %r13     # restore rbx,r12-r15    \n"
"       movq 32(%rdi), %r12                              \n"
"       movq 24(%rdi), %rbx                              \n"
"       movq 8(%rdi), %rbp      # restore frame_pointer  \n"
"       movq 0(%rdi), %rsp      # restore stack_pointer  \n"
"       movq 16(%rdi), %rax     # restore insn_pointer   \n"
"       movq %rax, (%rsp)                                \n"
"       ret                                              \n"
);


協程的調度器

調度器的實現,有兩種方案,一種是生產者消費者模式,另一種多狀態運行。

image.png


邏輯代碼如下:

while (1) {
 
        //遍歷睡眠集合,將滿足條件的加入到ready
        nty_coroutine *expired = NULL;
        while ((expired = sleep_tree_expired(sched)) != ) {
            TAILQ_ADD(&sched->ready, expired);
        }
 
        //遍歷等待集合,將滿足添加的加入到ready
        nty_coroutine *wait = NULL;
        int nready = epoll_wait(sched->epfd, events, EVENT_MAX, 1);
        for (i = 0;i < nready;i ++) {
            wait = wait_tree_search(events[i].data.fd);
            TAILQ_ADD(&sched->ready, wait);
        }
 
        // 使用resume回覆ready的協程運行權
        while (!TAILQ_EMPTY(&sched->ready)) {
            nty_coroutine *ready = TAILQ_POP(sched->ready);
            resume(ready);
        }
    }


多狀態運行

image.png

實現邏輯代碼如下:

while (1) {
 
        //遍歷睡眠集合,使用resume恢復expired的協程運行權
        nty_coroutine *expired = NULL;
        while ((expired = sleep_tree_expired(sched)) != ) {
            resume(expired);
        }
 
        //遍歷等待集合,使用resume恢復wait的協程運行權
        nty_coroutine *wait = NULL;
        int nready = epoll_wait(sched->epfd, events, EVENT_MAX, 1);
        for (i = 0;i < nready;i ++) {
            wait = wait_tree_search(events[i].data.fd);
            resume(wait);
        }
 
        // 使用resume恢復ready的協程運行權
        while (!TAILQ_EMPTY(sched->ready)) {
            nty_coroutine *ready = TAILQ_POP(sched->ready);
            resume(ready);
        }


性能測試

測試環境:4臺VMWare 虛擬機

1臺服務器 6G內存,4核CPU

3臺客戶端 2G內存,2核CPU

操作系統:ubuntu 14.04

服務器端測試代碼:https://github.com/wangbojing/NtyCo

客戶端測試代碼:https://github.com/wangbojing/c1000k_test/blob/master/client_mutlport_epoll.c

 

按照每一個連接啓動一個協程來測試。協程啓動數量能夠達70W無異常。



image.png


image.png



    BAT, 滴滴,今日頭條,美圖,美團等一線內推 技術崗位內推 

    QQ羣935760465


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