当前位置导航:炫浪网>>网络学院>>操作系统>>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
  ////////////////////////////////////////////////////////////////////
  // analysis 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
  01FA/2 ALL vid_mode Video mode control
  01FC/2 ALL root_dev Default root device number
  01FE/2 ALL boot_flag 0xAA55 magic number
  0200/2 2.00+ jump Jump instruction
  0202/4 2.00+ header Magic signature "HdrS"
  0206/2 2.00+ version Boot protocol version supported
  0208/4 2.00+ realmode_swtch Boot loader hook
  020C/4 2.00+ start_sys_seg Points to kernel version string
  0210/1 2.00+ type_of_loader Boot loader identifier
  0211/1 2.00+ loadflags Boot protocol option flags
  0212/2 2.00+ setup_move_size Move to high memory size (used with hooks)
  0214/4 2.00+ code32_start Boot loader hook
  0218/4 2.00+ ramdisk_ima
相关内容
赞助商链接