今回はstart_kernelから「trap_init」を見てみます。
実体は「arch/x86/kernel/trap.c」にあります。
=========
void __init trap_init(void)
{
int i;
~(省略)~
/* トラップゲートを登録 */
set_intr_gate(0, ÷_error);
/* 割り込みゲートを登録 */
set_intr_gate_ist(1, &debug, DEBUG_STACK);
set_intr_gate_ist(2, &nmi, NMI_STACK);
/* ユーザープログラムから直接呼び出せる割り込みゲートを登録 */
set_system_intr_gate_ist(3, &int3, DEBUG_STACK);
~(省略)~
#ifdef CONFIG_X86_32
/* タスクゲートの登録 */
set_task_gate(8, GDT_ENTRY_DOUBLEFAULT_TSS);
#else
set_intr_gate_ist(8, &double_fault, DOUBLEFAULT_STACK);
#endif
~(省略)~
/* Reserve all the builtin and the syscall vector: */
for (i = 0; i < FIRST_EXTERNAL_VECTOR; i++)
set_bit(i, used_vectors);
#ifdef CONFIG_X86_64
set_bit(IA32_SYSCALL_VECTOR, used_vectors);
#else
set_bit(SYSCALL_VECTOR, used_vectors);
#endif
/*CPUの初期化。詳しくは次回 */
cpu_init();
#ifdef CONFIG_X86_32
/* 特になにもしない */
trap_init_hook();
#endif
}
=========
主に使っている関数を整理すると以下のようになります。
set_intr_gate:トラップゲートの登録
set_intr_gate_ist:割り込みゲートの登録
set_system_intr_gate_ist:ユーザープログラムから直接呼び出せる割り込みゲートの登録
set_task_gate:タスクゲートの登録
トラップゲートと割り込みゲートは何が違うの?というと割り込みゲートが呼び出された場合は「割り込み禁止」状態になっているという違いです。
2009年10月23日金曜日
2009年10月19日月曜日
page_alloc_init、parse_early_param、parse_args、sort_main_extable
よーやくstart_kernelに戻ってきました。
忘れてしまっているのでもう一度今呼んでいる部分を中心に抜粋。
=========
preempt_disable();
build_all_zonelists();
page_alloc_init();
printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
parse_earlpy_param();
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption);
if (!irqs_disabled()) {
printk(KERN_WARNING "start_kernel(): bug: interrupts were "
"enabled *very* early, fixing it\n");
local_irq_disable();
}
sort_main_extable();
trap_init();
rcu_init();
========
前回まででbuild_all_zonelistsを見てきたので今回はpage_alloc_init、
これはCPUのホットプラグをサポートしている場合のみに走る処理。
次のparse_early_paramはコマンドラインで渡された関数を実行するもの。
parse_argsはカーネルに渡すパラメータを設定します。
sort_main_extableは例外テーブルをソートします。
ちょっと駆け足だけれど、今日はここまで。
忘れてしまっているのでもう一度今呼んでいる部分を中心に抜粋。
=========
preempt_disable();
build_all_zonelists();
page_alloc_init();
printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
parse_earlpy_param();
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption);
if (!irqs_disabled()) {
printk(KERN_WARNING "start_kernel(): bug: interrupts were "
"enabled *very* early, fixing it\n");
local_irq_disable();
}
sort_main_extable();
trap_init();
rcu_init();
========
前回まででbuild_all_zonelistsを見てきたので今回はpage_alloc_init、
これはCPUのホットプラグをサポートしている場合のみに走る処理。
次のparse_early_paramはコマンドラインで渡された関数を実行するもの。
parse_argsはカーネルに渡すパラメータを設定します。
sort_main_extableは例外テーブルをソートします。
ちょっと駆け足だけれど、今日はここまで。
2009年10月15日木曜日
nr_free_zone_pages
9/25の「build_all_zonelists」から延々と潜り込んできましたが、ようやくbuild_all_zonelistsまで戻って来ました。
次はnr_free_pagecache_pagesを見てみましょう。
実体は同じくmm/page_alloc.cです。
=======
unsigned int nr_free_pagecache_pages(void)
{
return nr_free_zone_pages(gfp_zone(GFP_HIGHUSER_MOVABLE));
}
=======
というわけで「nr_free_zone_pages」を見てみましょう。
=======
static unsigned int nr_free_zone_pages(int offset)
{
struct zoneref *z;
struct zone *zone;
/* Just pick one node, since fallback list is circular */
unsigned int sum = 0;
/* NUMAじゃなければ自ノードのゾーンリストがとれてくる */
struct zonelist *zonelist = node_zonelist(numa_node_id(), GFP_KERNEL);
/* ゾーンリストにゾーンがあるだけ繰り返す */
for_each_zone_zonelist(zone, z, zonelist, offset) {
/* ゾーンの総ページ数 */
unsigned long size = zone->present_pages;
/* 空きページ緊急確保量 */
unsigned long high = zone->pages_high;
if (size > high)
/* 割り当て可能サイズを加算 */
sum += size - high;
}
return sum;
}
=======
このようにして割り当て可能なページ数を算出しています。
次はnr_free_pagecache_pagesを見てみましょう。
実体は同じくmm/page_alloc.cです。
=======
unsigned int nr_free_pagecache_pages(void)
{
return nr_free_zone_pages(gfp_zone(GFP_HIGHUSER_MOVABLE));
}
=======
というわけで「nr_free_zone_pages」を見てみましょう。
=======
static unsigned int nr_free_zone_pages(int offset)
{
struct zoneref *z;
struct zone *zone;
/* Just pick one node, since fallback list is circular */
unsigned int sum = 0;
/* NUMAじゃなければ自ノードのゾーンリストがとれてくる */
struct zonelist *zonelist = node_zonelist(numa_node_id(), GFP_KERNEL);
/* ゾーンリストにゾーンがあるだけ繰り返す */
for_each_zone_zonelist(zone, z, zonelist, offset) {
/* ゾーンの総ページ数 */
unsigned long size = zone->present_pages;
/* 空きページ緊急確保量 */
unsigned long high = zone->pages_high;
if (size > high)
/* 割り当て可能サイズを加算 */
sum += size - high;
}
return sum;
}
=======
このようにして割り当て可能なページ数を算出しています。
2009年10月12日月曜日
build_zonelists_in_zone_orderとbuild_zonlists_node
今回もまた「build_zonelists」の中から呼んでいる「build_zonelists_in_zone_order」を
見てみます。今回も実体は「mm/page_alloc.c」にあります。
========
static void build_zonelists_in_node_order(pg_data_t *pgdat, int node)
{
int j;
struct zonelist *zonelist;
zonelist = &pgdat->node_zonelists[0]
/* ゾーンリストの空きを探す */
for (j = 0; zonelist->_zonerefs[j].zone != NULL; j++)
;
/* ノードのゾーンリストにゾーンを設定する */
j = build_zonelists_node(NODE_DATA(node), zonelist, j,
MAX_NR_ZONES - 1);
/* ゾーンリストの末尾をNULLに設定 */
zonelist->_zonerefs[j].zone = NULL;
zonelist->_zonerefs[j].zone_idx = 0;
}
========
ついでに「build_zonlists_node」も見ておきましょう。
=========
static int build_zonelists_node(pg_data_t *pgdat, struct zonelist *zonelist,
int nr_zones, enum zone_type zone_type)
{
struct zone *zone;
BUG_ON(zone_type >= MAX_NR_ZONES);
zone_type++;
do {
zone_type--;
zone = pgdat->node_zones + zone_type;
/* ゾーンの総ページ数が0でなければ */
if (populated_zone(zone)) {
/* ゾーンリストにゾーンを設定 */
zoneref_set_zone(zone,
&zonelist->_zonerefs[nr_zones++]);
check_highest_zone(zone_type);
}
} while (zone_type);
return nr_zones;
}
=========
こうしてノードごとのゾーンリストにゾーンを格納していっています。
ところで「populated_zone」の定義は以下のようになっています。
==========
static inline int populated_zone(struct zone *zone)
{
return (!!zone->present_pages);
}
==========
なんでまた「!!」なんてしているんでしょうか。
否定の否定なので何もつけないのと同じですが、何か意図があるんでしょうか?
見てみます。今回も実体は「mm/page_alloc.c」にあります。
========
static void build_zonelists_in_node_order(pg_data_t *pgdat, int node)
{
int j;
struct zonelist *zonelist;
zonelist = &pgdat->node_zonelists[0]
/* ゾーンリストの空きを探す */
for (j = 0; zonelist->_zonerefs[j].zone != NULL; j++)
;
/* ノードのゾーンリストにゾーンを設定する */
j = build_zonelists_node(NODE_DATA(node), zonelist, j,
MAX_NR_ZONES - 1);
/* ゾーンリストの末尾をNULLに設定 */
zonelist->_zonerefs[j].zone = NULL;
zonelist->_zonerefs[j].zone_idx = 0;
}
========
ついでに「build_zonlists_node」も見ておきましょう。
=========
static int build_zonelists_node(pg_data_t *pgdat, struct zonelist *zonelist,
int nr_zones, enum zone_type zone_type)
{
struct zone *zone;
BUG_ON(zone_type >= MAX_NR_ZONES);
zone_type++;
do {
zone_type--;
zone = pgdat->node_zones + zone_type;
/* ゾーンの総ページ数が0でなければ */
if (populated_zone(zone)) {
/* ゾーンリストにゾーンを設定 */
zoneref_set_zone(zone,
&zonelist->_zonerefs[nr_zones++]);
check_highest_zone(zone_type);
}
} while (zone_type);
return nr_zones;
}
=========
こうしてノードごとのゾーンリストにゾーンを格納していっています。
ところで「populated_zone」の定義は以下のようになっています。
==========
static inline int populated_zone(struct zone *zone)
{
return (!!zone->present_pages);
}
==========
なんでまた「!!」なんてしているんでしょうか。
否定の否定なので何もつけないのと同じですが、何か意図があるんでしょうか?
2009年10月9日金曜日
find_next_best_node
前回見ていた「build_zonelists」の中から今回は「find_next_best_node」を見てみます。
ゾーンリストに追加するノードを検索する関数で実体は「mm/page_alloc.c」にあります。
==========
static int find_next_best_node(int node, nodemask_t *used_node_mask)
{
int n, val;
int min_val = INT_MAX;
int best_node = -1;
node_to_cpumask_ptr(tmp, 0);
/* nodeが未使用状態であるならnodeをそのまま返す */
if (!node_isset(node, *used_node_mask)) {
/* nodeが使用中であるというbitをused_node_maskに立ててやる */
node_set(node, *used_node_mask);
return node;
}
/* nodeの数だけ繰り返す */
for_each_node_state(n, N_HIGH_MEMORY) {
/* 既にチェック済みのノードは飛ばす */
if (node_isset(n, *used_node_mask))
continue;
/* nodeとnが同じかどうか */
val = node_distance(node, n);
/* nodeより小さいノードへペナルティをかけてやる */
val += (n < node);
/* 先頭ではなくて未使用のノードを優先する */
node_to_cpumask_ptr_next(tmp, n);
/* CPUを持っているノードにはペナルティ */
if (!cpus_empty(*tmp))
val += PENALTY_FOR_NODE_WITH_CPUS;
/* Slight preference for less loaded node */
val *= (MAX_NODE_LOAD*MAX_NUMNODES);
val += node_load[n];
/* valがこれまでの最小値より小さければ最適ノードを入れ替え *//*
if (val < min_val) {
min_val = val;
best_node = n;
}
}
/* 次の検索にひっかからないようにビットを立ててやる */
if (best_node >= 0)
node_set(best_node, *used_node_mask);
return best_node;
}
==========
ゾーンリストに追加したくない条件についてはペナルティをかけてやって、そのノードが選ばれないようにしています。後半に出てくる「node_load」というのは、前回見たuild_zonelistsで値をセットしていて新たに選ばれたノードが格納されています。
既に選ばれたノードについてはfind_next_best_nodeの「val += node_load[n]」という部分でペナルティがかかるため、ページの割り当てが局所化しないようになっているのです。
ゾーンリストに追加するノードを検索する関数で実体は「mm/page_alloc.c」にあります。
==========
static int find_next_best_node(int node, nodemask_t *used_node_mask)
{
int n, val;
int min_val = INT_MAX;
int best_node = -1;
node_to_cpumask_ptr(tmp, 0);
/* nodeが未使用状態であるならnodeをそのまま返す */
if (!node_isset(node, *used_node_mask)) {
/* nodeが使用中であるというbitをused_node_maskに立ててやる */
node_set(node, *used_node_mask);
return node;
}
/* nodeの数だけ繰り返す */
for_each_node_state(n, N_HIGH_MEMORY) {
/* 既にチェック済みのノードは飛ばす */
if (node_isset(n, *used_node_mask))
continue;
/* nodeとnが同じかどうか */
val = node_distance(node, n);
/* nodeより小さいノードへペナルティをかけてやる */
val += (n < node);
/* 先頭ではなくて未使用のノードを優先する */
node_to_cpumask_ptr_next(tmp, n);
/* CPUを持っているノードにはペナルティ */
if (!cpus_empty(*tmp))
val += PENALTY_FOR_NODE_WITH_CPUS;
/* Slight preference for less loaded node */
val *= (MAX_NODE_LOAD*MAX_NUMNODES);
val += node_load[n];
/* valがこれまでの最小値より小さければ最適ノードを入れ替え *//*
if (val < min_val) {
min_val = val;
best_node = n;
}
}
/* 次の検索にひっかからないようにビットを立ててやる */
if (best_node >= 0)
node_set(best_node, *used_node_mask);
return best_node;
}
==========
ゾーンリストに追加したくない条件についてはペナルティをかけてやって、そのノードが選ばれないようにしています。後半に出てくる「node_load」というのは、前回見たuild_zonelistsで値をセットしていて新たに選ばれたノードが格納されています。
既に選ばれたノードについてはfind_next_best_nodeの「val += node_load[n]」という部分でペナルティがかかるため、ページの割り当てが局所化しないようになっているのです。
2009年10月5日月曜日
__build_all_zonelistsとbuild_zonelists
今回はbuild_all_zonelists関数から呼び出している「__build_all_zonelists」を見てみます。その名の通りすべてのノードのzonelistをビルドするのですが、「ノードをビルドする」とはなんなのか?と。
実体はbuild_all_zonelistsと同じmm/page_alloc.cにあります
========
static int __build_all_zonelists(void *dummy)
{
int nid;
for_each_online_node(nid) {
pg_data_t *pgdat = NODE_DATA(nid);
build_zonelists(pgdat);
build_zonelist_cache(pgdat);
}
return 0;
}
========
ノードリストから一つ一つゾーンリストを取り出してbuild_zonelists、build_zonelist_cacheしています。
「pg_dat_t」というのはノードごとにメモリを管理するための構造体です。
詳しくは「LinuxKernelHackJapan」の解説をどうぞ。
次にここから呼び出している「build_zonelists」を見てみます。
実体は同じファイルにあります。
======
static void build_zonelists(pg_data_t *pgdat)
{
~(変数宣言)~
/* ゾーンリストを初期化する */
for (i = 0; i < MAX_ZONELISTS; i++) {
zonelist = pgdat->node_zonelists + i;
zonelist->_zonerefs[0].zone = NULL;
zonelist->_zonerefs[0].zone_idx = 0;
}
/* used_maskをゼロ埋め */
local_node = pgdat->node_id;
load = num_online_nodes();
prev_node = local_node;
nodes_clear(used_mask);
/* 色々初期化 */
memset(node_load, 0, sizeof(node_load));
memset(node_order, 0, sizeof(node_order));
j = 0;
/* ゾーンリストに追加する最適なノードを探す */
while ((node = find_next_best_node(local_node, &used_mask)) >= 0) {
/* local_nodeとnodeが同じノードを指しているかどうか(違っていれば1) */
int distance = node_distance(local_node, node);
/* 異なるノードで再利用するのに十分に離れたノードであった場合 */
if (distance > RECLAIM_DISTANCE)
zone_reclaim_mode = 1;
/* ノードの割り当てが局所化しないように、注意しながらloadするノードを格納 */
if (distance != node_distance(local_node, prev_node))
node_load[node] = load;
prev_node = node;
load--;
/* ノードの順序に従って格納してやる */
if (order == ZONELIST_ORDER_NODE)
build_zonelists_in_node_order(pgdat, node);
else
node_order[j++] = node; /* remember order */
}
if (order == ZONELIST_ORDER_ZONE) {
/* calculate node order -- i.e., DMA last! */
build_zonelists_in_zone_order(pgdat, j);
}
/* 自ノードのゾーンリストを構築する */
build_thisnode_zonelists(pgdat);
}
======
長かったのだけど、いまいちチンプンカンプンなのだ。。
ページの管理はゾーンという単位でおこなっていて、ノードごとにどのゾーンからページを確保するかという順番を決めるゾーンリストがあります。そのゾーンリストがノードごとにあって、ここではゾーンリストを構築している、という処理になります。
ここから呼び出している関数はまた次回から順に見ていきます。
start_kernelに戻る日はいつなのだろう。。
実体はbuild_all_zonelistsと同じmm/page_alloc.cにあります
========
static int __build_all_zonelists(void *dummy)
{
int nid;
for_each_online_node(nid) {
pg_data_t *pgdat = NODE_DATA(nid);
build_zonelists(pgdat);
build_zonelist_cache(pgdat);
}
return 0;
}
========
ノードリストから一つ一つゾーンリストを取り出してbuild_zonelists、build_zonelist_cacheしています。
「pg_dat_t」というのはノードごとにメモリを管理するための構造体です。
詳しくは「LinuxKernelHackJapan」の解説をどうぞ。
次にここから呼び出している「build_zonelists」を見てみます。
実体は同じファイルにあります。
======
static void build_zonelists(pg_data_t *pgdat)
{
~(変数宣言)~
/* ゾーンリストを初期化する */
for (i = 0; i < MAX_ZONELISTS; i++) {
zonelist = pgdat->node_zonelists + i;
zonelist->_zonerefs[0].zone = NULL;
zonelist->_zonerefs[0].zone_idx = 0;
}
/* used_maskをゼロ埋め */
local_node = pgdat->node_id;
load = num_online_nodes();
prev_node = local_node;
nodes_clear(used_mask);
/* 色々初期化 */
memset(node_load, 0, sizeof(node_load));
memset(node_order, 0, sizeof(node_order));
j = 0;
/* ゾーンリストに追加する最適なノードを探す */
while ((node = find_next_best_node(local_node, &used_mask)) >= 0) {
/* local_nodeとnodeが同じノードを指しているかどうか(違っていれば1) */
int distance = node_distance(local_node, node);
/* 異なるノードで再利用するのに十分に離れたノードであった場合 */
if (distance > RECLAIM_DISTANCE)
zone_reclaim_mode = 1;
/* ノードの割り当てが局所化しないように、注意しながらloadするノードを格納 */
if (distance != node_distance(local_node, prev_node))
node_load[node] = load;
prev_node = node;
load--;
/* ノードの順序に従って格納してやる */
if (order == ZONELIST_ORDER_NODE)
build_zonelists_in_node_order(pgdat, node);
else
node_order[j++] = node; /* remember order */
}
if (order == ZONELIST_ORDER_ZONE) {
/* calculate node order -- i.e., DMA last! */
build_zonelists_in_zone_order(pgdat, j);
}
/* 自ノードのゾーンリストを構築する */
build_thisnode_zonelists(pgdat);
}
======
長かったのだけど、いまいちチンプンカンプンなのだ。。
ページの管理はゾーンという単位でおこなっていて、ノードごとにどのゾーンからページを確保するかという順番を決めるゾーンリストがあります。そのゾーンリストがノードごとにあって、ここではゾーンリストを構築している、という処理になります。
ここから呼び出している関数はまた次回から順に見ていきます。
start_kernelに戻る日はいつなのだろう。。
2009年9月29日火曜日
set_zonelist_orderとdefault_zonelist_order
前回見たbuild_all_zonelists関数から呼び出している関数のうち、今回は最初に呼んでいる
「set_zonelist_order」を見てみます。
実体は同じくmm/page_alloc.cにあります。
========
となっているので「deaflt_zonelist_order」を見てみます。
========
=========
ノードリスト中のノードサイズに従ってどのゾーンから空きページを探すかを決定している、、、
のだと思います。まだ自信なし。。
ここで返った値が、先述のset_zonelist_orderの中でcurrent_zonelist_orderに代入されます。
「set_zonelist_order」を見てみます。
実体は同じくmm/page_alloc.cにあります。
========
2298 static void set_zonelist_order(void)========
2299 {
2300 if (user_zonelist_order == ZONELIST_ORDER_DEFAULT)
2301 current_zonelist_order = default_zonelist_order();
2302 else
2303 current_zonelist_order = user_zonelist_order;
2304 }
となっているので「deaflt_zonelist_order」を見てみます。
========
2244 static int default_zonelist_order(void)
2245 {
2246 ~変数宣言~
2257 low_kmem_size = 0;
2258 total_size = 0;
/* 全ノードのゾーンリストをチェックする */
2259 for_each_online_node(nid) {
2260 for (zone_type = 0; zone_type < MAX_NR_ZONES; zone_type++) {
2261 z = &NODE_DATA(nid)->node_zones[zone_type];
2262 if (populated_zone(z)) {
/* ゾーンタイプがZON_DMAかZONE_DMA32ならlow_kmem_sizeに加算 */
2263 if (zone_type < ZONE_NORMAL)
2264 low_kmem_size += z->present_pages;
/* トータルのサイズを加算 */
2265 total_size += z->present_pages;
2266 }
2267 }
2268 }
/* DMAエリアが無かったり、巨大ならZONELIST_ORDER_NODEでリターン */
2269 if (!low_kmem_size || /* there are no DMA area. */
2270 low_kmem_size > total_size/2) /* DMA/DMA32 is big. */
2271 return ZONELIST_ORDER_NODE;
/* 平均サイズを算出 */
2277 average_size = total_size /
2278 (nodes_weight(node_states[N_HIGH_MEMORY]) + 1);
2279 for_each_online_node(nid) {
2280 low_kmem_size = 0;
2281 total_size = 0;
/* もう一度同じように計算する */
2282 for (zone_type = 0; zone_type < MAX_NR_ZONES; zone_type++) {
2283 z = &NODE_DATA(nid)->node_zones[zone_type];
2284 if (populated_zone(z)) {
2285 if (zone_type < ZONE_NORMAL)
2286 low_kmem_size += z->present_pages;
2287 total_size += z->present_pages;
2288 }
2289 }
/* 平均より大きく、全サイズの70%を占めるノードがあるならZONELIST_ORDER_NODEでリターン*/
2290 if (low_kmem_size &&
2291 total_size > average_size && /* ignore small node */
2292 low_kmem_size > total_size * 70/100)
2293 return ZONELIST_ORDER_NODE;
2294 }
/* ここまで来たらZONELIST_ORDER_ZONEでリターン */
2295 return ZONELIST_ORDER_ZONE;
2296 }
=========
ノードリスト中のノードサイズに従ってどのゾーンから空きページを探すかを決定している、、、
のだと思います。まだ自信なし。。
ここで返った値が、先述のset_zonelist_orderの中でcurrent_zonelist_orderに代入されます。
2009年9月25日金曜日
build_all_zonelists
今回見てみるのはbuild_all_zonelistsです。
実体はmm/page_alloc.cにあります。
========
========
要約するとゾーンリストを構築して、割り当て可能なページ数を「vm_total_pages」に格納する処理になります。
Linuxではページをゾーンという単位で管理しており、ゾーンリストというのはそれらゾーンをリストしたものです。
ゾーンリストについては下記を参考にしました。
Linux Kernel Hack Japan「build_all_zonelists()/linux2.6」
ここから呼び出している関数群についてはまた次回から見ていこうと思います。
実体はmm/page_alloc.cにあります。
========
/* コメントやprintkは省略してます */
void build_all_zonelists(void)
{
set_zonelist_order();
if (system_state == SYSTEM_BOOTING) {
/* すべてのノードのzonelistsを構築する */
__build_all_zonelists(NULL);
/* デバッグ用なので無視 */
mminit_verify_zonelist();
/* current->mems_allowedを初期化している。なんだ?? */
cpuset_init_current_mems_allowed();
} else {
/* ここに来たら異常。システムを停止させる */
stop_machine(__build_all_zonelists, NULL, NULL);
}
/* 全ノードの割り当て可能なページ数を取得 */
vm_total_pages = nr_free_pagecache_pages();
/* デバッグ用なので省略 */
}
========
要約するとゾーンリストを構築して、割り当て可能なページ数を「vm_total_pages」に格納する処理になります。
Linuxではページをゾーンという単位で管理しており、ゾーンリストというのはそれらゾーンをリストしたものです。
ゾーンリストについては下記を参考にしました。
Linux Kernel Hack Japan「build_all_zonelists()/linux2.6」
ここから呼び出している関数群についてはまた次回から見ていこうと思います。
2009年9月21日月曜日
preempt_disable
間があいてしまいました。
忘れないうちに今日はpreempt_disableを見てみましょう。
実体はinclude/linux/preempt.hにあります。
======
inc_preempt_countでプリエンプションのカウンタをインクリメントしています。preempt_enableのときは逆にデクリメントします。その後のbarrierは最適化しないでね、とコンパイラに指示するものです。
do-while文ですが条件が0固定なので1度しか実行されません。敢えてdo-whileにしているのでしょうが、なぜなのでしょう?
と思って調べてみたら解説がありました。
[ruby-dev:11748] Re: do while(0)
なるほど。。。
忘れないうちに今日はpreempt_disableを見てみましょう。
実体はinclude/linux/preempt.hにあります。
======
#define preempt_disable() \======
do { \
inc_preempt_count(); \
barrier(); \
} while (0)
inc_preempt_countでプリエンプションのカウンタをインクリメントしています。preempt_enableのときは逆にデクリメントします。その後のbarrierは最適化しないでね、とコンパイラに指示するものです。
do-while文ですが条件が0固定なので1度しか実行されません。敢えてdo-whileにしているのでしょうが、なぜなのでしょう?
と思って調べてみたら解説がありました。
[ruby-dev:11748] Re: do while(0)
なるほど。。。
2009年9月7日月曜日
smp_prepare_boot_cp(パス)
次に来るsmp_prepare_boot_cpはSMP構成のみ有効になるコードなのでパスします。
start_kernelの次の部分を見てみましょう。
=====
スケジューラの初期化です。
実体はkernel/sched.cにありますが、大きな関数なのでスケジューラ関係は
別の機会にまとめて見てみようと思います。
なのでその次。
=====
=====
次回から1つずつ追っていこうと思います。
start_kernelの次の部分を見てみましょう。
=====
/*=====
* Set up the scheduler prior starting any interrupts (such as the
* timer interrupt). Full topology setup happens at smp_init()
* time - but meanwhile we still have a functioning scheduler.
*/
sched_init();
スケジューラの初期化です。
実体はkernel/sched.cにありますが、大きな関数なのでスケジューラ関係は
別の機会にまとめて見てみようと思います。
なのでその次。
=====
/*
* Disable preemption - early bootup scheduling is extremely
* fragile until we cpu_idle() for the first time.
*/
preempt_disable();
build_all_zonelists();
page_alloc_init();
printk(KERN_NOTICE "Kernel command line: %s¥n", boot_command_line);
parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption);
if (!irqs_disabled()) {
printk(KERN_WARNING "start_kernel(): bug: interrupts were "
"enabled *very* early, fixing it¥n");
local_irq_disable();
}
sort_main_extable();
trap_init();
rcu_init();
=====
次回から1つずつ追っていこうと思います。
2009年9月6日日曜日
setup_nr_cpu_ids
今回はsetup_nr_cpu_idsを見てみます。
実体はinit/main.cにあります。
=====
=====
これだけ!
実体はinit/main.cにあります。
=====
static void __init setup_nr_cpu_ids(void)
{
/* 有効なプロセッサIDを取得 */
nr_cpu_ids = find_last_bit(cpumask_bits(cpu_possible_mask),NR_CPUS) + 1;
}
=====
これだけ!
2009年9月5日土曜日
setup_per_cpu_areas
今回はsetup_per_cpu_areasを見てみます。
実体はinit/main.cにあります。
=====
=====
__per_cpu_startセクションはarch/x86/kernel/vmlinux_32.lds.Sにあります。
実体はinit/main.cにあります。
=====
static void __init setup_per_cpu_areas(void)
{
unsigned long size, i;
char *ptr;
/* CPUの数を取得 */
unsigned long nr_possible_cpus = num_possible_cpus();
/* コピーに必要なサイズをとってくる */
size = ALIGN(PERCPU_ENOUGH_ROOM, PAGE_SIZE);
/* メモリを確保 */
ptr = alloc_bootmem_pages(size * nr_possible_cpus);
/* CPUの数だけ繰り返す */
for_each_possible_cpu(i) {
__per_cpu_offset[i] = ptr - __per_cpu_start;
/* 確保した領域に__per_cpu_startのセクションをコピーする */
memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start);
ptr += size;
}
}
=====
__per_cpu_startセクションはarch/x86/kernel/vmlinux_32.lds.Sにあります。
2009年9月3日木曜日
setup_command_line
今日はsetup_command_lineを見てみましょう。
実体はstart_kernelと同じinit/main.cにあります。
=====
=====
これはブート時のコマンドライン引数を保存しているだけですね。
実体はstart_kernelと同じinit/main.cにあります。
=====
static void __init setup_command_line(char *command_line)
{
saved_command_line = alloc_bootmem(strlen (boot_command_line)+1);
static_command_line = alloc_bootmem(strlen (command_line)+1);
strcpy (saved_command_line, boot_command_line);
strcpy (static_command_line, command_line);
}
=====
これはブート時のコマンドライン引数を保存しているだけですね。
2009年9月1日火曜日
mm_init_owner
今日はsetup_archを見てみましょう。
と、思ったのだけど非常に大規模なソースなので、ちょっとパス。start_kernelが見終わったら改めて見てみようと思います。実体はarch/x86/kernel/setup.cにあって、BIOSからとってきた情報をいろいろとセットしているみたい。
というわけで、ちょっとスキップして今回はmm_init_ownerを見てみます。
実体はkernel/fork.cにあります。
======
これだけ。
start_kernel内では
mm_init_owner(&init_mm, &init_task);
としているので、init_mm->ownerにinit_taskのアドレスを格納しています。
で、この2つはどこから来たのでしょう。
それぞれarch/x86/kernel/init_task.cに以下のようにあります。
=====
INIT_MM、INIT_TASKはinclude/linux/init_task.hにあります。
=====
=====
とinit_mm、init_taskの一部メンバを初期化しています。
と、思ったのだけど非常に大規模なソースなので、ちょっとパス。start_kernelが見終わったら改めて見てみようと思います。実体はarch/x86/kernel/setup.cにあって、BIOSからとってきた情報をいろいろとセットしているみたい。
というわけで、ちょっとスキップして今回はmm_init_ownerを見てみます。
実体はkernel/fork.cにあります。
======
void mm_init_owner(struct mm_struct *mm, struct task_struct *p)======
{
mm->owner = p;
}
これだけ。
start_kernel内では
mm_init_owner(&init_mm, &init_task);
としているので、init_mm->ownerにinit_taskのアドレスを格納しています。
で、この2つはどこから来たのでしょう。
それぞれarch/x86/kernel/init_task.cに以下のようにあります。
=====
struct mm_struct init_mm = INIT_MM(init_mm);=====
~(略)~
struct task_struct init_task = INIT_TASK(init_task);
INIT_MM、INIT_TASKはinclude/linux/init_task.hにあります。
=====
#define INIT_MM(name) \
{ \
.mm_rb = RB_ROOT,
.pgd = swapper_pg_dir, \
.mm_users = ATOMIC_INIT(2), \
.mm_count = ATOMIC_INIT(1), \
.mmap_sem = __RWSEM_INITIALIZER(name.mmap_sem), \
.page_table_lock = __SPIN_LOCK_UNLOCKED(name.page_table_lock), \
.mmlist = LIST_HEAD_INIT(name.mmlist), \
.cpu_vm_mask = CPU_MASK_ALL, \
}
~(略)~
#define INIT_TASK(tsk) \
{ \
.state = 0, \
.stack = &init_thread_info, \
.usage = ATOMIC_INIT(2), \
.flags = PF_KTHREAD, \
.lock_depth = -1, \
.prio = MAX_PRIO-20, \
.static_prio = MAX_PRIO-20, \
.normal_prio = MAX_PRIO-20, \
.policy = SCHED_NORMAL, \
.cpus_allowed = CPU_MASK_ALL, \
.mm = NULL, \
.active_mm = &init_mm, \
.se = { \
.group_node = LIST_HEAD_INIT(tsk.se.group_node), \
}, \
.rt = { \
.run_list = LIST_HEAD_INIT(tsk.rt.run_list), \
.time_slice = HZ, \
.nr_cpus_allowed = NR_CPUS, \
}, \
.tasks = LIST_HEAD_INIT(tsk.tasks), \
.ptraced = LIST_HEAD_INIT(tsk.ptraced), \
.ptrace_entry = LIST_HEAD_INIT(tsk.ptrace_entry), \
.real_parent = &tsk, \
.parent = &tsk, \
.children = LIST_HEAD_INIT(tsk.children), \
.sibling = LIST_HEAD_INIT(tsk.sibling), \
.group_leader = &tsk, \
.real_cred = &init_cred, \
.cred = &init_cred, \
.cred_exec_mutex = \
__MUTEX_INITIALIZER(tsk.cred_exec_mutex), \
.comm = "swapper", \
.thread = INIT_THREAD, \
.fs = &init_fs, \
.files = &init_files, \
.signal = &init_signals, \
.sighand = &init_sighand, \
.nsproxy = &init_nsproxy, \
.pending = { \
.list = LIST_HEAD_INIT(tsk.pending.list), \
.signal = {{0}}}, \
.blocked = {{0}}, \
.alloc_lock = __SPIN_LOCK_UNLOCKED(tsk.alloc_lock), \
.journal_info = NULL, \
.cpu_timers = INIT_CPU_TIMERS(tsk.cpu_timers), \
.fs_excl = ATOMIC_INIT(0), \
.pi_lock = __SPIN_LOCK_UNLOCKED(tsk.pi_lock), \
.timer_slack_ns = 50000, /* 50 usec default slack */ \
.pids = { \
[PIDTYPE_PID] = INIT_PID_LINK(PIDTYPE_PID), \
[PIDTYPE_PGID] = INIT_PID_LINK(PIDTYPE_PGID), \
[PIDTYPE_SID] = INIT_PID_LINK(PIDTYPE_SID), \
}, \
.dirties = INIT_PROP_LOCAL_SINGLE(dirties), \
INIT_IDS \
INIT_TRACE_IRQFLAGS \
INIT_LOCKDEP \
}
=====
とinit_mm、init_taskの一部メンバを初期化しています。
2009年8月31日月曜日
page_address_init
今日はpage_address_initを見てみましょう。
実体はmm/highmem.cにあります。
=====
=====
ページアドレスマップのリストとハッシュテーブルを初期化しています。
ページとアドレスの対応付けを管理する変数なのでしょう。
実体はmm/highmem.cにあります。
=====
void __init page_address_init(void)
{
int i;
INIT_LIST_HEAD(&page_address_pool);
for (i = 0; i < ARRAY_SIZE(page_address_maps); i++)
list_add(&page_address_maps[i].list, &page_address_pool);
for (i = 0; i < ARRAY_SIZE(page_address_htable); i++) {
INIT_LIST_HEAD(&page_address_htable[i].lh);
spin_lock_init(&page_address_htable[i].lock);
}
spin_lock_init(&pool_lock);
}
=====
ページアドレスマップのリストとハッシュテーブルを初期化しています。
ページとアドレスの対応付けを管理する変数なのでしょう。
2009年8月28日金曜日
boot_cpu_init
今回はboot_cpu_initを見ていきます。
実体はstart_kernelと同じinit/main.cにあります。
======
======
プロセッサIDはどこから拾ってくるのかsmp_processor_idを追ってみたけれど、スタックポインタの前あたりに置いてあるようです。いまいち追いきれていません。。
あとはビットを立てているようで、これはレジスタのbitなのかなと思ったのだけどどうやらカーネル内部で持っている構造体?のメンバのようなのだけど、ちっとも追えません。
ちょっと私には難易度高かったです。
実体はstart_kernelと同じinit/main.cにあります。
======
static void __init boot_cpu_init(void)
{
/* カレントCPUのプロセッサIDを持ってくる */
int cpu = smp_processor_id();
/* Mark the boot cpu "present", "online" etc for SMP and UP case */
set_cpu_online(cpu, true);
set_cpu_present(cpu, true);
set_cpu_possible(cpu, true);
}
======
プロセッサIDはどこから拾ってくるのかsmp_processor_idを追ってみたけれど、スタックポインタの前あたりに置いてあるようです。いまいち追いきれていません。。
あとはビットを立てているようで、これはレジスタのbitなのかなと思ったのだけどどうやらカーネル内部で持っている構造体?のメンバのようなのだけど、ちっとも追えません。
ちょっと私には難易度高かったです。
2009年8月26日水曜日
tick_init
お次はtick_initを見てみます。
tickを初期化しているのでしょう。
実体はkernel/time/tick-common.cにあります。
======
======
時間計測のイベントを受け取るための登録をしています。
clockevents_register_notifierは同じ階層のclockevents.cにあります。
======
/**
* clockevents_register_notifier - register a clock events change listener
*/
int clockevents_register_notifier(struct notifier_block *nb)
{
int ret;
spin_lock(&clockevents_lock);
ret = raw_notifier_chain_register(&clockevents_chain, nb);
spin_unlock(&clockevents_lock);
return ret;
}
======
こうしてイベントを通知してもらうためのリスナーチェインに登録しています。
この辺の仕組みはまた別の機会に、、、
tickを初期化しているのでしょう。
実体はkernel/time/tick-common.cにあります。
======
/**
* tick_init - initialize the tick control
*
* Register the notifier with the clockevents framework
*/
void __init tick_init(void)
{
clockevents_register_notifier(&tick_notifier);
}
======
時間計測のイベントを受け取るための登録をしています。
clockevents_register_notifierは同じ階層のclockevents.cにあります。
======
/**
* clockevents_register_notifier - register a clock events change listener
*/
int clockevents_register_notifier(struct notifier_block *nb)
{
int ret;
spin_lock(&clockevents_lock);
ret = raw_notifier_chain_register(&clockevents_chain, nb);
spin_unlock(&clockevents_lock);
return ret;
}
======
こうしてイベントを通知してもらうためのリスナーチェインに登録しています。
この辺の仕組みはまた別の機会に、、、
2009年8月24日月曜日
lock_kernel
start_kernelは少し進みます。
======
======
今回はlock_kernelを見てみましょう。
実体は「__acquires」という名前でarch/x86/kernel/cpu/mtrr/generic.cにあります。
======
======
まあ、なんか、いろいろとクリアしているみたいです。
======
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
lock_kernel();
tick_init();
boot_cpu_init();
page_address_init();
printk(KERN_NOTICE);
printk(linux_banner);
setup_arch(&command_line);
mm_init_owner(&init_mm, &init_task);
setup_command_line(command_line);
unwind_setup();
setup_per_cpu_areas();
setup_nr_cpu_ids();
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
======
今回はlock_kernelを見てみましょう。
実体は「__acquires」という名前でarch/x86/kernel/cpu/mtrr/generic.cにあります。
======
static void prepare_set(void) __acquires(set_atomicity_lock)
{
unsigned long cr0;
/* スピンロックを獲得 */
spin_lock(&set_atomicity_lock);
/* CR0レジストをCD=1、NW=0の状態にする */
cr0 = read_cr0() | X86_CR0_CD;
write_cr0(cr0);
wbinvd();
/* グローバルページ機能をDisableにする */
if ( cpu_has_pge ) {
cr4 = read_cr4();
write_cr4(cr4 & ~X86_CR4_PGE);
}
/* TLBをクリア */
__flush_tlb();
/* MTRRの値を読み取る */
rdmsr(MTRRdefType_MSR, deftype_lo, deftype_hi);
/* MTRRを無効にしてuncachableに設定する */
mtrr_wrmsr(MTRRdefType_MSR, deftype_lo & ~0xcff, deftype_hi);
}
======
まあ、なんか、いろいろとクリアしているみたいです。
2009年8月21日金曜日
early_init_irq_lock_class
前回のつづきで、「early_init_irq_lock_class」を見てみましょう。
実体はkernel/irq/handle.cにあります。
======
ロックの整合性を検証するlockdepのためにロックのマッピング情報を初期化しています。
ここもこれだけ。
実体はkernel/irq/handle.cにあります。
======
void early_init_irq_lock_class(void)======
{
int i;
for (i = 0; i < NR_IRQS; i++)
lockdep_set_class(&irq_desc[i].lock, &irq_desc_lock_class);
}
ロックの整合性を検証するlockdepのためにロックのマッピング情報を初期化しています。
ここもこれだけ。
early_boot_irqs_off
前回のつづきで、「early_boot_irqs_off」を見てみましょう。
実体はkernel/trace/trace_irqoff.cにあります。
======
空です。。
実体はkernel/trace/trace_irqoff.cにあります。
======
void early_boot_irqs_off(void)======
{
}
空です。。
登録:
投稿 (Atom)