linux/init/main.c

Bu bölümü yazarken kendimi suçlu hissettim çünkü yeterince olmasa bile hakkında çok sayıda belge var. start_kernel() destekli işlevler, sürekli gelişen işletim sistemi dahili bileşenlerine bağımlı olduğu için, sürümden sürüme değişir. Sık sık belge güncellemek için vaktim olmadığından bu bölümü olabildiğince basit tutmaya karar verdim.

start_kernel()

///////////////////////////////////////////////////////////////////////////////
asmlinkage void __init start_kernel(void)
{
  char * command_line;
  extern char saved_command_line[];
/*
 * Kesmeler hala etkin değil. Gerekli kurulumu yap, sonra etkinleştir
 */
  lock_kernel();
  printk(linux_banner);

  /* Linux'da bellek yönetimi, esp. for setup_arch()
    * Linux-2.4.4 MM Başlangıç durumuna getirme */
  setup_arch(&command_line);
  printk("Kernel command line: %s\n", saved_command_line);

  /* linux/Documentation/kernel-parameters.txt
    * The Linux BootPrompt-HowTo */
  parse_options(command_line);

  trap_init() {
#ifdef CONFIG_EISA
    if (isa_readl(0x0FFFD9) == 'E'+('I'<<8)+('S'<<16)+('A'<<24))
      EISA_bus = 1;
#endif
#ifdef CONFIG_X86_LOCAL_APIC
    init_apic_mappings();
#endif
    set_xxxx_gate(x, &func);    // kurulum kapıları
    cpu_init();
  }
  init_IRQ();
  sched_init();
  softirq_init() {
    for (int i=0; i<32: i++)
            tasklet_init(bh_task_vec+i, bh_action, i);
    open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
    open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
  }
  time_init();

  /*
    * HACK ALERT! Bu erken. PCI ve bunun gibi kurulumları bitirdikten
    * ve console_init()'in bunu farketmesinde önce konsolu etkinleştirmeliyiz.
    * Birşeylerin kötü gitmesi durumunda bunun erkenden çıktı olmasını isteriz.
    */
  console_init();
#ifdef CONFIG_MODULES
  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);
  }

  kmem_cache_init();
  sti();

  // BogoMips mini-Howto
  calibrate_delay();

  // linux/Documentation/initrd.txt
#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();
  kmem_cache_sizes_init();
  pgtable_cache_init();

  /*
    * Yüksek belleğe (highmem) sahip olan mimariler için, num_mappedpages
    * çekirdeğin kullanabileceği bellek miktarını ifade eder. Diğer mimariler için
    * toplam sayfa ile aynıdır. Her iki rakama da ihtiyaç duyarız çünkü bazı
    * altsistemler çekirdeğin ne kadar bellek kullanabileceğine dayanarak
    * başlangıç durumuna getirilir.
    */
  if (num_mappedpages == 0)
          num_mappedpages =  num_physpages;

  fork_init(num_mempages);
  proc_caches_init();
  vfs_caches_init(num_physpages);
  buffer_init(num_physpages);
  page_cache_init(num_physpages);
#if defined(CONFIG_ARCH_S390)
  ccwcache_init();
#endif
  signals_init();
#ifdef CONFIG_PROC_FS
  proc_root_init();
#endif
#if defined(CONFIG_SYSVIPC)
  ipc_init();
#endif
  check_bugs();
  printk("POSIX conformance testing by UNIFIX\n");

  /*
    *      İlk işlemdeki (thread) iyi gidenleri sayarız
    *      atıl (idlers) gibi init de kilitsiz bir çekirdek işlemidir,
    *      sistem çağrısı yapar (ve böylece kilitlenir).
    */
  smp_init() {
#ifndef CONFIG_SMP
# ifdef CONFIG_X86_LOCAL_APIC
    APIC_init_uniprocessor();
#  else
    do { } while (0);
# endif
#else
    /* Check  smp_init(). */
#endif
  }

  rest_init() {
    // init process, pid = 1
    kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
    unlock_kernel();
    current->need_resched = 1;
    // idle process, pid = 0
    cpu_idle();     // never return
  }
}

start_kernel() "init" işlemi oluşturmak için rest_init()'i çağırır ve kendisi "idle" işlem durumuna geçer.

init()

"Init" süreci:

///////////////////////////////////////////////////////////////////////////////
static int init(void * unused)
{
  lock_kernel();
  do_basic_setup();

  prepare_namespace();

  /*
    * Tamam, ilk önyüklemeyi (bootup) tamamladık, ayakta ve
    * çalışır durumdayız. initmem bölütlerinden kurtul ve
    * kullanıcı kipini başlat...
    */
  free_initmem();
  unlock_kernel();

  if (open("/dev/console", O_RDWR, 0) < 0)        // stdin
          printk("Warning: unable to open an initial console.\n");

  (void) dup(0);                                  // stdout
  (void) dup(0);                                  // stderr

  /*
    * Biri başarılı olana kadar her birini deneyeceğiz
    * Gerçekten bozuk bir makinayı toparlamaya çalışıyorsak
    * init yerine Bourne kabuğu kullanılabilir.
    */

  if (execute_command)
          execve(execute_command,argv_init,envp_init);
  execve("/sbin/init",argv_init,envp_init);
  execve("/etc/init",argv_init,envp_init);
  execve("/bin/init",argv_init,envp_init);
  execve("/bin/sh",argv_init,envp_init);
  panic("No init found.  Try passing init= option to kernel.");
}

Kullanıcı kipi "init" süreciyle ilgili bilgiler için man init veya SysVinit'e bakınız.

cpu_idle()

"Idle" süreç:

/*
 * Boşta bekleme (idle) evresi. Yapılacak yararlı bir iş yok,
 * bu yüzden sadece gücü korumaya çalış ve düşük çıkış gecikmesine
 * sahip ol (örn. birinin yeniden iş yapma isteği
 * belirtmesini bekleyen bir döngü içinde kal)
 */
void cpu_idle (void)
{
  /* hiç bir önceliği olmayan sonsuz atıl döngü */
  init_idle();
  current->nice = 20;
  current->counter = -100;

  while (1) {
    void (*idle)(void) = pm_idle;
    if (!idle)
      idle = default_idle;
    while (!current->need_resched)
      idle();
    schedule();
    check_pgt_cache();
  }
}

///////////////////////////////////////////////////////////////////////////////
void __init init_idle(void)
{
  struct schedule_data * sched_data;
  sched_data = &aligned_data[smp_processor_id()].schedule_data;

  if (current != &init_task && task_on_runqueue(current)) {
    printk("UGH! (%d:%d) was on the runqueue, removing.\n",
            smp_processor_id(), current->pid);
    del_from_runqueue(current);
  }
  sched_data->curr = current;
  sched_data->last_schedule = get_cycles();
  clear_bit(current->processor, &wait_init_idle);
}

///////////////////////////////////////////////////////////////////////////////
void default_idle(void)
{
  if (current_cpu_data.hlt_works_ok && !hlt_counter) {
    __cli();
    if (!current->need_resched)
      safe_halt();
    else
      __sti();
  }
}

/* linux/include/asm-i386/system.h içinde tanımlı */
#define __cli()                 __asm__ __volatile__("cli": : :"memory")
#define __sti()                 __asm__ __volatile__("sti": : :"memory")

/* atıl döngü içinde kullanıldı; sti'nin tamamlanması bir komut süresi alır */
#define safe_halt()             __asm__ __volatile__("sti; hlt": : :"memory")

İşlemci bir kesme eylemcisinden dönen "hlt"yi takip eden komut ile kod çalıştırmaya devam edecektir.