presentation template design-9 - 東京大学tau/lecture/...静的優先順位 +...
TRANSCRIPT
スケジューラについての調査
03-120480島津 真人
はじめに
● いろいろなスケジューラの話をします
● 細かいこととか、技術的なことを tumblr に少しまとめてたりします
http://amiq11.tumblr.com
今回の分も きちんとまとめたいです ここからいける
スケジューラとは
● 沢山仕事するときに、どれを実行するか選ぶための機構
– プロセス・スレッド => タスク
● ハードウェアは決まっている
– 例えば、 CPU が 2 コア 4 スレッドとか
● NG パターン
– 飢餓状態 ( starvation )
● ずっとタスクが実行されないままになること
– IO bound なものと CPU bound なものの区別
● 同等だとどういうことになるか?
スケジューラの種類
● FIFO / RR
● O(1)
● CFS ( Completely Fair Scheduler )
● BFS ( Brain Fuck Scheduler )
● RM ( Rate Monotonic )
● DM ( Deadline Monotonic )
● EDF ( Earliest Deadline First )
● ポリシー
– システムコール sched_setscheduler で変更可
– man 見て
O(1)
● kernel 2.6 から
● 本とかはめっちゃある
– 詳解 Linux カーネルとか
● タスクを優先度ごとのキュー (= runqueue,rq ) に並べる
– active/expired
● 頭から一個取ってくるだけ→ O(1)
O(1)
● 静的優先順位 + 動的優先順位 (nice)
● 優先順位に応じて、実行できる時間が決まってる
● nice を決めるときには
– IO bound と CPU bound
– これまでの動きから予測する
CFS - 概要
● kernel 2.6.23 から
● O(1) はややこしいし予測にも失敗する
● 単純に、「全てのタスクに同じ CPU 時間」を割り当てる
● 実行時間: vruntime
– 理想的には全てのタスクの vruntime は常に等しい
CFS - 概要
● 単純にタイムスライスを切って分ける
– cat /proc/sys/kernel/sched_latency_ns
– (default: 6ms * (1 + ilog(ncpus)), units: nanoseconds) @fair.c l.36
– この期間中に1回は動く
CFS - 概要
● ただ、単純と言いつつ実はちょっと面倒なことをしている
– 眠っていたときの処理
● vruntime を一番小さいやつに合わせる
– すぐ追いついちゃったときの処理
● unsigned int sysctl_sched_wakeup_granularity = 1000000UL;
– cat /proc/sys/kernel/sched_wakeup_granurarity_ns– next-buddy (preempt failure) / last-buddy (cache hot) ルール
– スライスしすぎちゃったときの処理
● min_granularity_ns
– cat /proc/sys/kernel/sched_min_granularity_ns– (default: 0.75 msec * (1 + ilog(ncpus)), units: nanoseconds) @fair.c
l.65
CFS – 実装
● 次のタスクを選ぶ関数
– pick_next_task
– スケジューリングクラスごとにある
static struct task_struct *pick_next_task_fair(struct rq *rq){
struct task_struct *p;struct cfs_rq *cfs_rq = &rq->cfs;struct sched_entity *se;
if (!cfs_rq->nr_running)return NULL;
do {se = pick_next_entity(cfs_rq);set_next_entity(cfs_rq, se);cfs_rq = group_cfs_rq(se);
} while (cfs_rq);
p = task_of(se);if (hrtick_enabled(rq))
hrtick_start_fair(rq, p);
return p;}
CFS – 実装
● 次のタスクを選ぶ関数
– pick_next_task
– スケジューリングクラスごとにある
● すっきりしてるけど闇は深いよ?
static struct task_struct *pick_next_task_fair(struct rq *rq){
struct task_struct *p;struct cfs_rq *cfs_rq = &rq->cfs;struct sched_entity *se;
if (!cfs_rq->nr_running)return NULL;
do {se = pick_next_entity(cfs_rq);set_next_entity(cfs_rq, se);cfs_rq = group_cfs_rq(se);
} while (cfs_rq);
p = task_of(se);if (hrtick_enabled(rq))
hrtick_start_fair(rq, p);
return p;}
CFS - 実装
● rq とかいいつつ赤黒木 (O(logn))
● キーは vruntime → 左端が一番動いてないやつ
● 赤黒木って?
– 赤黒木は平衡二分木のひとつ
– 黒の数が全ての経路で同じ , 赤は続かない
– 深さは一番差があるときで 2倍
– 回転はちょっと面倒
全体の実装
● スケジューラ全体の実装について
● 重要な構造体は以下
– task_struct→ プロセスの情報管理
– sched_xx_entity
● →xx = なし (CFS) / rt (FIFO/RR) / dl (DEADLINE)
→xx に対応したスケジューラの情報管理 (vruntime とか )
– sched_class→ スケジューラのポリシー自体
● 関数ポインタ群
全体の実装
● task_struct
– タスクに関する情報
– 4kB近くあったり
struct task_struct {volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */void *stack;atomic_t usage;unsigned int flags; /* per process flags, defined below */unsigned int ptrace;int prio, static_prio, normal_prio;const struct sched_class *sched_class;struct sched_entity se;struct sched_rt_entity rt;struct sched_dl_entity dl;pid_t pid;pid_t tgid;struct timespec start_time; /* monotonic time */struct timespec real_start_time; /* boot based time */struct css_set __rcu *cgroups;
…
全体の実装
● task_struct
– タスクに関する情報
– 4kB近くあったり
struct task_struct {volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */void *stack;atomic_t usage;unsigned int flags; /* per process flags, defined below */unsigned int ptrace;int prio, static_prio, normal_prio;const struct sched_class *sched_class;struct sched_entity se;struct sched_rt_entity rt;struct sched_dl_entity dl;pid_t pid;pid_t tgid;struct timespec start_time; /* monotonic time */struct timespec real_start_time; /* boot based time */struct css_set __rcu *cgroups;
…
int pdeath_signal; /* The signal sent when the parent dies */unsigned int jobctl; /* JOBCTL_*, siglock protected *//* ??? */unsigned int personality;unsigned did_exec:1;unsigned in_execve:1; /* Tell the LSMs that the process is doing an
* execve */
全体の実装
● sched_entity
– 各種のスケジューリングクラスに対応したデータを保持
struct sched_entity {struct load_weight load; /* for load-balancing */struct rb_node run_node;struct list_head group_node;unsigned int on_rq;
u64 exec_start;u64 sum_exec_runtime;u64 vruntime;u64 prev_sum_exec_runtime;
u64 nr_migrations;
#ifdef CONFIG_SCHEDSTATSstruct sched_statistics statistics;
#endif
#ifdef CONFIG_FAIR_GROUP_SCHEDstruct sched_entity *parent;/* rq on which this entity is (to be) queued: */struct cfs_rq *cfs_rq;/* rq "owned" by this entity/group: */struct cfs_rq *my_q;
#endif};
全体の実装
● sched_xx_class
– dl_sched_class
const struct sched_class dl_sched_class = {.next = &rt_sched_class,.enqueue_task = enqueue_task_dl,.dequeue_task = dequeue_task_dl,.yield_task = yield_task_dl,
.check_preempt_curr = check_preempt_curr_dl,
.pick_next_task = pick_next_task_dl,
.put_prev_task = put_prev_task_dl,
#ifdef CONFIG_SMP.select_task_rq = select_task_rq_dl,
.set_cpus_allowed = set_cpus_allowed_dl,
.rq_online = rq_online_dl,
.rq_offline = rq_offline_dl,
.pre_schedule = pre_schedule_dl,
.post_schedule = post_schedule_dl,
.task_woken = task_woken_dl,#endif
.set_curr_task = set_curr_task_dl,
.task_tick = task_tick_dl,
.task_fork = task_fork_dl,
.task_dead = task_dead_dl,
.prio_changed = prio_changed_dl,
.switched_from = switched_from_dl,
.switched_to = switched_to_dl,};
実装● リスト構造
● pick_next_task あたりを見るとタスクをどう選ぶかわかりやすい
実装● リスト構造
● pick_next_task あたりを見るとタスクをどう選ぶかわかりやすい
● schedule() を呼ぶと次のタスクを選ぶ
static inline struct task_struct *pick_next_task(struct rq *rq){
const struct sched_class *class;struct task_struct *p;
/* * Optimization: we know that if all tasks are in * the fair class we can call that function directly: */if (likely(rq->nr_running == rq->cfs.h_nr_running)) {
p = fair_sched_class.pick_next_task(rq);if (likely(p))
return p;}
for_each_class(class) {p = class->pick_next_task(rq);if (p)
return p;}
BUG(); /* the idle class will always have a runnable task */}
全体の実装
● hrtick
– high resolution tick
– 全体の tick( .config でHZ
として定義 ) と異なる
– 10us制限あり
– 時間が来ると schedule を起こす
static struct task_struct *pick_next_task_fair(struct rq *rq){
struct task_struct *p;struct cfs_rq *cfs_rq = &rq->cfs;struct sched_entity *se;
if (!cfs_rq->nr_running)return NULL;
do {se = pick_next_entity(cfs_rq);set_next_entity(cfs_rq, se);cfs_rq = group_cfs_rq(se);
} while (cfs_rq);
p = task_of(se);if (hrtick_enabled(rq))
hrtick_start_fair(rq, p);
return p;}
実際に使ってみた - 方法
● 評価方法
– 右のようなプログラム (load.c) を用意する
– これの優先順位を適当にいじったときの、タスクのスイッチングを確認する
– 確認には trace-cmd を使う
● trace-cmd record -e sched_switch● trace って ?
– カーネルが提供するログ– mount -t debugfs nobody /proc/sys/debug– cat /proc/sys/debug/tracing/trace
– kernelshark を用いて表示
● まず1コア1スレッドの仮想マシン (kvm)上で動かす
– 3.7.0-rc6-sched-deadline-mainline-dl
#include <stdio.h>void main( void ){ volatile int i = 0; printf( "Load Program\n" ); printf( "Start!\n" ); while ( 1 ) i++;
}
実際に使ってみた – CFS
● CFS x3 nice は 10
実際に使ってみた – CFS
● CFS x3 nice が 5/10/15
– CFS における nice → vruntime の重み付け (1 につき 1.25倍 → 1.25^5 = 3.05)
実際に使ってみた – CFS
● 普段の状況はこんな感じ
CFS
● CFS を大学生に例えると ...?
● 状況
– OS の発表
– と BDM (びっくり★どっきり☆メカ )
– と実験レポ
– と試験勉強をしないといけない
CFS
● とりあえず OS の発表準備する
● ある程度進めた後、違うタスクを始める
● とりあえず BDM の準備する
● ある程度進めた後、違うタスクを始める
● …
CFS
● 発表当日
進捗は・・・
● OS の準備 50%
● BDM の準備 50%
● 実験レポの準備 50%
● 試験勉強 50%
実時間 (RealTime)
● 発表に間に合ってない!!!!
– けど試験勉強も進んでる
● なぜ?
– 締め切りの存在
– 締め切りがあるようなタスクには不向き
● ロボットの制御など
– マップの更新 /モータの制御 (1ms ごと /10ms ごととか )
– 逆に画像を用いた補正などはゆっくりでいい
実時間
● ロボットだとどういう感じか
– 10ms に一度
● センサ情報の更新 ( 例:速度 v )● 自分の位置の計算 (例: pos=∫vdt )● モータ出力の更新 (例: V(電圧) = A*diff )
– カメラからの画像がとれたら
● 自己位置の補正
● モータ出力が遅れると?
– 例:画像処理が遅くて固まる
FIFO/RR
● じゃあ先にやるべきものをとりあえずやってしまおう
● 最も原始的
● FIFO はキューの頭のやつを終わるまでやる
● RR は優先度が同じやつを決まった時間 (time slice) ごとにかわりばんこにやる
FIFO/RR
● 例えばさっきの例で言えば ...
● 先に OS の発表準備を終わらせる
● 終わったら実験レポも終わらせる
● その次に BDM を完成
● 最後に試験勉強
FIFO/RR
● ダメな点 1
– 簡単に飢餓
● ダメな点 2
– じつは上手に終わらない例がある
FIFO/RR
● 例えば、 OS の発表の準備中に急にサークルの友人から電話がかかってくる
– 「回路製作の締め切りが日曜になりました!よろしく!!!」
● でも自分はすでに OS の発表準備をしている
FIFO/RR
● 例えば、 OS の発表の準備中に急にサークルの友人から電話がかかってくる
– 「回路製作の締め切りが日曜になりました!よろしく!!!」
● でも自分はすでに OS の発表準備をしている
よって無視
● ”適切に優先順位を指定してやれば”大丈夫
– 予め全ての優先順位が決まってないと難しい
● 同じなら
– FIFO :とりあえず先にやってるものが終わるまでは次に移れない
– RR :交代に進めるけど、時間は半々
実際には後輩に任せました ☆ -( ゝω ・ ) b
実際に使ってみた – FIFO/RR
● RR + CFS prio 1 + nice10
● /proc/sys/kernel/sched_rt_period_us → 1s/proc/sys/kernel/sched_rt_runtime_us → 0.95s
実際に使ってみた – FIFO/RR
● RR(same) x2 prio 50
実際に使ってみた – FIFO/RR
● FIFO(same) x2 prio 50
– 飢餓状態!!!
実際に使ってみた – FIFO/RR
● RR(higher) + RR(lower) prio 49(pid 2260)/51(pid 2261)
– 飢餓状態!!!
実際に使ってみた – FIFO/RR
● 実際に RR を動かしてみよう
● 0.05s しか割り当ててもらえない辛さ
● コマンド
– schedtool -R -p 1 -e ./load &
– trace-cmd record -e sched_switch
– killall -KILL load
– kernelshark
RM/DM
● RM → 周期が短いものから実行
● DM → “相対”デッドラインが短いものから実行
● 相対デッドライン ( relative deadline )
● 絶対デッドライン ( absolute deadline )
RM/DM
● 固定優先度と動的優先度
– fixed priority / dynamic priority
● RM も DM も固定優先度
→ 予め優先順位が決まる
● RM は特に組み込みでよく使われる
● けっこうデッドライン守れる
– ほとんどの組み合わせで CPU使用率 80% くらいはいけるらしい
EDF - 概要
● 一番いいスケジューリングってなんだろう?
→ 一番締め切りが近いものからやる
● EDF ( Earliest Deadline First )
EDF - 概要
● EDF と RM の比較
● T1:period=4 runtime=1
● T2:period=6 runtime=2
● T3:period=8 runtime=3
● CPU usage =
1/4+2/6+3/8=95.8%
EDF - 概要
● メリット
– CPU使用率が 100%になるまではデッドラインを守れる
● デメリット
– CPU使用率が 100%を超えた時にドミノ倒し現象が起こる
ex. )明日締め切りなのに明後日までかかったら明後日締め切りのタスクは終わらない
– 動的に優先度を変えるために実装が (RM比で ) かなり面倒
● あまり普及していない
– マルチコアでは最適でなくなる
EDF – 応答時間
● EDF ってどれくらいの時間でタスクを完了するんだろう?
→締め切りを守れるにしても、どれくらい時間かかるの?
EDF – 応答時間
● 1 CPU
● CPUバウンド (IO待ちで寝たりしない )
● いつでもプリエンティブ
● 任意の時刻にタスクが現れる
● 相対デッドラインは、実行時間以上の任意の値を取る
● 相対デッドラインは実行時間に比例
● タスクの到着間隔の平均・実行時間の平均は既知
● この条件で、到着間隔と実行時間が指数分布という仮定
EDF – 応答時間
● 興味ある人は参考資料を見てください!!
● 条件がハマれば良さそう
● 到着間隔 / 実行時間が指数分布と置くのは正しい?
EDF – 実装
● github に EDF を実装したものが落ちてる
● https://github.com/jlelli/sched-deadline
● kernel-3.7.0-rc6+
EDF – 実装
● とりあえず pick_next_task_dl を見てみる
● すると、以下がキモっぽいdl_se = pick_next_dl_entity(rq, dl_rq);
● これは右のようになっている
● 要するに赤黒木
– キーは絶対デッドライン時刻
static struct sched_dl_entity *pick_next_dl_entity(struct rq *rq, struct dl_rq *dl_rq)
{struct rb_node *left = dl_rq->rb_leftmost;
if (!left)return NULL;
return rb_entry(left, struct sched_dl_entity, rb_node);}
実際に使ってみた
● これの実装では、パラメータは以下の3つ
– deadline - 締め切り
– runtime - 走ることの出来る時間
– period - タスクの周期
実際に使ってみた
● sched_setscheduler2 とやらを呼べって書いてある
● 呼んでみた→
● 呼んでみた・・・
#include <sys/types.h>#include <unistd.h>#include <linux/sched.h>#include <sched.h>#include <stdlib.h>
int main( int argc __attribute__((unused)), char *argv[] __attribute__((unused)) ){ pid_t mypid = getpid(); struct sched_param2 sched_status = { .sched_priority = 1 .sched_runtime = 10000, .sched_deadline = 100000, .sched_period = 100000 }; if ( sched_setscheduler2( mypid, SCHED_DEADLINE, &sched_status ) ) { perror( "sched_setscheduler2" ); return 1; }
volatile int i=0; while( 1 ) i++; return 0;}
実際に使ってみた
● sched_setscheduler2 とやらを呼べって書いてある
● 呼んでみた→
#include <sys/types.h>#include <unistd.h>#include <linux/sched.h>#include <sched.h>#include <stdlib.h>
int main( int argc __attribute__((unused)), char *argv[] __attribute__((unused)) ){ pid_t mypid = getpid(); struct sched_param2 sched_status = { .sched_priority = 1 .sched_runtime = 10000, .sched_deadline = 100000, .sched_period = 100000 }; if ( sched_setscheduler2( mypid, SCHED_DEADLINE, &sched_status ) ) { perror( "sched_setscheduler2" ); return 1; }
volatile int i=0; while( 1 ) i++; return 0;}
dame.c: In function ‘main’:dame.c:15: error: variable ‘sched_status’ has initializer but incomplete typedame.c:16: error: unknown field ‘__sched_priority’ specified in initializerdame.c:17: error: request for member ‘sched_flags’ in something not a structure or uniondame.c:18: warning: excess elements in struct initializerdame.c:18: warning: (near initialization for ‘sched_status’)dame.c:15: error: storage size of ‘sched_status’ isn’t known
_人人人人人人人人_ > 突然のエラー <  ̄ Y^Y^Y^Y^Y^Y^Y ̄
実際に使ってみた
● でもソースには関数あるし・・・
● 動かない理由がわからない
● わからない・・・
● そして溶けていった数日間
int sched_setscheduler2(struct task_struct *p, int policy, const struct sched_param2 *param2)
{return __sched_setscheduler(p, policy, param2, true);
}
カーネルコンパイル周りでサークルの先輩にめっちゃ助けてもらいました・・・
実際に使ってみた
まさか ?
実際に使ってみた
● schedtool を使ったら動いた
– 何で?システムコールが定義されてるって書いてあったけど・・・?
実際に使ってみた
● schedtool を使ったら動いた
– 何で?システムコールが定義されてるって書いてあったけど・・・?
● システムコールとは
– じつは割り込み (0x80 番 )
– 引数はスタックではなくレジスタ
– eax に種別をセット
● exit なら 1, write なら 4 など
● schedtool は右のようなマクロを自分で定義していた
#ifdef __i386__#define __NR_sched_setparam2 350#define __NR_sched_getparam2 351#define __NR_sched_setscheduler2 352#endif
…
#define sched_setscheduler2(pid, policy, param) \ syscall(__NR_sched_setscheduler2, pid, policy, param)
#define sched_getparam2(pid, param) \ syscall(__NR_sched_getparam2, pid, param)
#define sched_setparam2(pid, param) \ syscall(__NR_sched_setparam2, pid, param)
tags/v4 だと ,349,350,351 と 1 少なくする必要あり
実際に使ってみた
● EDF x1 runtime=10ms / deadline=100ms/ period=100ms
● 達成・・・して・・・なくね・・・?
実際に使ってみれなかった
● sched_class 的には dl_sched_class は rt より高い
– つまり誰からも干渉を受けず、自分のしごとを淡々とこなすはず
– 本来なら下図のようになるはず
● でもどうも途中で切れてるように見える
– trace-cmd のせい? ( うまく trace できてない可能性)
– preemption周りにバグ?(他のタスクが preempt したときにスルーできない)
実際に使ってみれなかった
● やりたかったこと
– 1コアで
● EDF + CFS● EDF + RR● EDF x2( 間に合う )● EDF x2( 間に合わない )● EDF x1( 1ms周期 )
– 今度は2コア2スレッド
● EDF x2 ( 間に合う )● EDF x3 ( それぞれ CPU 時間 2/3 )
– マルチコアのときには CPU100%は保証できない
● からどうしたらいいか?● という結論を導いて、もうすこし考察を深めたかった
まとめ
● CFS スケジューラは優秀
– 普通に使っている分には違和感を感じることはない
● EDF スケジューラはリアルタイム性が欲しいときに有効な手段
– だけどうまく実装されている例があまりない
– 動くようにしてみたいですね
● 意外と idle している時間って多い
● linux カーネルの広がる闇
– わからないことをしらべたらわからないからしらべたら (以下 n 回続く)
– CONFIG_SMP(マルチコア対応部分 ) がかなりある
– わからないことが多すぎて時間が食いつぶされてしまいました・・・
おまけ - main
● スケジューラの元ってどこ
● nohzとか hrtick とか ,SMP とかそうじゃないとか , 同じような関数でも違うやつがいろいろあって難しかった
● コンパイル時のオプション→
● ソースの感じでは hrtick として動くっぽい
… いろいろたどる …
● kernel の一番大元にいきついた
– arch/x86/kernel/head32.c i386_start_kernel() とか
– 疑問:これの上はどこ?
amiq@ostest> grep NO_HZ /boot/config-3.7.0-rc6+ CONFIG_NO_HZ=yamiq@ostest> grep HRTICK /boot/config-3.7.0-rc6+ CONFIG_SCHED_HRTICK=y
おまけ - main
● i386_start_kernel
● →start_kernel(init/main.c)
– dmesg の最初はここから
● → sched_init
– init_hrtick->hrtimer_init
● → tick_init
– tick_notifier 構造体の登録
– → … 数層の関数ジャンプ …
● → tick_periodic– do_timer → jiffies/xtime の変更とかしてる中枢
おまけ - main
● start_kernel → init_timers → open_softirq
● run_timer_softirq → hrtimer_run_pending → hrtimer_switch_to_hres → tick_setup_sched_timer
● ここで tick_sched_timer の準備をしてる
/* * linux/init/main.c * * Copyright (C) 1991, 1992 Linus Torvalds *
... */
[ 0.000000] hpet clockevent registered[ 0.000000] tsc: Detected 2393.996 MHz processor[ 0.008000] Calibrating delay loop (skipped) preset value.. 4787.99 BogoMIPS (lpj=9575984)
おまけ - main
● tick_sched_timer か tick_periodic → update_process_times(user_mode(regs));
● からの scheduler_tick
● ここでやっと scheduler に戻ってきた
● これがHZごとに実行されるらしい
– HZを使ってるところは見つからなかった・・・ ??
/* * This function gets called by the timer code, with HZ frequency. * We call it with interrupts disabled. */void scheduler_tick(void){
int cpu = smp_processor_id();struct rq *rq = cpu_rq(cpu);struct task_struct *curr = rq->curr;
sched_clock_tick();
raw_spin_lock(&rq->lock);update_rq_clock(rq);update_cpu_load_active(rq);curr->sched_class->task_tick(rq, curr, 0);raw_spin_unlock(&rq->lock);
perf_event_task_tick();
#ifdef CONFIG_SMPrq->idle_balance = idle_cpu(cpu);trigger_load_balance(rq, cpu);
#endif}
おまけ - タスクスイッチ
● タスクの切り替えってどうやってるの?
– PC とか、各種レジスタの退避とかスタックポインタとか
– 関数ジャンプならスタックに積むだけだけど・・・
● switch_toマクロ
– /arch/x86/include/asm/switch_to.h
● flags/EBP/ESP を保存して、データを引っ張ってくる
● レジスタはスタックにある
● __switch_to で切り替え
● スタックポインタとかは構造体のメンバとして保存
#define switch_to(prev, next, last) \do { \
/* \ * Context-switching clobbers all registers, so we clobber \ * them explicitly, via unused output variables. \ * (EAX and EBP is not listed because EBP is saved/restored \ * explicitly for wchan access and EAX is the return value of \ * __switch_to()) \ */ \unsigned long ebx, ecx, edx, esi, edi; \
\asm volatile("pushfl\n\t" /* save flags */ \
"pushl %%ebp\n\t" /* save EBP */ \ "movl %%esp,%[prev_sp]\n\t" /* save ESP */ \ "movl %[next_sp],%%esp\n\t" /* restore ESP */ \ "movl $1f,%[prev_ip]\n\t" /* save EIP */ \ "pushl %[next_ip]\n\t" /* restore EIP */ \ __switch_canary \ "jmp __switch_to\n" /* regparm call */ \ "1:\t" \ "popl %%ebp\n\t" /* restore EBP */ \ "popfl\n" /* restore flags */ \
\ /* output parameters */ \ : [prev_sp] "=m" (prev->thread.sp), \ [prev_ip] "=m" (prev->thread.ip), \ "=a" (last), \
\ /* clobbered output registers: */ \ "=b" (ebx), "=c" (ecx), "=d" (edx), \ "=S" (esi), "=D" (edi) \ \ __switch_canary_oparam \
\ /* input parameters: */ \ : [next_sp] "m" (next->thread.sp), \ [next_ip] "m" (next->thread.ip), \ \ /* regparm parameters for __switch_to(): */ \ [prev] "a" (prev), \ [next] "d" (next) \
\ __switch_canary_iparam \
\ : /* reloaded segment registers */ \
"memory"); \} while (0)
おまけ - タスクスイッチ