当前位置:操作系统 > Unix/Linux >>

linux内核分析----初始化

作者:e4gle

  参考我提供的三个文件:bootsect.txt,head.txt,setup.txt

  至于x86的引导无非如下步骤:

  1,cpu初始化自身,在固定位置执行一条指令。

  2,这条指令条转到bios中。

  3,bios找到启动设备并获取mbr,该mbr指向我们的lilo

  4,bios装载并把控制权交给lilo

  5,压缩内核自解压,并把控制权转交给解压内核。

  简单点讲,就是cpu成为内核引导程序的引导程序的引导程序的引导程序,西西。

  这时内核将跳转到start_kernel是/init/main.c的重点函数,main.c函数很多定义都是为此函数服务的,这里我简要介绍一下这个函数的初始化流程。

  初始化内核:

  从start_kernel函数(/init/main.c)开始系统初始化工作,好,我们首先分析这个函数:

  函数开始首先:

  #ifdef __SMP__

  static int boot_cpu = 1;

  /* "current" has been set up, we need to load it now *//*定义双处理器用*/

  if (!boot_cpu)

  initialize_secondary();

  boot_cpu = 0;

  #endif

  定义双处理器。

  printk(linux_banner); /*打印linux banner*/

  打印内核标题信息。

  开始初始化自身的部分组件(包括内存,硬件终端,调度等),我来逐个分析其中的函数:

  setup_arch(&command_line, &memory_start, &memory_end);/*初始化内存*/

  返回内核参数和内核可用的物理地址范围

  函数原型如下:

  setup_arch(char **, unsigned long *, unsigned long *);

  返回内存起始地址:

  memory_start = paging_init(memory_start,memory_end);

  看看paging_init的定义,是初始化请求页:

  paging_init(unsigned long start_mem, unsigned long end_mem)(会在以后的内存管理子系统分析时详细介绍)

  {

  int i;

  struct memclust_struct * cluster;

  struct memdesc_struct * memdesc;

  /* initialize mem_map[] */

  start_mem = free_area_init(start_mem, end_mem);/*遍历查找内存的空闲页*/

  /* find free clusters, update mem_map[] accordingly */

  memdesc = (struct memdesc_struct *)

  (hwrpb->mddt_offset + (unsigned long) hwrpb);

  cluster = memdesc->cluster;

  for (i = memdesc->numclusters ; i > 0; i--, cluster++) {

  unsigned long pfn, nr;

  /* Bit 0 is console/PALcode reserved. Bit 1 is

  non-volatile memory -- we might want to mark

  this for later */

  if (cluster->usage & 3)

  continue;

  pfn = cluster->start_pfn;

  if (pfn >= MAP_NR(end_mem)) /* if we overrode mem size */

  continue;

  nr = cluster->numpages;

  if ((pfn + nr) > MAP_NR(end_mem)) /* if override in cluster */

  nr = MAP_NR(end_mem) - pfn;

  while (nr--)

  clear_bit(PG_reserved, &mem_map[pfn++].flags);

  }

  memset((void *) ZERO_PAGE(0), 0, PAGE_SIZE);

  return start_mem;

  }

  trap_init(); 初始化硬件中断

  /arch/i386/kernel/traps.c文件里定义此函数

  sched_init() 初始化调度

  /kernel/sched.c文件里有详细的调度算法(这些会在以后进程管理和调度的结构分析中详细介绍)

  parse_options(command_line) 分析传给内核的各种选项(随后再详细介绍)

  memory_start = console_init(memory_start,memory_end) 初始化控制台

  memory_start = kmem_cache_init(memory_start, memory_end) 初始化内核内存cache(同样,在以后的内存管理分析中介绍此函数)

  sti();接受硬件中断

  kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);

  current->need_resched = 1; need_resched标志增加,调用schedule(调度里面会详细说明)

  cpu_idle(NULL) 进入idle循环以消耗空闲的cpu时间片

  已经基本完成内核初始化工作,已经把需要完成的少量责任传递给了init,所身于地工作不过是进入idle循环以消耗空闲的cpu时间片。所以在这里调用了cpu_idle(NULL),它从不返回,所以当有实际工作好处理时,该函数就会被抢占。

  parse_options函数:

  static void __init parse_options(char *line)/*参数收集在一条长命令行中,内核被赋给指向该命令行头部的指针*/

  {

  char *next;

  char *quote;

  int args, envs;

  if (!*line)

  return;

  args = 0;

  envs = 1;/* TERM is set to 'linux' by default */

  next = line;

  while ((line = next) != NULL) {

  quote = strchr(line,'"');

  next = strchr(line, ' ');

  while (next != NULL && quote != NULL && quote < next) {

  next = strchr(quote+1, '"');

  if (next != NULL) {

  quote = strchr(next+1, '"');

  next = strchr(next+1, ' ');

  }

  }

  if (next != NULL)

  *next++ = 0;

  /*

  * check for kernel options first..

  */

  if (!strcmp(line,"ro")) {

  root_mountflags |= MS_RDONLY;

  continue;

  }

  if (!strcmp(line,"rw")) {

  root_mountflags &= ~MS_RDONLY;

  continue;

  }

  if (!strcmp(line,"debug")) {

  console_loglevel = 10;

  continue;

  }

  if (!strcmp(line,"quiet")) {

  console_loglevel = 4;

  continue;

  }

  if (!strncmp(line,"init=",5)) {

  line += 5;

  execute_command = line;

  args = 0;

  continue;

  }

  if (checksetup(line))

  continue;

  if (strchr(line,'=')) {

  if (envs >= MAX_INIT_ENVS)

  break;

  envp_init[++envs] = line;

  } else {

  if (args >= MAX_INIT_ARGS)

  break;

  argv_init[++args] = line;

  }

  }

  argv_init[args+1] = NULL;

  envp_init[envs+1] = NULL;

  }

  ////////////////////////////////////////////////////////////////////

  // setup.txt

  // Copyright(C) 2001, Feiyun Wang

  ////////////////////////////////////////////////////////////////////

  // 易做图ysis on linux/arch/i386/boot/setup.S (for Linux 2.2.17)

  ////////////////////////////////////////////////////////////////////

  ////////////////////////////////////////////////////////////////////

  // export the margin tags for .text, .data and .bss

  {

  .text

  begtext:

  .data

  begdata:

  .bss

  begbss:

  }

  ////////////////////////////////////////////////////////////////////

  .text

  start()

  SYSSEG = 0x1000

  SETUPSEG = 0x9020

  modelist = end of .text:

  {

  // if loaded by bootsect.S,

  // you can assume CS=SETUPSEG (=0x9020), otherwise...

  goto start_of_setup();

  /*

  http://lxr.linux.no/source/Documentation/i386/boot.txt

  Offset/Size Proto Name Meaning

  01F1/1 ALL setup_sects The size of the setup in sectors

  01F2/2 ALL root_flags If set, the root is mounted readonly

  01F4/2 ALL syssize DO NOT USE - for bootsect.S use only

  01F6/2 ALL swap_dev DO NOT USE - obsolete

  01F8/2 ALL ram_size DO NOT USE - for bootsect.S use only

 
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,