ucontext

概述

ucontext系列允许在C中实现高级控制流模式,例如迭代器,纤维和协程。

主要的4个函数

  • int setcontext(const ucontext_t *ucp)

This function transfers control to the context in ucp. Execution continues from the point at which the context was stored in ucp. setcontext does not return.

  • int getcontext(ucontext_t *ucp)

Saves current context into ucp. This function returns in two possible cases: after the initial call, or when a thread switches to the context in ucp via setcontext or swapcontext. The getcontext function does not provide a return value to distinguish the cases (its return value is used solely to signal error), so the programmer must use an explicit flag variable, which must not be a register variable and must be declared volatile to avoid constant propagation or other compiler optimisations.

  • void makecontext(ucontext_t ucp, void (func)(), int argc, …)

The makecontext function sets up an alternate thread of control in ucp, which has previously been initialised using getcontext. The ucp.uc_stack member should be pointed to an appropriately sized stack; the constant SIGSTKSZ is commonly used. When ucp is jumped to using setcontext or swapcontext, execution will begin at the entry point to the function pointed to by func, with argc arguments as specified. When func terminates, control is returned to ucp.uc_link.

  • int swapcontext(ucontext_t oucp, ucontext_t ucp)

Transfers control to ucp and saves the current execution state into oucp.

内部结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/* sys/ucontext.h */

typedef struct {
void *ss_sp; /* Base address of stack */
int ss_flags; /* Flags */
size_t ss_size; /* Number of bytes in stack */
} stack_t;

struct _libc_fpxreg
{
unsigned short int significand[4];
unsigned short int exponent;
unsigned short int padding[3];
};

struct _libc_xmmreg
{
__uint32_t element[4];
};

struct _libc_fpstate
{
/* 64-bit FXSAVE format. */
__uint16_t cwd;
__uint16_t swd;
__uint16_t ftw;
__uint16_t fop;
__uint64_t rip;
__uint64_t rdp;
__uint32_t mxcsr;
__uint32_t mxcr_mask;
struct _libc_fpxreg _st[8];
struct _libc_xmmreg _xmm[16];
__uint32_t padding[24];
};

/* Structure to describe FPU registers. */
typedef struct _libc_fpstate *fpregset_t;

/* Context to describe whole processor state. */
typedef struct
{
gregset_t gregs;
/* Note that fpregs is a pointer. */
fpregset_t fpregs;
__extension__ unsigned long long __reserved1 [8];
} mcontext_t;


/* 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;

例子

  1. 理解程序是如何跳出循环的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    #include <stdio.h>
    #include <stdlib.h>
    #include <ucontext.h>

    /* The three contexts:
    * (1) main_context1 : The point in main to which loop will return.
    * (2) main_context2 : The point in main to which control from loop will
    * flow by switching contexts.
    * (3) loop_context : The point in loop to which control from main will
    * flow by switching contexts. */
    ucontext_t main_context1, main_context2, loop_context;

    /* The iterator return value. */
    volatile int i_from_iterator;

    /* This is the iterator function. It is entered on the first call to
    * swapcontext, and loops from 0 to 9. Each value is saved in i_from_iterator,
    * and then swapcontext used to return to the main loop. The main loop prints
    * the value and calls swapcontext to swap back into the function. When the end
    * of the loop is reached, the function exits, and execution switches to the
    * context pointed to by main_context1. */
    void loop(
    ucontext_t *loop_context,
    ucontext_t *other_context,
    int *i_from_iterator)
    {
    int i;

    for (i=0; i < 10; ++i) {
    /* Write the loop counter into the iterator return location. */
    *i_from_iterator = i;

    /* Save the loop context (this point in the code) into ''loop_context'',
    * and switch to other_context. */
    swapcontext(loop_context, other_context);
    }

    /* The function falls through to the calling context with an implicit
    * ''setcontext(&loop_context->uc_link);'' */
    }

    int main(void)
    {
    /* The stack for the iterator function. */
    char iterator_stack[SIGSTKSZ];

    /* Flag indicating that the iterator has completed. */
    volatile int iterator_finished;

    getcontext(&loop_context);
    /* Initialise the iterator context. uc_link points to main_context1, the
    * point to return to when the iterator finishes. */
    loop_context.uc_link = &main_context1;
    loop_context.uc_stack.ss_sp = iterator_stack;
    loop_context.uc_stack.ss_size = sizeof(iterator_stack);

    /* Fill in loop_context so that it makes swapcontext start loop. The
    * (void (*)(void)) typecast is to avoid a compiler warning but it is
    * not relevant to the behaviour of the function. */
    makecontext(&loop_context, (void (*)(void)) loop,
    3, &loop_context, &main_context2, &i_from_iterator);

    /* Clear the finished flag. */
    iterator_finished = 0;

    /* Save the current context into main_context1. When loop is finished,
    * control flow will return to this point. */
    getcontext(&main_context1);

    if (!iterator_finished) {
    /* Set iterator_finished so that when the previous getcontext is
    * returned to via uc_link, the above if condition is false and the
    * iterator is not restarted. */
    iterator_finished = 1;

    while (1) {
    /* Save this point into main_context2 and switch into the iterator.
    * The first call will begin loop. Subsequent calls will switch to
    * the swapcontext in loop. */
    swapcontext(&main_context2, &loop_context);
    printf("in main swapcontext %d\n", i_from_iterator);
    // break at loop_context.uc_link when loop is finished
    }
    }

    return 0;
    }
-->