asmlinkage void __init start_kernel(void)
{
char * command_line;
unsigned long mempages;
extern char saved_command_line[];
/*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
/*锁内核*/
lock_kernel();
/*打印内核的版本和编译的信息*/
printk(linux_banner);
/*解析内核的命令行中与内存相关的信息和内存分布信息*/
setup_arch(&command_line);
/*打印命令行信息*/
printk("Kernel command line: %s\n", saved_command_line);
/*解析传递给内核的命令行中的0号进程的程序名和环境变量*/
parse_options(command_line);
/*常用中断,异常的初始化*/
trap_init();
/*非常用的中断初始化*/
init_IRQ();
/*调度相关的计时器和底半部的初始化*/
sched_init();
/*时钟初始化*/
time_init();
/*软中断tasklet初始化*/
softirq_init();
/*
* HACK ALERT! This is early. We're enabling the console before
* we've done PCI setups etc, and console_init() must be aware of
* this. But we do want output early, in case something goes wrong.
*/
/*终端初始化*/
console_init();
#ifdef CONFIG_MODULES
/*初始化模块symbol表大小*/
init_modules();
#endif
if (prof_shift) {
unsigned int size;
/* only text is profiled */
prof_len = (unsigned long) &_etext - (unsigned long) &_stext;
prof_len >>= prof_shift;
size = prof_len * sizeof(unsigned int) + PAGE_SIZE-1;
prof_buffer = (unsigned int *) alloc_bootmem(size);
}
/*初始化slab分配器*/
kmem_cache_init();
sti();
calibrate_delay();
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && !initrd_below_start_ok &&
initrd_start < min_low_pfn << PAGE_SHIFT) {
printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
"disabling it.\n",initrd_start,min_low_pfn << PAGE_SHIFT);
initrd_start = 0;
}
#endif
/*设置高端内存和内存的标志位*/
mem_init();
/*初始化内部和一般的slab分配器*/
kmem_cache_sizes_init();
#ifdef CONFIG_3215_CONSOLE
con3215_activate();
#endif
#ifdef CONFIG_PROC_FS
/*建立proc文件系统的目录*/
proc_root_init();
#endif
mempages = num_physpages;
/*初始化最大线程数*/
fork_init(mempages);
/*创建一些常用的slab分配器的数据结构*/
proc_caches_init();
vfs_caches_init(mempages);
/*初始化buffer数据结构*/
buffer_init(mempages);
/*初始化页表的缓冲结构*/
page_cache_init(mempages);
kiobuf_setup();
/*创建signal的slab数据结构*/
signals_init();
bdev_init();
/*初始化文件系统的inode结构*/
inode_init(mempages);
#if defined(CONFIG_SYSVIPC)
/*初始化sysv的信号量,消息,共享内存*/
ipc_init();
#endif
#if defined(CONFIG_QUOTA)
dquot_init_hash();
#endif
check_bugs();
printk("POSIX conformance testing by UNIFIX\n");
/*
*
We count on the initial thread going ok
*
Like idlers init is an unlocked kernel thread, which will
*
make syscalls (and thus be locked).
*/
/*初始化SMP,主要是APIC的初始化*/
smp_init();
/*创建init进程*/
kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
unlock_kernel();
current->need_resched = 1;
/*运行idle进程,进行调度*/
cpu_idle();
}
void __init setup_arch(char **cmdline_p)
{
unsigned long bootmap_size;
unsigned long start_pfn, max_pfn, max_low_pfn;
int i;
#ifdef CONFIG_VISWS
visws_get_board_type_and_rev();
#endif
/*将rootfs转化成kdev的表示形式,这里跟传统的表示没有不同*/
ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV);
drive_info = DRIVE_INFO;
screen_info = SCREEN_INFO;
apm_info.bios = APM_BIOS_INFO;
/*将系统的描述信息写入全局变量中*/
if( SYS_DESC_TABLE.length != 0 ) {
MCA_bus = SYS_DESC_TABLE.table[3] &0x2;
machine_id = SYS_DESC_TABLE.table[0];
machine_submodel_id = SYS_DESC_TABLE.table[1];
BIOS_revision = SYS_DESC_TABLE.table[2];
}
aux_device_present = AUX_DEVICE_INFO;
#ifdef CONFIG_BLK_DEV_RAM
/*设置RAMDISK的标志*/
rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK;
rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0);
rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0);
#endif
setup_memory_region();
if (!MOUNT_ROOT_RDONLY)
root_mountflags &= ~MS_RDONLY;
/*在init_mm存放数据段,代码段和堆栈段的起始地址,结束地址*/
init_mm.start_code = (unsigned long) &_text;
init_mm.end_code = (unsigned long) &_etext;
init_mm.end_data = (unsigned long) &_edata;
init_mm.brk = (unsigned long) &_end;
/*存放内核和数据段的起始和结束地址,这里是转化为物理地址存放的*/
code_resource.start = virt_to_bus(&_text);
code_resource.end = virt_to_bus(&_etext)-1;
data_resource.start = virt_to_bus(&_etext);
data_resource.end = virt_to_bus(&_edata)-1;
/*解析命令行中的"mem="参数*/
parse_mem_cmdline(cmdline_p);
#define PFN_UP(x)
(((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
#define PFN_DOWN(x)
((x) >> PAGE_SHIFT)
#define PFN_PHYS(x)
((x) << PAGE_SHIFT)
/*
* 128MB for vmalloc and initrd
*/
#define VMALLOC_RESERVE
(unsigned long)(128 << 20)
#define MAXMEM
(unsigned long)(-PAGE_OFFSET-VMALLOC_RESERVE)
#define MAXMEM_PFN
PFN_DOWN(MAXMEM)
#define MAX_NONPAE_PFN
(1 << 20)
/*
* partially used pages are not usable - thus
* we are rounding upwards:
*/
/*找到起始物理页号*/
start_pfn = PFN_UP(__pa(&_end));
/*
* Find the highest page frame number we have available
*/
/*从E820中找到最高物理页号*/
max_pfn = 0;
for (i = 0; i < e820.nr_map; i++) {
unsigned long start, end;
/* RAM? */
if (e820.map[i].type != E820_RAM)
continue;
start = PFN_UP(e820.map[i].addr);
end = PFN_DOWN(e820.map[i].addr + e820.map[i].size);
if (start >= end)
continue;
if (end > max_pfn)
max_pfn = end;
}
/*
* Determine low and high memory ranges:
*/
/*找到最高的低端物理页号,既896M对应的物理页号*/
max_low_pfn = max_pfn;
if (max_low_pfn > MAXMEM_PFN) {
max_low_pfn = MAXMEM_PFN;
#ifndef CONFIG_HIGHMEM
/* Maximum memory usable is what is directly addressable */
printk(KERN_WARNING "Warning only %ldMB will be used.\n",
MAXMEM>>20);
if (max_pfn > MAX_NONPAE_PFN)
printk(KERN_WARNING "Use a PAE enabled kernel.\n");
else
printk(KERN_WARNING "Use a HIGHMEM enabled kernel.\n");
#else /* !CONFIG_HIGHMEM */
#ifndef CONFIG_X86_PAE
if (max_pfn > MAX_NONPAE_PFN) {
max_pfn = MAX_NONPAE_PFN;
printk(KERN_WARNING "Warning only 4GB will be used.\n");
printk(KERN_WARNING "Use a PAE enabled kernel.\n");
}
#endif /* !CONFIG_X86_PAE */
#endif /* !CONFIG_HIGHMEM */
}
/*设置高端内存的起始和结束地址*/
#ifdef CONFIG_HIGHMEM
highstart_pfn = highend_pfn = max_pfn;
if (max_pfn > MAXMEM_PFN) {
highstart_pfn = MAXMEM_PFN;
printk(KERN_NOTICE "%ldMB HIGHMEM available.\n",
pages_to_mb(highend_pfn - highstart_pfn));
}
#endif
/*
* Initialize the boot-time allocator (with low memory only):
*/
/*初始化896M以下的boot内存*/
bootmap_size = init_bootmem(start_pfn, max_low_pfn);
/*
* Register fully available low RAM pages with the bootmem allocator.
*/
/*将896M以下的内存设为可用状态*/
for (i = 0; i < e820.nr_map; i++) {
unsigned long curr_pfn, last_pfn, size;
/*
* Reserve usable low memory
*/
if (e820.map[i].type != E820_RAM)
continue;
/*
* We are rounding up the start address of usable memory:
*/
curr_pfn = PFN_UP(e820.map[i].addr);
if (curr_pfn >= max_low_pfn)
continue;
/*
* ... and at the end of the usable range downwards:
*/
last_pfn = PFN_DOWN(e820.map[i].addr + e820.map[i].size);
if (last_pfn > max_low_pfn)
last_pfn = max_low_pfn;
/*
* .. finally, did all the rounding and playing
* around just make the area go away?
*/
if (last_pfn <= curr_pfn)
continue;
size = last_pfn - curr_pfn;
free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(size));
}
/*
* Reserve the bootmem bitmap itself as well. We do this in two
* steps (first step was init_bootmem()) because this catches
* the (very unlikely) case of us accidentally initializing the
* bootmem allocator with an invalid RAM area.
*/
/*保留bootmem自己的内存*/
reserve_bootmem(HIGH_MEMORY, (PFN_PHYS(start_pfn) +
bootmap_size + PAGE_SIZE-1) - (HIGH_MEMORY));
/*
* reserve physical page 0 - it's a special BIOS page on many boxes,
* enabling clean reboots, SMP operation, laptop functions.
*/
/*保留第一个物理页*/
reserve_bootmem(0, PAGE_SIZE);
#ifdef CONFIG_SMP
/*
* But first pinch a few for the stack/trampoline stuff
* FIXME: Don't need the extra page at 4K, but need to fix
* trampoline before removing it. (see the GDT stuff)
*/
reserve_bootmem(PAGE_SIZE, PAGE_SIZE);
smp_alloc_memory(); /* AP processor realmode stacks in low memory*/
#endif
#ifdef CONFIG_X86_IO_APIC
/*
* Find and reserve possible boot-time SMP configuration:
*/
find_smp_config();
#endif
/*初始化构建页表*/
paging_init();
#ifdef CONFIG_X86_IO_APIC
/*
* get boot-time SMP configuration:
*/
if (smp_found_config)
get_smp_config();
#endif
#ifdef CONFIG_X86_LOCAL_APIC
init_apic_mappings();
#endif
#ifdef CONFIG_BLK_DEV_INITRD
/*将RAMDISK的空间保留下来*/
if (LOADER_TYPE && INITRD_START) {
if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) {
reserve_bootmem(INITRD_START, INITRD_SIZE);
initrd_start =
INITRD_START ? INITRD_START + PAGE_OFFSET : 0;
initrd_end = initrd_start+INITRD_SIZE;
}
else {
printk("initrd extends beyond end of memory "
"(0x%08lx > 0x%08lx)\ndisabling initrd\n",
INITRD_START + INITRD_SIZE,
max_low_pfn << PAGE_SHIFT);
initrd_start = 0;
}
}
#endif
/*
* Request address space for all standard RAM and ROM resources
* and also for regions reported as reserved by the e820.
*/
/*将ROM加入资源列表中*/
probe_roms();
/*将RAM等各种资源加入列表中*/
for (i = 0; i < e820.nr_map; i++) {
struct resource *res;
if (e820.map[i].addr + e820.map[i].size > 0x100000000ULL)
continue;
res = alloc_bootmem_low(sizeof(struct resource));
switch (e820.map[i].type) {
case E820_RAM:
res->name = "System RAM"; break;
case E820_ACPI:
res->name = "ACPI Tables"; break;
case E820_NVS:
res->name = "ACPI Non-volatile Storage"; break;
default:
res->name = "reserved";
}
res->start = e820.map[i].addr;
res->end = res->start + e820.map[i].size - 1;
res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
request_resource(&iomem_resource, res);
if (e820.map[i].type == E820_RAM) {
/*
* We dont't know which RAM region contains kernel data,
* so we try it repeatedly and let the resource manager
* test it.
*/
request_resource(res, &code_resource);
request_resource(res, &data_resource);
}
}
request_resource(&iomem_resource, &vram_resource);
/* request I/O space for devices used on all i[345]86 PCs */
for (i = 0; i < STANDARD_IO_RESOURCES; i++)
request_resource(&ioport_resource, standard_io_resources+i);
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
}
setup_arch->setup_memory_region 这个表示调用关系
void __init setup_memory_region(void)
{
char *who = "BIOS-e820";
/*
* Try to copy the BIOS-supplied E820-map.
*
* Otherwise fake a memory map; one section from 0k->640k,
* the next section from 1mb->appropriate_mem_k
*/
/*拷贝E820得到的内存分配图,如果E820没有获得内存分配图,自己创造一个分布图*/
if (copy_e820_map(E820_MAP, E820_MAP_NR) < 0) {
unsigned long mem_size;
/* compare results from other methods and take the greater */
if (ALT_MEM_K < EXT_MEM_K) {
mem_size = EXT_MEM_K;
who = "BIOS-88";
} else {
mem_size = ALT_MEM_K;
who = "BIOS-e801";
}
/*创造两个内存分配,0-0x9f000, 1M-E801或者88找到的内存的最大值*/
e820.nr_map = 0;
add_memory_region(0, LOWMEMSIZE(), E820_RAM);
add_memory_region(HIGH_MEMORY, (mem_size << 10) - HIGH_MEMORY, E820_RAM);
}
printk("BIOS-provided physical RAM map:\n");
/*根据打印内存的大小和起始地址和类型*/
print_memory_map(who);
}
setup_arch->setup_memory_region->copy_e820_map
/*
* Copy the BIOS e820 map into a safe place.
*
* Sanity-check it while we're at it..
*
* If we're lucky and live on a modern system, the setup code
* will have given us a memory map that we can use to properly
* set up memory. If we aren't, we'll fake a memory map.
*
* We check to see that the memory map contains at least 2 elements
* before we'll use it, because the detection code in setup.S may
* not be perfect and most every PC known to man has two memory
* regions: one from 0 to 640k, and one from 1mb up. (The IBM
* thinkpad 560x, for example, does not cooperate with the memory
* detection code.)
*/
static int __init copy_e820_map(struct e820entry * biosmap, int nr_map)
{
/* Only one memory region (or negative)? Ignore it */
if (nr_map < 2)
return -1;
do {
unsigned long long start = biosmap->addr;
unsigned long long size = biosmap->size;
unsigned long long end = start + size;
unsigned long type = biosmap->type;
/* Overflow in 64 bits? Ignore the memory map. */
if (start > end)
return -1;
/*
* Some BIOSes claim RAM in the 640k - 1M region.
* Not right. Fix it up.
*/
/*将跨0xA0000-0x100000的内存分开,这一段是系统使用的*/
if (type == E820_RAM) {
if (start < 0x100000ULL && end > 0xA0000ULL) {
if (start < 0xA0000ULL)
add_memory_region(start, 0xA0000ULL-start, type);
if (end <= 0x100000ULL)
continue;
start = 0x100000ULL;
size = end - start;
}
}
/*记录分配的内存的开始地址,大小和类型*/
add_memory_region(start, size, type);
} while (biosmap++,--nr_map);
return 0;
}
setup_arch->setup_memory_region->copy_e820_map->add_memory_region
void __init add_memory_region(unsigned long long start,
unsigned long long size, int type)
{
int x = e820.nr_map;
if (x == E820MAX) {
printk("Ooops! Too many entries in the memory map!\n");
return;
}
/*记录内存分布的开始,大小和类型*/
e820.map[x].addr = start;
e820.map[x].size = size;
e820.map[x].type = type;
e820.nr_map++;
} /* add_memory_region */
start_kernel->setup_arch->parse_mem_cmdline
static inline void parse_mem_cmdline (char ** cmdline_p)
{
char c = ' ', *to = command_line, *from = COMMAND_LINE;
int len = 0;
int usermem = 0;
/* Save unparsed command line copy for /proc/cmdline */
memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE);
saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
for (;;) {
/*
* "mem=nopentium" disables the 4MB page tables.
* "mem=XXX[kKmM]" defines a memory region from HIGH_MEM
* to <mem>, overriding the bios size.
* "mem=XXX[KkmM]@XXX[KkmM]" defines a memory region from
* <start> to <start>+<mem>, overriding the bios size.
*/
/*解析mem=参数*/
if (c == ' ' && !memcmp(from, "mem=", 4)) {
if (to != command_line)
to--;
/*如果是mem=nopentium,将PSE的标志去掉*/
if (!memcmp(from+4, "nopentium", 9)) {
from += 9+4;
clear_bit(X86_FEATURE_PSE, &boot_cpu_data.x86_capability);
/*通过exactmap, mem=xxxx@XXXX,完全使用命令行上指定的参数*/
} else if (!memcmp(from+4, "exactmap", 8)) {
from += 8+4;
e820.nr_map = 0;
usermem = 1;
} else {
/* If the user specifies memory size, we
* blow away any automatically generated
* size
*/
unsigned long start_at, mem_size;
/*如果命令行没有指定exactmap参数,将0-0x9f000加入到内存分布表中*/
if (usermem == 0) {
/* first time in: zap the whitelist
* and reinitialize it with the
* standard low-memory region.
*/
e820.nr_map = 0;
usermem = 1;
add_memory_region(0, LOWMEMSIZE(), E820_RAM);
}
/*解析出内存块的大小*/
mem_size = memparse(from+4, &from);
/*解析出内存块的开始地址,如果没指定就用1M做为起始地址*/
if (*from == '@')
start_at = memparse(from+1, &from);
else {
start_at = HIGH_MEMORY;
mem_size -= HIGH_MEMORY;
usermem=0;
}
/*将内存块加入到内存分布表中*/
add_memory_region(start_at, mem_size, E820_RAM);
}
}
c = *(from++);
if (!c)
break;
if (COMMAND_LINE_SIZE <= ++len)
break;
*(to++) = c;
}
*to = '\0';
*cmdline_p = command_line;
if (usermem) {
printk("user-defined physical RAM map:\n");
print_memory_map("user");
}
}