Linuxカーネルのcurrent_thread_info実装
x86_64アーキテクチャにおけるcurrent_thread_info関数は、現在実行中のスレッド情報を取得する重要な機能です。この関数の実装は、CPUごとのデータ管理とスタック構造に依存しています。
実装コードの分析
static inline struct thread_info *get_current_thread_info(void) {
unsigned long stack_base;
struct thread_info *thread_data;
stack_base = per_cpu_read(kernel_stack);
thread_data = (struct thread_info *)(stack_base + STACK_OFFSET - TASK_STACK_SIZE);
return thread_data;
}
CPUごとのデータ管理機構
現代のマルチコアプロセッサでは、各CPUコアが独立したデータ領域を持つ必要があります。Linuxカーネルはper-CPU変数メカニズムを使用してこれを実現しています。
x86_64アーキテクチャでは、GSセグメントレジスタを利用してCPU固有のデータ領域にアクセスします。GSレジスタのベースアドレスはMSR_GS_BASEモデル固有レジスタで設定され、各CPUコアが異なる値を保持できます。
スタックレイアウトの詳細
x86_64の特権レベル切り替え時には、以下のレジスタがカーネルスタックに保存されます:
- SS(スタックセグメント)
- RSP(スタックポインタ)
- RFLAGS(フラグレジスタ)
- CS(コードセグメント)
- RIP(命令ポインタ)
各レジスタは8バイト使用するため、合計40バイトの領域が確保されます。
メモリ配置の計算
#define PRIVILEGE_SWITCH_SIZE (5 * 8)
#define STACK_SIZE 8192
/* スタックレイアウト:
高アドレス [+----------------------------+] ← kernel_stack
| 特権レベル切り替え領域 | (40バイト)
[+----------------------------+] ← 有効スタック開始点
| |
| カーネルスタック領域 |
| (下方に拡張) |
| |
[+----------------------------+] ← thread_info構造体
低アドレス [+----------------------------+] ← (kernel_stack - STACK_SIZE)
*/
計算式の解説:
kernel_stack: CPUごとのカーネルスタック先頭アドレスSTACK_SIZE: カーネルスタック全体のサイズ(8192バイト)PRIVILEGE_SWITCH_SIZE: 特権レベル切り替え時の保存領域(40バイト)
thread_info構造体の位置は、スタック先頭から特権レベル保存領域を加算し、スタック全体サイズを減算することで求められます。
実装の要点
この実装方式により:
- 各CPUコアが独立したthread_infoにアクセス可能
- 特権レベル切り替え時の状態保存を考慮
- 効率的なインライン関数として実装
- アーキテクチャ固有の機能を活用