makecontext, swapcontext -
ユーザーコンテキストを操作する
#include <ucontext.h>
void makecontext(ucontext_t *ucp, void
(*func)(), int argc, ...);
int swapcontext(ucontext_t *oucp, const ucontext_t
*ucp );
In a System V-like environment, one has the type
ucontext_t (defined in
<ucontext.h> and described in
getcontext(3)) and the four
functions
getcontext(3),
setcontext(3),
makecontext(),
and
swapcontext() that allow user-level context switching between
multiple threads of control within a process.
makecontext()
関数は、ポインター
ucp
が指すコンテキストを変更する
(
ucp は以前の
getcontext(3)
呼び出しで得られたものである)。
makecontext()
を起動する前には、呼び出し者は、このコンテキスト用に
新しいスタックを確保し、そのアドレスを
ucp->uc_stack に代入し、
さらに後継のコンテキストを定義し、そのアドレスを
ucp->uc_link に
代入しなければならない。
このコンテキストが将来
(
setcontext(3) または
swapcontext()
によって)
有効にされると、関数
func が呼ばれ、
引数として
argc
以降の整数 (
int)
引数の列が渡される。
呼び出し者は
argc
にこれらの引数の個数を指定しなければならない。
この関数が戻ると、後継のコンテキストが有効になる。
後継コンテキストのポインターが
NULL
の場合、そのスレッドが終了する。
swapcontext()
関数は現在のコンテキストを
ポインター
oucp
が指す構造体に保存し、
ポインター
ucp
が指すコンテキストを有効にする。
成功すると、
swapcontext()
は返らない
(しかし後に
oucp
が有効になった場合には返ることがある。
このときには
swapcontext() は
0
を返すように見える。)
失敗すると、
swapcontext() は
-1 を返し、
errno
をエラーに応じて設定する。
- ENOMEM
- スタックに割り当てる空間が残っていない。
makecontext() と
swapcontext()
は、バージョン 2.1
以降の glibc
で提供されている。
この節で使用されている用語の説明については、
attributes(7) を参照。
インターフェース |
属性 |
値 |
makecontext() |
Thread safety |
MT-Safe race:ucp |
swapcontext() |
Thread safety |
MT-Safe race:oucp race:ucp |
SUSv2, POSIX.1-2001. POSIX.1-2008
では、移植性の問題から
makecontext() と
swapcontext()
の仕様が削除されている。
代わりに、アプリケーションを
POSIX
スレッドを使って書き直すことが
推奨されている。
ucp->uc_stack の解釈は
sigaltstack(2)
の場合と同じである。
すなわちこの構造体には、
スタックとして用いられるメモリー領域の開始アドレスと長さが含まれ、
これはスタックが伸びる方向がどちらであるかには関係しない。
したがって、ユーザープログラムはこの件については心配しなくてよい。
int
とポインター型が同じ大きさであるアーキテクチャーでは
(x86-32
はその例であり、両方の型とも
32 ビットである)、
makecontext() の
argc
以降の引数としてポインターを渡してもうまく動くかもしれない。
しかしながら、このようにすると、移植性は保証されず、
標準に従えば動作は未定義であり、ポインターが
int
よりも大きいアーキテクチャーでは正しく動作しないことだろう。
それにも関わらず、バージョン
2.8 以降の glibc では、
makecontext()
に変更が行われ、(x86-64
などの) いくつかの 64
ビットアーキテクチャーで
引数としてポインターを渡すことができるようになっている。
以下のサンプルプログラムは、
getcontext(3),
makecontext(),
swapcontext()
の使用方法の例を示すものである。
このプログラムを実行すると、以下のような出力が得られる:
$ ./a.out
main: swapcontext(&uctx_main, &uctx_func2)
func2: started
func2: swapcontext(&uctx_func2, &uctx_func1)
func1: started
func1: swapcontext(&uctx_func1, &uctx_func2)
func2: returning
func1: returning
main: exiting
#include <ucontext.h>
#include <stdio.h>
#include <stdlib.h>
static ucontext_t uctx_main, uctx_func1, uctx_func2;
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
static void
func1(void)
{
printf("func1: started\n");
printf("func1: swapcontext(&uctx_func1, &uctx_func2)\n");
if (swapcontext(&uctx_func1, &uctx_func2) == -1)
handle_error("swapcontext");
printf("func1: returning\n");
}
static void
func2(void)
{
printf("func2: started\n");
printf("func2: swapcontext(&uctx_func2, &uctx_func1)\n");
if (swapcontext(&uctx_func2, &uctx_func1) == -1)
handle_error("swapcontext");
printf("func2: returning\n");
}
int
main(int argc, char *argv[])
{
char func1_stack[16384];
char func2_stack[16384];
if (getcontext(&uctx_func1) == -1)
handle_error("getcontext");
uctx_func1.uc_stack.ss_sp = func1_stack;
uctx_func1.uc_stack.ss_size = sizeof(func1_stack);
uctx_func1.uc_link = &uctx_main;
makecontext(&uctx_func1, func1, 0);
if (getcontext(&uctx_func2) == -1)
handle_error("getcontext");
uctx_func2.uc_stack.ss_sp = func2_stack;
uctx_func2.uc_stack.ss_size = sizeof(func2_stack);
/* Successor context is f1(), unless argc > 1 */
uctx_func2.uc_link = (argc > 1) ? NULL : &uctx_func1;
makecontext(&uctx_func2, func2, 0);
printf("main: swapcontext(&uctx_main, &uctx_func2)\n");
if (swapcontext(&uctx_main, &uctx_func2) == -1)
handle_error("swapcontext");
printf("main: exiting\n");
exit(EXIT_SUCCESS);
}
sigaction(2),
sigaltstack(2),
sigprocmask(2),
getcontext(3),
sigsetjmp(3)
この man ページは Linux
man-pages
プロジェクトのリリース
5.10
の一部である。プロジェクトの説明とバグ報告に関する情報は
https://www.kernel.org/doc/man-pages/
に書かれている。