当前位置: 首页 > news >正文

做暧昧的小视频网站2台州seo排名公司

做暧昧的小视频网站2,台州seo排名公司,公司建推广网站多少钱,最新远程网站建设服务文章目录 协程设计原理与汇编实现1. 协程概念2. 协程的实现2.1 setjmp2.2 ucontext2.3 汇编实现2.4 优缺点2.5 实现协程原语2.5.1 create()2.5.2 yield()2.5.3 resume()2.5.4 exit()2.5.5 switch()2.5.6 sleep() 2.6 协程调度器 3. 利用hook使用协程版本的库函数学习参考 协程设…

文章目录

  • 协程设计原理与汇编实现
    • 1. 协程概念
    • 2. 协程的实现
      • 2.1 setjmp
      • 2.2 ucontext
      • 2.3 汇编实现
      • 2.4 优缺点
      • 2.5 实现协程原语
        • 2.5.1 create()
        • 2.5.2 yield()
        • 2.5.3 resume()
        • 2.5.4 exit()
        • 2.5.5 switch()
        • 2.5.6 sleep()
      • 2.6 协程调度器
    • 3. 利用hook使用协程版本的库函数
    • 学习参考

协程设计原理与汇编实现

本文介绍了协程的概念、特征、优势、以及其实现原理。

1. 协程概念

协程是一种轻量级的用户态线程。它允许在单个线程内执行多个任务,使得程序可以在不同的函数之间灵活地切换,以便更好地利用 CPU 资源。这种机制特别适合 IO 密集型任务(如网络请求、文件读写)和异步编程场景。协程可以被暂停和恢复,避免了阻塞等待,同时不需要系统级线程的切换成本。

协程的实现在底层是由执行流的跳转切换机制实现的。一般情况,有一个协程调度器作为每个协程挂起时要切换回的代码。

应用场景

  • webserver
  • kv存储
  • 图床,网络层

同步和异步

"同步"和"异步"主要是指在执行任务时,任务与调用方的相互关系。在同步操作中,调用方会等待任务执行完毕然后继续执行。在异步操作中,调用方会立即返回并继续执行后续的操作,不会等待任务执行完,任务执行完可以通过回调、事件等方式通知调用方。

异步的好处

  • 多线程并发,充分利用cpu,性能好。

异步的坏处

  • 代码复杂,不好理解,需要设置回调函数或者使用事件机制。

协程的好处

  • 同步的编程方式,实现异步的性能。

互联网中协程可能被用到的场景

  1. 浏览器网页加载发送异步HTTP请求时可能用到了协程。
  2. 淘宝商店界面加载商品信息
  3. 直播界面加载评论和视频流
  4. 贴吧加载新的帖子回复
  5. bilibili异步加载新的回复
  6. 网络游戏中加载各种位置信息
  7. 微信聊天时,需要异步加载和发送信息
  8. 音视频通话异步加载流媒体
  9. chatgpt异步发送和接收问答消息
  10. github的git仓库托管服务器可能使用协程处理用户的push、pull等请求

2. 协程的实现

2.1 setjmp

setjmplongjmp 提供了一种低级的非局部跳转机制,适用于需要在 C 程序中实现复杂控制流或异常处理的情况。但由于它们带来的复杂性和潜在风险,使用时需要小心,确保不会影响程序的可维护性和可读性。

代码示例

#include <setjmp.h>
#include <stdio.h>jmp_buf env1, env2, env3;// coroutine1
void func1(void)
{int cur = 0;int ret = setjmp(env1);if (ret == 0)longjmp(env3, 1);printf("func1: %d [%d]\n", ret, cur++);if (ret < 20){longjmp(env2, ++ret);    }
}// coroutine1
void func2(void)
{int cur = 0;int ret = setjmp(env2);printf("func2: %d [%d]\n", ret, cur++);if (ret < 20){longjmp(env1, ++ret);    }
}int main()
{int ret = setjmp(env3);if (ret == 0)func1();elsefunc2();return 0;
}

从实现代码中可以看到setjmp机制需要我们自己保证协程所在的栈空间已被建立,并且还没有退出。协程所在的函数需要先手动执行,才能进行调度。协程的调度也比较麻烦。

2.2 ucontext

ucontext 是一种用于实现协程和用户态线程的机制。它在一些类 Unix 系统(例如 Linux)中提供了在用户态创建、切换和恢复上下文的接口。ucontext 通过保存和恢复 CPU 寄存器、堆栈指针等状态,允许程序在不同执行流之间切换,适用于实现协程和轻量级任务调度等。

其中保存协程上下文信息的结构体ucontext_t为

#include <ucontext.h>typedef struct ucontext {ucontext_t *uc_link;       // 执行结束后切换到的上下文sigset_t uc_sigmask;       // 信号屏蔽字stack_t uc_stack;          // 栈信息(地址和大小)mcontext_t uc_mcontext;    // 寄存器状态
} ucontext_t;

ucontext API 提供了几个主要函数来创建和切换上下文:

  1. getcontext(ucontext_t *ucp):获取当前上下文并保存到 ucp
  2. setcontext(const ucontext_t *ucp):恢复指定上下文并跳转到该上下文。
  3. makecontext(ucontext_t *ucp, void (*func)(), int argc, ...):为 ucp 配置要执行的函数 func 及其参数。
  4. swapcontext(ucontext_t *oucp, const ucontext_t *ucp):保存当前上下文到 oucp,然后切换到 ucp 上下文。

代码示例

#include <ucontext.h>
#include <stdio.h>ucontext_t ctx[2];
ucontext_t main_ctx;int count = 0;// coroutine1
void func1(void)
{int cur = 0;while (count++ < 20){printf("func1: %d [%d]\n", count, cur++);// yieldswapcontext(&ctx[0], &ctx[1]);}
}// coroutine1
void func2(void)
{int cur = 0;while (count++ < 20){printf("func2: %d [%d]\n", count, cur++);// yieldswapcontext(&ctx[1], &ctx[0]);}
}int main()
{char stack1[2048] = {0};char stack2[2048] = {0};getcontext(&ctx[0]);ctx[0].uc_stack.ss_sp = stack1;ctx[0].uc_stack.ss_size = sizeof(stack1);// 执行完之后跳转的地方ctx[0].uc_link = &main_ctx;makecontext(&ctx[0], func1, 0);getcontext(&ctx[1]);ctx[1].uc_stack.ss_sp = stack2;ctx[1].uc_stack.ss_size = sizeof(stack2);ctx[1].uc_link = &main_ctx;makecontext(&ctx[1], func2, 0);printf("start\n");swapcontext(&main_ctx, &ctx[0]);return 0;
}

ucontext 机制虽然强大,但需要谨慎使用。现代开发中,通常使用其他更高层的协程库,如 libco、libuv 或 Boost.Context 等。

2.3 汇编实现

使用汇编语言来实现协程的切换:主要操作为恢复和保存寄存器的值

int _switch(nty_cpu_ctx *new_ctx, nty_cpu_ctx *cur_ctx);__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"
);

上面的_switch函数实现了协程上下文的切换,和线程切换所作的工作类似

2.4 优缺点

  1. setjmp实现方式复杂,但是跨平台性好
  2. ucontext实现方式简单,但是跨平台性一般
  3. 汇编实现方式复杂,跨平台型差,但是效率高

2.5 实现协程原语

2.5.1 create()

主要工作是创建一个保存协程上下文的数据结构。一个协程的上下文必须包括如下信息:

  • 协程运行的函数和参数信息

  • cpu寄存器上下文

  • 运行时栈上下文

  • 协程状态

  • 协程id

  • 协程所属的调度器

  • 其他信息

一个示例如下:

struct _coroutine_context
{ucontext_t ctx;			// 里面包括寄存器状态和栈上下文proc_coroutine func;	// 协程运行的函数和参数信息void *arg;void *data;coroutine_status status;	// 协程状态scheduler *sched;			// 所属的调度器uint64_t id;
};

创建协程所作的主要工作包括:

  • 分配一个协程上下文并初始化
  • 获取并设置调度器
  • 将改协程加入调度器进行管理
2.5.2 yield()

主要工作是调用swapcontext()或者_switch()切换会协程调度器。

2.5.3 resume()

主要工作是恢复协程的执行。

2.5.4 exit()

主要工作是协程从调度器中删除,然后释放协程上下文。

2.5.5 switch()

协程切换,主要是切换协程的寄存器。

2.5.6 sleep()

让协程停止执行一段时间。

2.6 协程调度器

协程调度器管理协程,包括一个就绪协程队列,一个sleep协程的集合,一个运行时协程队列,一个等待协程集合。可以采用事件机制,当某事件发生时(例如某fd可读),可以将相应的协程从等待集合中取出并恢复执行。

其核心代码如下

while (1)
{// 检查sleep集合,查看是否有协程超时coroutine_context *expired;while ((expired = check_expired(sched))){resume(expired);}// 检查wait结合,查看是否有协程有监听的事件发生coroutine_context *waked;int nready = epoll_wait(epfd, events, EVENTS_SIZE, 1);for (int i = 0; i < nready; ++i){waked = wait_search(events[i].data.fd);resume(waked);}// 恢复ready队列中的协程的运行coroutine_context *rdy;while (!is_ready_empty(sched)){rdt = ready_pop(sched);resyme(rdt);}
}

3. 利用hook使用协程版本的库函数

利用运行时动态链接,可以在运行时将一个函数替换为为使用协程的版本。

例如,以下代码将read函数在运行时替换为了另一个函数:

#include <dlfcn.h>
#include <unistd.h>typedef ssize_t (*readf_t)(int fd, void *buf, size_t count);readf_t readf;void init_hook()
{readf = (readf_t)dlsym(RTLD_NEXT, "read");
}ssize_t read(int fd, void *buf, size_t count)
{if (!readf) init_hook();// 如果对应的fd不可读,那么就挂起协程yield_if_not_ok(fd, POLLIN | POLLERR | POLLHUP);return readf(fd, buf, count);
}

学习参考

学习更多相关知识请参考零声 github。

http://www.ds6.com.cn/news/7151.html

相关文章:

  • dw网站开发环境优秀的网页设计网站
  • 宁德网站推广杭州推广公司排名
  • 网页设计与制作怎么把图片加进去seo关键词排名优化怎么样
  • perl 动态网站开发广州抖音推广公司
  • 网站前端做报名框创建自己的网站
  • 私人做网站收费g3云推广
  • 做门户网站广告市场调研怎么做
  • 南通做网站的公司推广策略怎么写
  • 网站怎么做自适应政府免费培训 面点班
  • 正规的h5网站宁波seo排名外包
  • 得到app公司广州seo排名优化服务
  • 毕业设计做音乐网站可以吗百度收录时间
  • 怎么做像表白墙的网站软文范例200字
  • 教做黏土手工的网站淘宝交易指数换算工具
  • 做网站设计师的感想实事新闻热点
  • wordpress导航图标代码关键词优化排名软件s
  • 辽宁省和城乡建设厅网站网络营销理论
  • 网站开发入门书籍推荐人际网络营销2900
  • 宁波网站建设设计制作方案与价格seopeix
  • 赣州网红打卡旅游景点百度快速优化排名软件
  • 网站首页鲁大师广告投放平台都有哪些
  • 网站关键词推广上海百度搜索排名优化
  • wordpress 添加样式表seo排名的职位
  • 企业咨询顾问的工作内容seo站内优化和站外优化
  • 做水军那些网站好长沙网站seo服务
  • 商务网站内容维护和管理的范围免费一键搭建网站
  • 网站维护提示代码地推公司
  • 如何百度到自己的网站seo管理系统培训运营
  • 做衬衫的网站网络营销经典成功案例
  • 丽水市建设局网站爱站网的关键词是怎么来的