vpp中的协程实现
????????最近在看 vpp 的源碼,發現里面用了大量的 setjmp 和 longjmp,在逐漸深入閱讀代碼的過程中才意識到這就是所謂的協程呀。花了一些時間將其中的關鍵代碼抽出來進行調試,收獲不少,想要學習協程的同學可以做個參考。
????????下面這個關于協程的 demo 是基于 x86 架構的,一共涉及四個文件,分別是
- longjmp.h? // 聲明 jmp 相關函數的原型
- longjmp.S? ?// 使用 x86 匯編代碼實現 jmp 相關函數
- main.c? // 使用 jmp 相關函數實現協程和進行測試
- Makefile
????????longjmp.h 源碼
#ifndef included_clib_longjmp_h #define included_clib_longjmp_h#define CLIB_ARCH_LONGJMP_REGS (22)typedef unsigned long u64; typedef u64 uword;typedef struct {uword regs[CLIB_ARCH_LONGJMP_REGS]; } clib_longjmp_t __attribute__ ((aligned (16)));/* Return given value to saved context. */ void clib_longjmp (clib_longjmp_t * save, uword return_value);/* Save context. Returns given value if jump is not taken;otherwise returns value from clib_longjmp if long jump is taken. */ uword clib_setjmp (clib_longjmp_t * save, uword return_value_not_taken);/* Call function on given stack. */ uword clib_calljmp (uword (*func) (uword func_arg),uword func_arg, void *stack);#endif /* included_clib_longjmp_h *//** fd.io coding-style-patch-verification: ON** Local Variables:* eval: (c-set-style "gnu")* End:*/????????longjmp.S 源碼
.global clib_setjmp.align 4.type clib_setjmp, @function clib_setjmp:movq %rbx, 8*0(%rdi)movq %rbp, 8*1(%rdi)movq %r12, 8*2(%rdi)movq %r13, 8*3(%rdi)movq %r14, 8*4(%rdi)movq %r15, 8*5(%rdi)/* Save SP after return. */leaq 8(%rsp), %rdxmovq %rdx, 8*6(%rdi)/* Save PC we are returning to from stack frame. */movq 0(%rsp), %raxmovq %rax, 8*7(%rdi)/* Give back user's return value. */movq %rsi, %raxret.global clib_longjmp.align 4.type clib_longjmp, @function clib_longjmp: /* Restore regs. */movq 8*0(%rdi), %rbxmovq 8*1(%rdi), %rbpmovq 8*2(%rdi), %r12movq 8*3(%rdi), %r13movq 8*4(%rdi), %r14movq 8*5(%rdi), %r15movq 8*6(%rdi), %rspmovq 8*7(%rdi), %rdx/* Give back user's return value. */movq %rsi, %rax/* Away we go. */jmpq *%rdx .global clib_calljmp.align 4.type clib_calljmp, @function clib_calljmp:/* Make sure stack is 16-byte aligned. */movq %rdx, %raxandq $0xf, %raxsubq %rax, %rdx/* Get return address. */pop %rax/* Switch to new stack. */xchgq %rsp, %rdx/* Save return address on new stack. */push %rax/* Save old stack pointer on new stack. */push %rdx/* Get function. */movq %rdi, %rdx/* Move argument into place. */movq %rsi, %rdi/* Away we go. */callq *%rdx/* Switch back to old stack. */movq 8(%rsp), %rdxmovq 0(%rsp), %rcxxchgq %rcx, %rsp/* Return to caller. */jmpq *%rdx? ? ? ? main.c 源碼
#include <assert.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <stdint.h> #include <sys/time.h> #include <unistd.h> #include "longjmp.h"#define always_inline static inline __attribute__ ((__always_inline__))#define RETURN_SETJMP_MAGIC 0x0930 #define RETURN_LONGJMP_MAGIC 0x0931#define RESUME_SETJMP_MAGIC 0x1112 #define RESUME_LONGJMP_MAGIC 0x1113typedef double f64;typedef uword (*worker_func_t)(uword func_arg);struct process_context {f64 interval;clib_longjmp_t return_longjmp;/* Where to longjmp to resume node after suspend. */clib_longjmp_t resume_longjmp;int process_index;void *stack;int stack_size;worker_func_t function;f64 wait_left;f64 wakeup_time;const char *name; };uword oam_process(uword arg);uword dhcp_process(uword arg);struct function_register {worker_func_t function;const char *name; };struct function_register worker_funcs[] = {{oam_process, "oam_process"},{dhcp_process, "dhcp_process"}, };#define WORKER_NUM (sizeof(worker_funcs) / sizeof(struct function_register))struct process_context workers[WORKER_NUM];always_inline int vlib_process_suspend_time_is_zero(f64 dt);always_inline int vlib_process_suspend_time_is_zero(f64 dt) {return dt < 10e-6; }int init_workers(void) {int i;memset(workers, 0, sizeof(workers));for (i = 0; i < WORKER_NUM; ++i) {workers[i].interval = (i + 1);workers[i].process_index = i;workers[i].stack_size = (1 << 16);workers[i].stack = malloc(sizeof(char) * workers[i].stack_size);if (workers[i].stack == NULL) {printf("alloc stack for process failed!\n");return -1;}memset(workers[i].stack, 0, sizeof(char) * workers[i].stack_size);workers[i].function = worker_funcs[i].function;workers[i].name = worker_funcs[i].name;}return 0; }always_inline f64 vlib_time_now(void) {struct timespec ts;clock_gettime(CLOCK_MONOTONIC, &ts);return (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0; }always_inline f64 get_now(void) {struct timeval t;gettimeofday(&t, NULL);return (f64)t.tv_sec + (f64)t.tv_usec / 1000000.0; }always_inline f64 vlib_process_wait_for_event_or_clock(struct process_context *worker, f64 dt);always_inline f64 vlib_process_wait_for_event_or_clock(struct process_context *worker, f64 dt) {f64 wakeup_time;int r;if (vlib_process_suspend_time_is_zero(dt))return dt; wakeup_time = get_now() + dt;r = clib_setjmp(&worker->resume_longjmp, RESUME_SETJMP_MAGIC);if (r == RESUME_SETJMP_MAGIC) {worker->wakeup_time = wakeup_time;worker->wait_left = dt;clib_longjmp(&worker->return_longjmp, RETURN_LONGJMP_MAGIC);}assert(worker->wakeup_time == wakeup_time);return wakeup_time - get_now(); }int ts_to_str(uint64_t ts, char *buf, int max_len) {time_t t = (time_t)ts;const char *format = "%Y-%m-%d %H:%M:%S";struct tm lt;int ret = 0;(void) localtime_r(&t, <);if ((ret = strftime(buf, max_len, format, <)) == 0) {return sprintf(buf, "unknown");}return ret; }uword oam_process(uword arg) {f64 now, dt;int cnt = 0;char time_str[32];struct process_context *worker;worker = (struct process_context *)arg;dt = worker->interval;while (1) {dt = vlib_process_wait_for_event_or_clock(worker, dt);worker->wait_left = dt;if (!vlib_process_suspend_time_is_zero(dt))continue;now = get_now();ts_to_str(now, time_str, sizeof(time_str));printf("[%s][%s] worker %d: begin to recv packet ...\n", __func__,time_str, worker->process_index);/* recv one packet *//* handle this packet */now = get_now();ts_to_str(now, time_str, sizeof(time_str));printf("[%s][%s] worker %d: handle %d-th packet done!\n", __func__,time_str, worker->process_index, cnt);dt = worker->interval;cnt++;}return 0; }uword dhcp_process(uword arg) {f64 now, dt;int cnt = 0;char time_str[32];struct process_context *worker;worker = (struct process_context *)arg;dt = worker->interval;while (1) {dt = vlib_process_wait_for_event_or_clock(worker, dt);worker->wait_left = dt;if (!vlib_process_suspend_time_is_zero(dt))continue;now = get_now();ts_to_str(now, time_str, sizeof(time_str));printf("[%s][%s] worker %d: begin to recv packet ...\n", __func__,time_str, worker->process_index);/* recv one packet *//* handle this packet */now = get_now();ts_to_str(now, time_str, sizeof(time_str));printf("[%s][%s] worker %d: handle %d-th packet done!\n", __func__,time_str, worker->process_index, cnt);dt = worker->interval;cnt++;}return 0; }int resume_process(struct process_context *worker) {int r;r = clib_setjmp(&worker->return_longjmp, RETURN_SETJMP_MAGIC);if (r == RETURN_SETJMP_MAGIC) {clib_longjmp(&worker->resume_longjmp, RESUME_LONGJMP_MAGIC);}return 0; }int start_process(struct process_context *worker) {int r;r = clib_setjmp(&worker->return_longjmp, RETURN_SETJMP_MAGIC); if (r == RETURN_SETJMP_MAGIC) {r = clib_calljmp(worker->function, (uword)worker,worker->stack + worker->stack_size);}return r; }int main(void) {int i;if (init_workers() < 0) {printf("init workers failed!\n");return -1;}for (i = 0; i < WORKER_NUM; ++i) {start_process(&workers[i]);}while (1) {for (i = 0; i < WORKER_NUM; ++i) {resume_process(&workers[i]);}/* do something */}return 0; }? ? ? ? Makefile 源碼
all: mainmain: main.c longjmp.S gcc -g -O2 -DFORTIFY_SOURCE=2 -fstack-protector -fPIC -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast -Wno-address-of-packed-member $^ -o $@clean:rm main????????運行效果:
# make gcc -g -O2 -DFORTIFY_SOURCE=2 -fstack-protector -fPIC -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast -Wno-address-of-packed-member main.c longjmp.S -o main # ./main [oam_process][2021-11-23 19:22:26] worker 0: begin to recv packet ... [oam_process][2021-11-23 19:22:26] worker 0: handle 0-th packet done! [oam_process][2021-11-23 19:22:27] worker 0: begin to recv packet ... [oam_process][2021-11-23 19:22:27] worker 0: handle 1-th packet done! [dhcp_process][2021-11-23 19:22:27] worker 1: begin to recv packet ... [dhcp_process][2021-11-23 19:22:27] worker 1: handle 0-th packet done! [oam_process][2021-11-23 19:22:28] worker 0: begin to recv packet ... [oam_process][2021-11-23 19:22:28] worker 0: handle 2-th packet done! [oam_process][2021-11-23 19:22:30] worker 0: begin to recv packet ... [oam_process][2021-11-23 19:22:30] worker 0: handle 3-th packet done! [dhcp_process][2021-11-23 19:22:30] worker 1: begin to recv packet ... [dhcp_process][2021-11-23 19:22:30] worker 1: handle 1-th packet done! [oam_process][2021-11-23 19:22:31] worker 0: begin to recv packet ... [oam_process][2021-11-23 19:22:31] worker 0: handle 4-th packet done! [oam_process][2021-11-23 19:22:32] worker 0: begin to recv packet ... [oam_process][2021-11-23 19:22:32] worker 0: handle 5-th packet done! [dhcp_process][2021-11-23 19:22:32] worker 1: begin to recv packet ... [dhcp_process][2021-11-23 19:22:32] worker 1: handle 2-th packet done! [oam_process][2021-11-23 19:22:33] worker 0: begin to recv packet ... [oam_process][2021-11-23 19:22:33] worker 0: handle 6-th packet done! [dhcp_process][2021-11-23 19:22:35] worker 1: begin to recv packet ... [dhcp_process][2021-11-23 19:22:35] worker 1: handle 3-th packet done! [oam_process][2021-11-23 19:22:35] worker 0: begin to recv packet ... [oam_process][2021-11-23 19:22:35] worker 0: handle 7-th packet done! [oam_process][2021-11-23 19:22:36] worker 0: begin to recv packet ... [oam_process][2021-11-23 19:22:36] worker 0: handle 8-th packet done! [dhcp_process][2021-11-23 19:22:37] worker 1: begin to recv packet ... [dhcp_process][2021-11-23 19:22:37] worker 1: handle 4-th packet done! [oam_process][2021-11-23 19:22:37] worker 0: begin to recv packet ... [oam_process][2021-11-23 19:22:37] worker 0: handle 9-th packet done! [oam_process][2021-11-23 19:22:39] worker 0: begin to recv packet ... [oam_process][2021-11-23 19:22:39] worker 0: handle 10-th packet done! [dhcp_process][2021-11-23 19:22:40] worker 1: begin to recv packet ... [dhcp_process][2021-11-23 19:22:40] worker 1: handle 5-th packet done!總結
- 上一篇: WIN10的永久杜比音效的安装(2021
- 下一篇: aliyun阿里云视频直播播放器代码