操作系统编程设计:ucontent

Introduction

前面我们介绍了基于setjmp/getjmp控制流的编程方法,下面我们将要介绍更高级的基于ucontent控制流编程方法。通过ucontent,可以实现迭代器(Iterator)、纤程(Fiber)、协程(Coroutine)。

ucontext

首先是最重要的ucontext结构,由context.h或者sys/context.h定义。

1
2
3
4
5
6
7
8
9
10
/* Userlevel context. */
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:当前context退出后,指向下一个要运行的context的指针
uc_stack:用于当前context的栈空间
uc_mcontext:用于保存当前context状态,具体来讲是所有寄存器的值
uc_sigmask:保存阻塞在当前context中信号

makecontext

makecontext的函数原型为:

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

makecontext将当前context修改为ucp指向的context,在使用makecontext前,需要为ucp->uc_stack分配新的栈空间,以及为ucp->uc_link分配下一个context,以指定当前context结束后的下一个context地址。

swapcontext

swapcontext的函数原型为:

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

swapcontext将当前context保存在oucp中,并且执行ucp指向的context。
若swapcontext执行成功,不返回任何值(因为context已经改变),仅执行错误时返回。

getcontext

getcontext的函数原型为:

int getcontext(ucontext_t *ucp);

getcontext将ucp指向的context设置为当前context。
仅当ucp为NULL时,函数会失败。

setcontext

setcontext的函数原型为:

int setcontext(const ucontext_t *ucp);

setcontext将当前context保存到ucp指向的context中。
若setcontext执行成功,不返回任何值(因为context已经改变),仅执行错误时返回。

Example

下面给出一个使用getcontext/setcontext的循环程序:

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <ucontext.h>
int main() {
ucontext_t context;
getcontext(&context);
printf("csprojectedu\n");
setcontext(&context);
return 0;
}

编译运行程序:

$ gcc ucontextdemo.c -o ucontextdemo
$ ./ucontextdemo
csprojectedu
csprojectedu
...

发生了什么?

  1. 当程序运行到getcontext时,将当前程序的context保存到context变量中。
  2. 打印csprojectedu。
  3. 程序运行到setcontext,将当前程序的context置为变量context指向的context。
  4. 由于context被重置为之前设置的地方,因此接下来将要打印csprojectedu。

Summary

通过简单的程序,介绍了ucontext的控制流的程序设计方法。由于makecontext可以指定运行的函数以及参数,因此可以编写出更多有意思的程序。

(本文出自csprojectedu.com,转载请注明出处)