C语言实现协程(一)

2023-10-27

目录

引言

在使用socket编程时,我们会用到accept、connect、recv、send等函数,这些函数在没有数据到达时,会阻塞等待IO数据的到达。这不利于我们处理多个连接并快速响应。一种方案是,服务端每accept一个连接,就创建一个新的线程用来处理这个连接。这会导致线程过多,而且线程之前切换开销很大。这就可以使用到协程了。当然不止socket这种可以使用协程,IO密集型都可以使用协程,无论是网络IO还是其他IO。

协程

协程可以理解为用户态的轻量级的非抢占式的线程。

特点

用户态:协程是在用户态实现调度。
轻量级:协程不用内核调度,内核态与用户态之间切换。
非抢占:协程是由用户自己实现调度,并且同一时间只能有一个协程在执行,协程自己主动交出CPU的。

优缺点

优点:

  1. 协程切换的时候开销小,用户态且轻量
  2. 非抢占式,不用加很多锁,减小复杂度,不用很复杂的处理线程同步问题。
    缺点:
    协程不能利用多核,只能使用单核,因为同时只有一个协程在运行。

适用场景

IO密集型。
在IO密集的情况下,协程的开销比线程小,能快速实现调度。
协程不适用于计算密集型,协程不能很好的利用多核cpu。

ucontext组件

linux下在头文件< ucontext.h >提供了getcontext(),setcontext(),makecontext(),swapcontext()四个函数和mcontext_t和ucontext_t结构体。

其中mcontext_t与机器相关。ucontext_t结构体如下(一般在/usr/include下):

typedef struct ucontext
{
    unsigned long int uc_flags;
    struct ucontext *uc_link;
    stack_t uc_stack;
    mcontext_t uc_mcontext;
    __sigset_t uc_sigmask;
    struct _libc_fpstate __fpregs_mem;
} ucontext_t;

其中uc_link指向下文,及当前上下文(可以理解为执行状态)执行完了,恢复运行的上下文;
uc_sigmask为该上下文中的阻塞信号集合;
uc_stack为该上下文中使用的栈;
uc_mcontext保存的上下文的特定机器表示,包括调用线程的特定寄存器等。
通过栈式计算机原理,我们可以知道,保存栈区和所有通用寄存器的值就可以保存程序运行的状态。这里uc_mcontext就是用来保存所有的寄存器的值的。而我们把栈设置到uc_stack所指向的内存,
uc_stack就保存了栈的状态。

ucontext的4个函数介绍

int getcontext(ucontext_t *ucp);

获取当前上下文,初始化ucp结构体,将当前的上下文保存到ucp中。如果执行成功,返回0。执行失败返回-1。

int setcontext(const ucontext_t *ucp);

设置当前上下文,设置当前的上下文为ucp, 恢复ucp的执行状态。如果ucp执行完了,会恢复到uc_link所指向的上下文,若uc_link为NULL,则线程退出。如果执行成功,不返回。执行失败返回-1。

void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);

创建上下文,修改通过getcontext取得的上下文ucp, 然后给该上下文指定一个栈空间ucp->stack,设置后继的上下文ucp->uc_link。

int swapcontext(ucontext_t *oucp, ucontext_t *ucp);

切换上下文,保存当前上下文到oucp结构体中,然后激活upc上下文。 如果执行成功,不返回。执行失败返回-1。

ucontext简单试用

下面一段简单代码体验一下获取和恢复上下文:

#include <stdio.h>
#include <ucontext.h>
#include <unistd.h>

int main(int argc, const char *argv[]){
	ucontext_t context;
	
	getcontext(&context);
	puts("Hello world");
	sleep(1);
	setcontext(&context);
	return 0;
}

保存并编译,执行结果:

Hello world  
Hello world  
Hello world  
Hello world  
Hello world  
Hello world  
Hello world  
^C  

如果不主动ctrl+c结束程序,会不断执行下去。第8行哪里getcontext保存了上下文,相当于按了一个暂停并保存起来放到了context变量里面,程序继续执行,到第11行的时候,恢复了原来的上下文,相当于把context变量里面保存的东西恢复了,恢复到第8行的状态。所以程序会不断执行下去。

函数之前切换

下面一段简单的代码体验一下不同函数间切换和恢复上下文:

#include <stdio.h>
#include <ucontext.h>
#include <unistd.h>


void fun1()
{
    printf("func1\n");
}
void fun2(ucontext_t *ctx)
{
    ucontext_t t_ctx;
    printf("func2\n");
    swapcontext(&t_ctx, ctx);
}

int main(int argc, char *argv[]) {
    ucontext_t context, context2, main_ctx;
    char stack[1024];                                                                                                                         
    getcontext(&context);
    getcontext(&context2);
    context.uc_stack.ss_sp = stack;
    context.uc_stack.ss_size = 1024;
    context.uc_link = &main_ctx;
    context2.uc_stack.ss_sp = stack;
    context2.uc_stack.ss_size = 1024;
    makecontext(&context,(void (*)(void))fun1, 0);
    swapcontext(&main_ctx, &context);
    puts("main");
    makecontext(&context2,(void (*)(void))fun2,1,&main_ctx);
    swapcontext(&main_ctx, &context2);
    puts("main over");
    return 0;
}

执行结果:

func1
main
func2
main over

这里第28行,先保存上下文到main_ctx,切换到context上下文执行,就调用了fun1函数,fun1函数执行完后恢复uc_link指向的上下文main_ctx。然后接着执行输出了main。第31行保存上下文到main_ctx恢复context2,执行fun2函数,并传一个函数main_ctx,执行到14行后保存上下文到gCtx,然后恢复ctx(ctx是通过指针传过来的mainc_ctx);即恢复到了32行的状态最后main函数执行完成。

总结

linux系统,为我们提供了获取当前执行状态的函数、恢复当前上下文的函数和构造当前上下文的函数,所有栈的区域我们都可以自己分配一段空间来做协程的栈,每个协程都有自己独立的空间,我们就可以自己在用户态实现上线文切换,实现一个“用户态的线程”,即协程。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言实现协程(一) 的相关文章

随机推荐

  • 谈谈从小公司进入大厂,我都做对了哪些事?

    故事得从19年的那个秋天说起 同事小丽给俺发了条消息 俺对象进城了 纳尼 你对象从乡下来了 不是 俺对象去大公司了 哦哦 这么个意思啊 你对象真棒 考官都问什么了 我看看 于是俺也蠢蠢欲动 开启了大厂面试的征程 下面是俺的经验 可以给诸位借
  • 从入门到精通真不难:Python最佳学习路线(学习教程)分享

    随着人工智能时代的来临 Python开始崭露头角并迅速吸引了人们的广泛关注 很多人想要从事Python开发 但需要学什么内容 怎么快速学习呢 接下来就给大家分享Python最佳学习路线 帮你快速找准自己定位 第一阶段Python基础与Lin
  • 【Spring Cloud Alibaba】链路监控 Sleuth+Zipkin

    Spring Cloud Alibaba 链路监控 Sleuth Zipkin 1 Zipkin 2 项目集成 3 持久化 4 Kafka数据传输 4 1 环境搭建 4 2 Zipkin 配置 5 采样策略 5 1 采样率策略 5 2 抽样
  • 关于java RSA密钥的长度问题

    最近在搞udp可靠通信 不单单是丢失重传 为了进行密钥传输学习一下密钥长度的一些知识 mark一下 java默认的rsa填充方案为RSA ECB PKCS1Padding 一般说的rsa密钥长度单位是bit 本文所有长度单位均为byte 除
  • 网络编程技术简介

    网络编程技术简介 文章目录 网络编程技术简介 什么是网络编程 传统的网络编程技术简介 NET传统网络编程 WCF编程技术 套接字编程 Socket类 套接字有3种不同的类型 特点 TCP应用编程 TcpClient类 TcpListener
  • c语言浮点型与整形比较大小,C语言整形与浮点型转化过程中的精度损失

    本篇文章主要解决的问题是C语言整形与浮点型转化过程中的精度损失从而容易产生bug 对于浮点和整形的相关内容大家可以参考下面两篇文章 这里就不再赘叙了 1整形与浮点转化的精度损失参考小程序 1 include 2 include 3 4 5
  • bugku Flask_FileUpload

    进去是上传一个文件 ctrl u看一下 只允许上传jpg和png文件 提示说上传的文件会一python执行 那就构建一个名为4 jpg的文件 内容为 import os os system cat flag os system用来执行cmd
  • pytorch中model.train和model.eval

    model eval 因为自定义的网络以及自定义的网络中的各个层都继承于nn Module这个父类nn Module存在一个training的属性 默认为True 所以 model eval 使得自定义的网络以及自定义的网络中的各个层的tr
  • 嵌入式GUI盘点-你了解几款?

    图形用户界面 Graphical User Interface 简称 GUI 又称图形用户接口 是指采用图形方式显示的计算机操作用户界面 GUI一般需要MCU有一定的硬件资源和处理能力 现在MCU资源越来越多 同时市场需求也增多了 GUI功
  • PostgreSQl 语言篇

    PostgreSQl SQL语言 概念 pgsql是关系型数据库管理系统 关系本质上是表的数学表达 表是行的集合 同一个表中每行都有相同的列 每个列都是一个特定的数据类型 每列都有固定的顺序 但是无序的 表被分组存放到数据库 由单个pgsq
  • 敏捷软件测试常见的七个误区【你确定都知道】

    敏捷软件测试常见的七个误区 敏捷软件开发是从1990年代开始逐渐引起广泛关注的一种新型软件开发方法 是能够应对快速变化的需求的一种软件开发能力 它作为一种新型的开发模式 被越来越多地应用到软件项目中 敏捷软件测试指的是在敏捷软件开发过程中跟
  • 记录一次u-rate不生效的问题

    今天在写一个小程序项目的时候 使用u rate评分组件时 一直不显示 在测试uView是否正常引入 以及各个的引入是否有错外 在全部排查了之后 任何没有找到解决方案 于是我在想会不会是u rate组件中的某个属性导致了渲染失败 于是我在查看
  • ⚡文件工具类⚡

    目录 文件工具类的介绍 文件工具类的功能简介 stat接口 获取文件的大小 判断文件是否存在 获取文件最后一次进入时间 获取文件最后一次修改时间 获取文件名 获取文件的内容 往文件中写入内容 获取目录下所有文件的相对路径 压缩文件 解压文件
  • 深拷贝与浅拷贝【JavaScript基础面试题】

    浅拷贝 对象的浅拷贝 Object assign 使用 Object assign 该函数会拷贝assign 的第二个参数 比如下例中 我们会拷贝第二个参数 也就是obj1 const obj1 a 1 b 2 const obj2 Obj
  • Verilog 条件语句if else

    注意 只能在initial或always过程块语句中使用 生成latch 在时序逻辑中 不完整的 if else 结构并不会生成锁存器 而组合逻辑中不完整的 if else 结构就会生成锁存器 所以时序逻辑可以没有最后的else 默认保持
  • qt creator解决win10下显示界面过大的问题

    最终解决方法 https blog csdn net qq 43373204 article details 117166794 这种方法只能当时生效一次 https www jianshu com p 0368e5705c51
  • 鼠标移动效果

  • CSS中position属性( absolute

    本文为CSDN博主 夜之子 的原创文章 原文链接 https blog csdn net chen zw article details 8741365 我们先来看看CSS3 Api中对position属性的相关定义 static 无特殊定
  • Ubuntu的Linux下warning: implicit declaration of function ‘fork’ [-Wimplicit-function-declaration]解决方案

    一 问题描述 要编译运行的文件 include
  • C语言实现协程(一)

    目录 基本原理 协程管理 协程实现 引言 在使用socket编程时 我们会用到accept connect recv send等函数 这些函数在没有数据到达时 会阻塞等待IO数据的到达 这不利于我们处理多个连接并快速响应 一种方案是 服务端