Unix/Linux编程:getcontext、setcontext

article/2025/9/13 0:05:56

ucontext该结构提供了所谓的用户上下文信息,用于描述调用信号处理器函数前的进程状态,其中包括上一个进程信号掩码以及寄存器的保存值,例如程序计数器(cp)和栈指针寄存器(sp),使用结构 ucontext_t 的其他函数有 getcontext()、makecontext()、setcontext()和 swapcontext(),分别对应的功能是允许进程去接收、创建、改变以及交换执行上下文。(这些操作有点类似于 setjmp()和 longjmp(),但更为通用。)可以使用这些函数来实现协程(coroutines),令进程的执行线程在两个(或多个)函数之间交替。SUSv3 规定了这些函数,但将它们标记为已废止。SUSv4 则将其删去,并建议使用 POSIX 线程来重写旧有的应用程序。glibc 手册页提供了关于这些函数的深入信息。

ucontext使得linux程序可以在用户态执行上下文切换,从而避免了进程或者线程切换导致的切换用户空间、切换堆栈,因此,效率相对更高。

结构体

有两个结构体,分别是mcontext_t和ucontext_t,其中mcontext_t是透明的。我们只需要关注ucontext_t就可以了
ucontext_t定义在头文件ucontext.h中,为:

/* Userlevel context.  */
typedef struct ucontext_t{unsigned long int __ctx(uc_flags);struct ucontext_t *uc_link;stack_t uc_stack;mcontext_t uc_mcontext;sigset_t uc_sigmask;struct _libc_fpstate __fpregs_mem;} ucontext_t;

关键字段说明:

  • uc_link:当前context执行结束之后要执行的下一个context,如果uc_link为空,执行完当前context后退出程序。
  • uc_stack:当前上下文所需要的stack。
  • uc_mcontext:保存具体的程序执行上下文,如PC值、堆栈指针和寄存器的值。它的实现依赖于底层,是硬件平台相关的,因此不透明。
  • uc_sigmask:执行上下文过程中,要屏蔽的信号集合,即信号掩码。

stack_t 结构参考这里

函数

int getcontext(ucontext_t *ucp);

初始化ucp结构体,将当前上下文保存在ucp中。成功时,返回0,错误返回-1,并设置errno

int setcontext(const ucontext_t *ucp);

设置当前上下文为ucp所指向的上下文。setcontext的上下文ucp应该通过getcontext或者makecontext取得,如果调用成功则不返回。

  • 如果程序是通过getcontext取得,则程序会继续执行这个调用。
  • 如果context是通过makecontext取得,则程序调用makecontext函数的第二个参数指向的函数,这时,如果函数返回,则恢复ucp->uc_link,如果func函数返回,则恢复makecontext第一个参数指向的上下文第一个参数指向的上下文context_t中指向的uc_link.如果uc_link为NULL,则线程退出。
void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);

makecontext修改通过getcontext取得的上下文ucp(这意味着调用makecontext前必须先调用getcontext)。然后给该上下文指定一个栈空间ucp->stack,设置后继的上下文ucp->uc_link.

当上下文通过setcontext或者swapcontext激活后,执行func函数,argc为func的参数个数,后面是func的参数序列。当func执行返回后,继承的上下文被激活,如果继承上下文为NULL时,线程退出。

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

保存当前上下文到oucp结构体中,然后激活upc上下文。

如果执行成功,getcontext返回0,setcontext和swapcontext不返回;如果执行失败,getcontext,setcontext,swapcontext返回-1,并设置对于的errno.

简单说来, getcontext获取当前上下文,setcontext设置当前上下文,swapcontext切换上下文,makecontext创建一个新的上下文。

例子

#include <ucontext.h>
#include <stdio.h>int done = 0;int main()
{ucontext_t context;getcontext(&context);if (done){printf("return from getcontext,exit\n");return 0;}done = 1;setcontext(&context);printf("finished");return 0;//never goto here!
}

在这里插入图片描述
程序先调用getcontext保存当前寄存器信息到了context中,然后执行setcontext,所谓的setcontext就是把context中的寄存器信息恢复到当前的寄存器信息,也就是说,强制把context的pc bp sp等值,赋值到了当前cpu的寄存中,显而易见的是,这又跳回去到了getcontext 处。

上面这个goto一样,那用goto就行了?那看下面这个例子(test2.c):

#include <ucontext.h>
#include <stdio.h>int done = 0;int func1(ucontext_t *context)
{done = 1;setcontext(context);
}
int main()
{ucontext_t context;getcontext(&context);if (done){printf("return from getcontext,exit\n");return 0;}func1(&context);printf("finished");return 0;
}

在这里插入图片描述
goto 一定做不了函数之间的跳转,只能做本地跳转。当然 getcontet 和 setcontext 肯定不仅仅只有这些功能,看下面这个例子。

//test3.c#include <ucontext.h>
#include <stdio.h>
#include <malloc.h>
void func()
{printf("in func\n");
}int main()
{ucontext_t context;getcontext(&context);//指定栈context.uc_stack.ss_sp = malloc(10000);context.uc_stack.ss_size = 10000;context.uc_link = NULL;makecontext(&context, func, 0);setcontext(&context);printf("finished");return 0;//never goto here!
}

在这里插入图片描述

先setcontext初始化context,然后makecontext,指定跳转的函数,然后再setcontext切换context到func函数。

除此之外,还为新的context指定了新的栈,为什么呢?因为如果不指定栈,那么还是getcontext时获取的sp bp指针,sp bp描述的是main函数的栈大小,如果main函数栈大小是100字节,但是你所要指定的fun的栈大小需要1000字节,显然不够用。

看到这,肯定好多人心里很是疑惑,如果执行完func,还会返回到main吗?答是不会。因为这里没有设置后继上下文。

例子:

#include <ucontext.h>
#include <stdio.h>void func1(void * arg)
{puts("1");puts("11");puts("111");puts("1111");}
void context_test()
{char stack[1024*128];ucontext_t child,main;getcontext(&child); //获取当前上下文child.uc_stack.ss_sp = stack;//指定栈空间child.uc_stack.ss_size = sizeof(stack);//指定栈空间大小child.uc_stack.ss_flags = 0;child.uc_link = &main;//设置后继上下文makecontext(&child,(void (*)(void))func1,0);//修改上下文指向func1函数swapcontext(&main,&child);//切换到child上下文,保存当前上下文到mainputs("main");//如果设置了后继上下文,func1函数指向完后会返回此处
}int main()
{context_test();return 0;
}

在context_test中,创建了一个用户线程child,其运行的函数为func1.指定后继上下文为main
func1返回后激活后继上下文,继续执行主函数。
在这里插入图片描述

例子:

#include <ucontext.h>
#include <stdio.h>
#include <malloc.h>
int did = 0;
void func()
{did = 1;printf("in func\n");
}int main()
{ucontext_t context,rt;getcontext(&context);getcontext(&rt);if(did == 1){printf("continue from func\n");return 0;}//指定栈context.uc_stack.ss_sp = malloc(10000);context.uc_stack.ss_size = 10000;context.uc_link = &rt;makecontext(&context, func, 0);setcontext(&context);return 0;//never goto here!
}

在这里插入图片描述
test4.c比test3.c就多个一个对uc_link的赋值(还有一些流程控制变量)。uc_link就指定了func执行完之后,接着执行的上下文,如果为空,则执行完func后,exit,就像test3.c一样;如果uc_link不为空,则执行完func后,接着执行uc_link就指定的上下文。

就如__start_context逻辑一致。

接下来,看test4.c,我们从getcontext(&rt);处返回了,但是能不能从setcontext处返回呢,当然可以,方法1就是getcontext(&rt);返回后,goto到setcontext 后面。但是glibc还提供了一个接口就是swapcontext。且看下面那个例子。

#include <ucontext.h>
#include <stdio.h>
#include <malloc.h>
int did = 0;
void func()
{did = 1;printf("in func\n");
}int main()
{ucontext_t context,rt;getcontext(&context);//指定栈context.uc_stack.ss_sp = malloc(10000);context.uc_stack.ss_size = 10000;context.uc_link = &rt;makecontext(&context, func, 0);swapcontext(&rt,&context);printf("finally return\n");return 0;//should goto here!
}

swapcontext 两件事,一个就是保存当前的上下文,到rt中,然后切换到context。context指定的上下文执行完成之后,就跳到了swapcontext后面了。

注意:这些xxxcontext函数给予了用户态程序“调度的功能”,这和os的调度不同,os是对各个线程,即task_struct进行切换调度,而各个线程内部,也就是用户态程序,可以用上面这些库函数进行颗粒度更小的调度(说切换更合理)。这就是所谓的协程的概念。


http://chatgpt.dhexx.cn/article/ca3wCkR7.shtml

相关文章

Andriod getContext和getActivity

原创文章&#xff0c;如有转载&#xff0c;请注明出处&#xff1a;http://blog.csdn.net/myth13141314/article/details/62045162 MainActivity.this&#xff1a;表示MainActivity对象&#xff0c;一般用在内部类中指示外面的this&#xff0c;如果在内部类直接用this&#xff…

岭回归模型|机器学习|回归算法

目录 1.岭回归模型1.1背景1.2损失函数 2.相关代码2.1RidgeRegression类2.2求解代码2.3绘图代码 3.直接调库使用 1.岭回归模型 1.1背景 对于回归问题来说&#xff0c;它们的基本内容基本上都是相同的&#xff0c;所以岭回归模型与线性回归模型类似&#xff1a; y θ 0 x 0 …

机器学习学习笔记(13)----岭回归(Ridge回归)

在《机器学习学习笔记&#xff08;4&#xff09;----线性回归的数学解析》&#xff0c;我们通过计算线性模型的损失函数的梯度&#xff0c;得到使得损失函数为最小值的的解析解&#xff0c;被称之为普通最小二乘法&#xff1a; (1) 公式(1)能够求得的前提是是满秩矩阵&#xf…

Python机器学习教程—岭回归的原理和实现

在某些场景下&#xff0c;线性回归无法给出一个效果好的预测模型&#xff0c;那么就需要使用线性回归的升级版&#xff0c;去面对更复杂的应用场景&#xff0c;本文所记录的岭回归便是线性回归的一个升级版。 目录 强势样本对模型的影响 实例 岭回归定义 岭回归的实现 岭…

岭回归和Lasso回归

偏差和方差 机器学习算法针对特定数据所训练出来的模型并非是十全十美的&#xff0c;再加上数据本身的复杂性&#xff0c;误差不可避免。说到误差&#xff0c;就必须考虑其来源&#xff1a;模型误差 偏差&#xff08;Bias&#xff09; 方差&#xff08;Variance&#xff09; …

基于Python的岭回归模型

本文是使用Python进行岭回归模型分析&#xff0c;消除多重共线性 一、岭回归原理 自变量之间存在多重共线性&#xff0c;当时&#xff0c;加上一个正的常数矩阵(K>0)&#xff0c;岭回归估计定义公式&#xff1a; &#xff08;k为岭参数&#xff09; 的奇异程度比奇异程度小&…

用SPSS进行岭回归分析

打开SPSS将数据输入到SPSS中。 点击左上角文件->新建->语法 输入 * Encoding: UTF-8. INCLUDE D:\Program Files\IBM\SPSS\Statistics\27\Samples\Simplified Chinese\Ridge regression.sps. ridgereg enter x1 x2 x3 x4 x5 x6 x7 x8 x9 x10/depy.include内容需要自己寻找…

R语言与岭回归

岭参数的一般选择原则 选择k&#xff08;或lambda&#xff09;值&#xff0c;使得&#xff1a;各回归系数的岭估计基本稳定用最小二乘估计时符号不合理的回归系数&#xff0c;其岭回归的符号变得合理回归系数没有不合乎实际意义的绝对值残差平方和增大的不多 用R语言进行岭回归…

岭回归(Ridge Regression)及实现

岭回归(Ridge Regression)及实现 https://blog.csdn.net/google19890102/article/details/27228279 一、一般线性回归遇到的问题 在处理复杂的数据的回归问题时&#xff0c;普通的线性回归会遇到一些问题&#xff0c;主要表现在&#xff1a; 在处理复杂的数据的回归问题时&…

岭回归(R语言)

代码实现如下&#xff1a; data3.3<-read.csv("C:/Users/Administrator/Desktop/data3.3.csv",headTRUE) datas<-data.frame(scale(data3.3[,1:6])) # 对样本数据进行标准化处理并转换为数据框的存储格式 library(MASS) ridge3.3<-lm.ridge(y~.-1,datas,l…

多元线性回归-岭回归

目录 1.精确相关关系 2.高度相关关系 3.多重共线性与相关性 4.岭回归 5.linear_model.Ridge 5.1.案例1&#xff1a;加利福尼亚房屋价值数据 5.2.案例2:波士顿房价数据集 6.选取最佳正则化参数取值 1.精确相关关系 精确相关关系&#xff0c;即完全相关。如矩阵A并不是满…

回归——岭回归

1、作用 岭回归是一种专用于共线性数据分析的有偏估计回归方法&#xff0c;实质上是一种改良的最小二乘估计法&#xff0c;通过放弃最小二乘法的无偏性&#xff0c;以损失部分信息、降低精度为代价获得回归系数更为符合实际、更可靠的回归方法&#xff0c;对病态数据的拟合要强…

【机器学习04】岭回归

4 岭回归 4.1 简介 普通线性回归模型使用基于梯度下降的最小二乘法&#xff0c;在最小化损失函数的前提下&#xff0c;寻找最优模型参数&#xff0c;在此过程中&#xff0c;包括少数异常样本在内的全部训练数据都会对最终模型参数造成程度相等的影响&#xff0c;异常值对模型…

机器学习——岭回归

岭回归的简单介绍 什么是岭回归&#xff1f;什么时候要用到岭回归&#xff1f;岭回归是一种解决标准方程法不能求逆矩阵时的办法。我们都知道&#xff0c;用标准方程法最大的一个缺点就是当数据的特征多于数据的样本时&#xff0c;标准方程法是不能使用的&#xff0c;因为不能…

数学建模学习:岭回归和lasso回归

线性回归 在多元线性回归模型中&#xff0c;估计回归系数使用的是OLS&#xff0c;并在最后讨论异方差和多重共线性对模型的影响。事实上&#xff0c;回归中自变量的选择大有门道&#xff0c;变量过多可能会导致多重共线性问题导致回归系数不显著&#xff0c;甚至造成OLS估计失…

python机器学习| 岭回归介绍及实现

岭回归介绍及实现 1 岭回归的引入2 岭回归的原理2.1 原理介绍2.2 原理代码实现 3 API 实现 1 岭回归的引入 在线性回归-正规方程和梯度下降中&#xff0c;我们介绍了基于正规方程或者梯度下降的优化算法&#xff0c;来寻找最优解。 在正规方程解中&#xff0c;它是基于直接求导…

岭回归与lasso回归算法

模型压缩与正则化主要包含岭回归&#xff08;Ridge regression&#xff09;和Lasso两种方法&#xff0c;二者的主要原理是将系数往等于0的方向压缩。 岭回归 lasso 全称&#xff1a;Least absolute shrinkage and selection operator最小绝对缩减和选择算子 一、岭回归示…

岭回归-回归实操

python 岭回归算法之回归实操 基本概念 正则化 正则化是指对模型做显式约束&#xff0c;以避免过拟合。本文用到的岭回归就是L2正则化。&#xff08;从数学的观点来看&#xff0c;岭回归惩罚了系数的L2范数或w的欧式长度&#xff09; 正则化的具体原理就不在这里多叙述了&a…

线性回归与岭回归

目录 一、什么是线性回归 1.线性回归简述 2.数组和矩阵 3.线性回归的算法 二、权重的求解 1.正规方程 2.梯度下降 三、线性回归案例 1.案例概述 2.数据获取 3.数据分割 4.数据标准化 5.模型训练 6.回归性能评估 7.梯度下降与正规方程区别 四、岭回归Ridge 1.过拟…

什么是岭回归?

问题引出&#xff1a; 当使用最小二乘法解决一下线性回归&#xff1a; 假设:已知x1,x2与y的关系服从线性回归模型&#xff1a; y102x13x2c 当x1和x2存在线性相关性时&#xff0c;使用最小二乘法来预测回归模型&#xff0c;就变得困难起来&#xff0c;因此物品们必须使用逐步回…