Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
iakovlev.org

KernelAnalysis

Roberto Arcomano
 
   Copyright (C) 2000,2001,2002 Roberto Arcomano.
Директория /usr/src/linux подразумевается как стартовая В данном документе для рассмотрения того , как функции вызывают друг друга , используется т.н. префикс "InterCallings Analysis "(ICA)" , например , команда the sleep_on : color=blue> |sleep_on |init_waitqueue_entry -- |__add_wait_queue | enqueuing request |list_add | |__list_add -- |schedule --- waiting for request to be executed |__remove_wait_queue -- |list_del | dequeuing request |__list_del -- В примере function1 -> function2 < function1 > есть указатель на другую функцию . Когда мы пишем function: , то это как правило метка .

Итак , что же такое ядро ? Это сердцевина компьютерной системы , которая позволяет конечным пользователям использовать ресурсы компьютера , которая - как в случае с виндами - может включать в себя графическое управление . В чем разница между пользовательским режимом и режимом ядра ?
   1. Kernel Mode: в этом случае машина оперирует с критическими 
      структурами данных - памятью , маппмнгом , IRQ , DMA и т.д.
   2. User Mode: пользовательские приложения
Процессоры Intel могут находиться в 4-х состояниях - 0,1,2,3 , и 0 - это Kernel Mode. Линукс использует лишь 2 уровня из 4-х .
 Когда происходит переключение из одного режима в другой - как правило :
   1. При вызове System Call: вызывается код в Kernel Mode
   2. При генерации прерывания или исключения
 Системные функции могут быть вызваны при :
        когда происходит вызов I/O device или file
        когда нужно вычислить  pid, изменить scheduling policy
        вызов fork
        выполнение команды
Ниже представлен список System Calls для 2.4.17, [ arch/i386/kernel/entry.S ]
 long SYMBOL_NAME(sys_ni_syscall)/* 0  -  old "setup()" system call*/
           .long SYMBOL_NAME(sys_exit)
           .long SYMBOL_NAME(sys_fork)
           .long SYMBOL_NAME(sys_read)
           .long SYMBOL_NAME(sys_write)
           .long SYMBOL_NAME(sys_open)             /* 5 */
           .long SYMBOL_NAME(sys_close)
           .long SYMBOL_NAME(sys_waitpid)
           .long SYMBOL_NAME(sys_creat)
           .long SYMBOL_NAME(sys_link)
           .long SYMBOL_NAME(sys_unlink)           /* 10 */
           .long SYMBOL_NAME(sys_execve)
           .long SYMBOL_NAME(sys_chdir)
           .long SYMBOL_NAME(sys_time)
           .long SYMBOL_NAME(sys_mknod)
           .long SYMBOL_NAME(sys_chmod)            /* 15 */
           .long SYMBOL_NAME(sys_lchown16)
 long SYMBOL_NAME(sys_ni_syscall)/* old break syscall holder */
           .long SYMBOL_NAME(sys_stat)
           .long SYMBOL_NAME(sys_lseek)
           .long SYMBOL_NAME(sys_getpid)           /* 20 */
           .long SYMBOL_NAME(sys_mount)
           .long SYMBOL_NAME(sys_oldumount)
           .long SYMBOL_NAME(sys_setuid16)
           .long SYMBOL_NAME(sys_getuid16)
           .long SYMBOL_NAME(sys_stime)            /* 25 */
           .long SYMBOL_NAME(sys_ptrace)
           .long SYMBOL_NAME(sys_alarm)
           .long SYMBOL_NAME(sys_fstat)
           .long SYMBOL_NAME(sys_pause)
           .long SYMBOL_NAME(sys_utime)            /* 30 */
 .long SYMBOL_NAME(sys_ni_syscall)/* old stty syscall holder */
 .long SYMBOL_NAME(sys_ni_syscall)/* old gtty syscall holder */
           .long SYMBOL_NAME(sys_access)
           .long SYMBOL_NAME(sys_nice)
 .long SYMBOL_NAME(sys_ni_syscall) /* 35 */ 
                /* old ftime syscall holder */
           .long SYMBOL_NAME(sys_sync)
           .long SYMBOL_NAME(sys_kill)
           .long SYMBOL_NAME(sys_rename)
           .long SYMBOL_NAME(sys_mkdir)
           .long SYMBOL_NAME(sys_rmdir)            /* 40 */
           .long SYMBOL_NAME(sys_dup)
           .long SYMBOL_NAME(sys_pipe)
           .long SYMBOL_NAME(sys_times)
            /* old prof syscall holder */
           .long SYMBOL_NAME(sys_ni_syscall)
           .long SYMBOL_NAME(sys_brk)              /* 45 */
           .long SYMBOL_NAME(sys_setgid16)
           .long SYMBOL_NAME(sys_getgid16)
           .long SYMBOL_NAME(sys_signal)
           .long SYMBOL_NAME(sys_geteuid16)
           .long SYMBOL_NAME(sys_getegid16)        /* 50 */
           .long SYMBOL_NAME(sys_acct)
            /* recycled never used phys() */
           .long SYMBOL_NAME(sys_umount)
           /* old lock syscall holder */
           .long SYMBOL_NAME(sys_ni_syscall)                               
           .long SYMBOL_NAME(sys_ioctl)
           .long SYMBOL_NAME(sys_fcntl)            /* 55 */
             /* old mpx syscall holder */
           .long SYMBOL_NAME(sys_ni_syscall)                             
           .long SYMBOL_NAME(sys_setpgid)
           /* old ulimit syscall holder */
           .long SYMBOL_NAME(sys_ni_syscall)                               
           .long SYMBOL_NAME(sys_olduname)
           .long SYMBOL_NAME(sys_umask)            /* 60 */
           .long SYMBOL_NAME(sys_chroot)
           .long SYMBOL_NAME(sys_ustat)
           .long SYMBOL_NAME(sys_dup2)
           .long SYMBOL_NAME(sys_getppid)
           .long SYMBOL_NAME(sys_getpgrp)          /* 65 */
           .long SYMBOL_NAME(sys_setsid)
           .long SYMBOL_NAME(sys_sigaction)
           .long SYMBOL_NAME(sys_sgetmask)
           .long SYMBOL_NAME(sys_ssetmask)
           .long SYMBOL_NAME(sys_setreuid16)       /* 70 */
           .long SYMBOL_NAME(sys_setregid16)
           .long SYMBOL_NAME(sys_sigsuspend)
           .long SYMBOL_NAME(sys_sigpending)
           .long SYMBOL_NAME(sys_sethostname)
           .long SYMBOL_NAME(sys_setrlimit)        /* 75 */
           .long SYMBOL_NAME(sys_old_getrlimit)
           .long SYMBOL_NAME(sys_getrusage)
           .long SYMBOL_NAME(sys_gettimeofday)
           .long SYMBOL_NAME(sys_settimeofday)
           .long SYMBOL_NAME(sys_getgroups16)      /* 80 */
           .long SYMBOL_NAME(sys_setgroups16)
           .long SYMBOL_NAME(old_select)
           .long SYMBOL_NAME(sys_symlink)
           .long SYMBOL_NAME(sys_lstat)
           .long SYMBOL_NAME(sys_readlink)         /* 85 */
           .long SYMBOL_NAME(sys_uselib)
           .long SYMBOL_NAME(sys_swapon)
           .long SYMBOL_NAME(sys_reboot)
           .long SYMBOL_NAME(old_readdir)
           .long SYMBOL_NAME(old_mmap)             /* 90 */
           .long SYMBOL_NAME(sys_munmap)
           .long SYMBOL_NAME(sys_truncate)
           .long SYMBOL_NAME(sys_ftruncate)
           .long SYMBOL_NAME(sys_fchmod)
           .long SYMBOL_NAME(sys_fchown16)         /* 95 */
           .long SYMBOL_NAME(sys_getpriority)
           .long SYMBOL_NAME(sys_setpriority)
           /* old profil syscall holder */
           .long SYMBOL_NAME(sys_ni_syscall)                               
           .long SYMBOL_NAME(sys_statfs)
           .long SYMBOL_NAME(sys_fstatfs)          /* 100 */
           .long SYMBOL_NAME(sys_ioperm)
           .long SYMBOL_NAME(sys_socketcall)
           .long SYMBOL_NAME(sys_syslog)
           .long SYMBOL_NAME(sys_setitimer)
           .long SYMBOL_NAME(sys_getitimer)        /* 105 */
           .long SYMBOL_NAME(sys_newstat)
           .long SYMBOL_NAME(sys_newlstat)
           .long SYMBOL_NAME(sys_newfstat)
           .long SYMBOL_NAME(sys_uname)
           .long SYMBOL_NAME(sys_iopl)             /* 110 */
           .long SYMBOL_NAME(sys_vhangup)
           .long SYMBOL_NAME(sys_ni_syscall)/* old "idle" system call */
           .long SYMBOL_NAME(sys_vm86old)
           .long SYMBOL_NAME(sys_wait4)
           .long SYMBOL_NAME(sys_swapoff)          /* 115 */
           .long SYMBOL_NAME(sys_sysinfo)
           .long SYMBOL_NAME(sys_ipc)
           .long SYMBOL_NAME(sys_fsync)
           .long SYMBOL_NAME(sys_sigreturn)
           .long SYMBOL_NAME(sys_clone)            /* 120 */
           .long SYMBOL_NAME(sys_setdomainname)
           .long SYMBOL_NAME(sys_newuname)
           .long SYMBOL_NAME(sys_modify_ldt)
           .long SYMBOL_NAME(sys_adjtimex)
           .long SYMBOL_NAME(sys_mprotect)         /* 125 */
           .long SYMBOL_NAME(sys_sigprocmask)
           .long SYMBOL_NAME(sys_create_module)
           .long SYMBOL_NAME(sys_init_module)
           .long SYMBOL_NAME(sys_delete_module)
           .long SYMBOL_NAME(sys_get_kernel_syms)  /* 130 */
           .long SYMBOL_NAME(sys_quotactl)
           .long SYMBOL_NAME(sys_getpgid)
           .long SYMBOL_NAME(sys_fchdir)
           .long SYMBOL_NAME(sys_bdflush)
           .long SYMBOL_NAME(sys_sysfs)            /* 135 */
           .long SYMBOL_NAME(sys_personality)
           .long SYMBOL_NAME(sys_ni_syscall)       /* for afs_syscall */
           .long SYMBOL_NAME(sys_setfsuid16)
           .long SYMBOL_NAME(sys_setfsgid16)
           .long SYMBOL_NAME(sys_llseek)           /* 140 */
           .long SYMBOL_NAME(sys_getdents)
           .long SYMBOL_NAME(sys_select)
           .long SYMBOL_NAME(sys_flock)
           .long SYMBOL_NAME(sys_msync)
           .long SYMBOL_NAME(sys_readv)            /* 145 */
           .long SYMBOL_NAME(sys_writev)
           .long SYMBOL_NAME(sys_getsid)
           .long SYMBOL_NAME(sys_fdatasync)
           .long SYMBOL_NAME(sys_sysctl)
           .long SYMBOL_NAME(sys_mlock)            /* 150 */
           .long SYMBOL_NAME(sys_munlock)
           .long SYMBOL_NAME(sys_mlockall)
           .long SYMBOL_NAME(sys_munlockall)
           .long SYMBOL_NAME(sys_sched_setparam)
           .long SYMBOL_NAME(sys_sched_getparam)   /* 155 */
           .long SYMBOL_NAME(sys_sched_setscheduler)
           .long SYMBOL_NAME(sys_sched_getscheduler)
           .long SYMBOL_NAME(sys_sched_yield)
           .long SYMBOL_NAME(sys_sched_get_priority_max)
           .long SYMBOL_NAME(sys_sched_get_priority_min)  /* 160 */
           .long SYMBOL_NAME(sys_sched_rr_get_interval)
           .long SYMBOL_NAME(sys_nanosleep)
           .long SYMBOL_NAME(sys_mremap)
           .long SYMBOL_NAME(sys_setresuid16)
           .long SYMBOL_NAME(sys_getresuid16)      /* 165 */
           .long SYMBOL_NAME(sys_vm86)
           .long SYMBOL_NAME(sys_query_module)
           .long SYMBOL_NAME(sys_poll)
           .long SYMBOL_NAME(sys_nfsservctl)
           .long SYMBOL_NAME(sys_setresgid16)      /* 170 */
           .long SYMBOL_NAME(sys_getresgid16)
           .long SYMBOL_NAME(sys_prctl)
           .long SYMBOL_NAME(sys_rt_sigreturn)
           .long SYMBOL_NAME(sys_rt_sigaction)
           .long SYMBOL_NAME(sys_rt_sigprocmask)   /* 175 */
           .long SYMBOL_NAME(sys_rt_sigpending)
           .long SYMBOL_NAME(sys_rt_sigtimedwait)
           .long SYMBOL_NAME(sys_rt_sigqueueinfo)
           .long SYMBOL_NAME(sys_rt_sigsuspend)
           .long SYMBOL_NAME(sys_pread)            /* 180 */
           .long SYMBOL_NAME(sys_pwrite)
           .long SYMBOL_NAME(sys_chown16)
           .long SYMBOL_NAME(sys_getcwd)
           .long SYMBOL_NAME(sys_capget)
           .long SYMBOL_NAME(sys_capset)           /* 185 */
           .long SYMBOL_NAME(sys_sigaltstack)
           .long SYMBOL_NAME(sys_sendfile)
           .long SYMBOL_NAME(sys_ni_syscall)               /* streams1 */
           .long SYMBOL_NAME(sys_ni_syscall)               /* streams2 */
           .long SYMBOL_NAME(sys_vfork)            /* 190 */
           .long SYMBOL_NAME(sys_getrlimit)
           .long SYMBOL_NAME(sys_mmap2)
           .long SYMBOL_NAME(sys_truncate64)
           .long SYMBOL_NAME(sys_ftruncate64)
           .long SYMBOL_NAME(sys_stat64)           /* 195 */
           .long SYMBOL_NAME(sys_lstat64)
           .long SYMBOL_NAME(sys_fstat64)
           .long SYMBOL_NAME(sys_lchown)
           .long SYMBOL_NAME(sys_getuid)
           .long SYMBOL_NAME(sys_getgid)           /* 200 */
           .long SYMBOL_NAME(sys_geteuid)
           .long SYMBOL_NAME(sys_getegid)
           .long SYMBOL_NAME(sys_setreuid)
           .long SYMBOL_NAME(sys_setregid)
           .long SYMBOL_NAME(sys_getgroups)        /* 205 */
           .long SYMBOL_NAME(sys_setgroups)
           .long SYMBOL_NAME(sys_fchown)
           .long SYMBOL_NAME(sys_setresuid)
           .long SYMBOL_NAME(sys_getresuid)
           .long SYMBOL_NAME(sys_setresgid)        /* 210 */
           .long SYMBOL_NAME(sys_getresgid)
           .long SYMBOL_NAME(sys_chown)
           .long SYMBOL_NAME(sys_setuid)
           .long SYMBOL_NAME(sys_setgid)
           .long SYMBOL_NAME(sys_setfsuid)         /* 215 */
           .long SYMBOL_NAME(sys_setfsgid)
           .long SYMBOL_NAME(sys_pivot_root)
           .long SYMBOL_NAME(sys_mincore)
           .long SYMBOL_NAME(sys_madvise)
           .long SYMBOL_NAME(sys_getdents64)       /* 220 */
           .long SYMBOL_NAME(sys_fcntl64)
           .long SYMBOL_NAME(sys_ni_syscall)       /* reserved for TUX */
           .long SYMBOL_NAME(sys_ni_syscall)       /* Reserved for Security */
           .long SYMBOL_NAME(sys_gettid)
           .long SYMBOL_NAME(sys_readahead)        /* 225 */
При возникновении IRQ Event вызывается IRQ Handler , и любая задача прерывается , после чего управление возвращается точно в точку прерывания :
 
                 Running Task
                |-----------|          (3)
   NORMAL       |   |       | [break execution] IRQ Handler
   EXECUTION (1)|   |       |     ------------->|---------|
                |  \|/      |     |             |  does   |
    IRQ (2)---->| ..        |----->             |  some   |
                |   |       |<-----             |  work   |
   BACK TO      |   |       |     |             |  ..(4). |
   NORMAL    (6)|  \|/      |     <-------------|_________|
   EXECUTION    |___________|  [return to code]
                                       (5)
                  USER MODE                     KERNEL MODE
 
 
   Специальные прерывания таймера возникают при :
  1. Системных предупреждениях
  2. Вызываются системным шедулятором
Ключевым понятием для современных ОС является понятие "Task" или приложение . Приложения выполняются в режиме многозадачности . Переключение между приложениями происходит автоматически через промежуток времени , называемый "timeslice" , которое обычно равно 10 ms . При этом приложение использует переменную , называемую "state" , которая может быть в 2-х состояниях - READY или BLOCKED. Виртуальная память и сегментация позволяют решить проблемы с размещением приложениий независимо от имеющихся ресурсов . Сегмент - это как-бы образ приложения в памяти. Память разбивается на страницы размером по 4 кб , и процесс загружается в одну или несколько таких страниц . На следующей схеме показано , как процесс размещается в памяти :
                                     |      ..            |
                                     |____________________|
                               ----->|      Page 1        |
                               |     |____________________|
                               |     |      ..            |
    ____________________       |     |____________________|
   |                    |      |---->|      Page 2        |
   |      Segment X     |  ----|     |____________________|
   |                    |      |     |       ..           |
   |____________________|      |     |____________________|
                               |     |       ..           |
                               |     |____________________|
                               |---->|      Page 3        |
                                     |____________________|
                                     |       ..           |
Сегмент разбивается на 3 куска , каждый из которых загружается в отдельную страницу . Пэйджинг имеет иерархическую структуру :
 
                             |         |           |         |
                             |         |   Offset2 |  Value  |
                             |         |        /|\|         |
                     Offset1 |         |-----    | |         |
                         /|\ |         |    |    | |         |
                          |  |         |    |   \|/|         |
                          |  |         |    ------>|         |
                         \|/ |         |           |         |
    Base Paging Address ---->|         |           |         |
                             | ....... |           | ....... |
                             |         |           |         |
 
 
   Linux Startup
 
   Ядро стартует с метки  ''startup_32:''
   |startup_32:
      |start_kernel
         |lock_kernel
         |trap_init
         |init_IRQ
         |sched_init
         |softirq_init
         |time_init
         |console_init
         |#ifdef CONFIG_MODULES
            |init_modules
         |#endif
         |kmem_cache_init
         |sti
         |calibrate_delay
         |mem_init
         |kmem_cache_sizes_init
         |pgtable_cache_init
         |fork_init
         |proc_caches_init
         |vfs_caches_init
         |buffer_init
         |page_cache_init
         |signals_init
         |#ifdef CONFIG_PROC_FS
           |proc_root_init
         |#endif
         |#if defined(CONFIG_SYSVIPC)
            |ipc_init
         |#endif
         |check_bugs
         |smp_init
         |rest_init
            |kernel_thread
            |unlock_kernel
            |cpu_idle
   ·  startup_32 [arch/i386/kernel/head.S]
   ·  start_kernel [init/main.c]
   ·  lock_kernel [include/asm/smplock.h]
   ·  trap_init [arch/i386/kernel/traps.c]
   ·  init_IRQ [arch/i386/kernel/i8259.c]
   ·  sched_init [kernel/sched.c]
   ·  softirq_init [kernel/softirq.c]
   ·  time_init [arch/i386/kernel/time.c]
   ·  console_init [drivers/char/tty_io.c]
   ·  init_modules [kernel/module.c]
   ·  kmem_cache_init [mm/slab.c]
   ·  sti [include/asm/system.h]
   ·  calibrate_delay [init/main.c]
   ·  mem_init [arch/i386/mm/init.c]
   ·  kmem_cache_sizes_init [mm/slab.c]
   ·  pgtable_cache_init [arch/i386/mm/init.c]
   ·  fork_init [kernel/fork.c]
   ·  proc_caches_init
   ·  vfs_caches_init [fs/dcache.c]
   ·  buffer_init [fs/buffer.c]
   ·  page_cache_init [mm/filemap.c]
   ·  signals_init [kernel/signal.c]
   ·  proc_root_init [fs/proc/root.c]
   ·  ipc_init [ipc/util.c]
   ·  check_bugs [include/asm/bugs.h]
   ·  smp_init [init/main.c]
   ·  rest_init
   ·  kernel_thread [arch/i386/kernel/process.c]
   ·  unlock_kernel [include/asm/smplock.h]
   ·  cpu_idle [arch/i386/kernel/process.c]
   Фактически функция start_kernel никогда не кончается
   Функция ''init'' :
   |init
      |lock_kernel
      |do_basic_setup
         |mtrr_init
         |sysctl_init
         |pci_init
         |sock_init
         |start_context_thread
         |do_init_calls
            |(*call())-> kswapd_init
      |prepare_namespace
      |free_initmem
      |unlock_kernel
      |execve
 
 
  В линуксе 4 типа сегментов :
   1. Kernel Code [0x10]
   2. Kernel Data / Stack [0x18]
   3. User Code [0x23]
   4. User Data / Stack [0x2b]
 Сегментные регистры :
   ·  CS - Code Segment
   ·  DS - Data Segment
   ·  SS - Stack Segment
   ·  ES - Alternative Segment
Под линуксом возможны 3 уровня пэйджинга , в зависимости от архитектуры . На интеловской платформе 2 уровня пэйджинга . В линуксе память процесса , несмотря на адресную "разбросанность" страниц , его составляющих , в дефрагментации не нуждается . Существует проблема со стеком ядра . Каждый процесс внутри ядра использует общий для всех стек ядра. И размер этого стека в каждой задаче должен быть ограничен . Функции ядра не могут быть рекурсивны и вызываются фиксированное число раз . При вызове IRQ происходит переключение задач . Некоторые процессы , такие например , которые отвечают за TCP/IP , могут в этот момент выстраиваться шедулятором в очередь . Этот механизм в ядре 2.4 называется 'ksoftirqd_CPUn' , где n - это константа kernel_thread. Схема механизма Softirq :
   |cpu_raise_softirq
      |__cpu_raise_softirq
      |wakeup_softirqd
         |wake_up_process
 
   ·  cpu_raise_softirq [kernel/softirq.c]
   ·  __cpu_raise_softirq [include/linux/interrupt.h]
   ·  wakeup_softirq [kernel/softirq.c]
   ·  wake_up_process [kernel/sched.c]
При вызове треда ядра под названием 'ksoftirqd_CPU0'' будет построена и выполнена очередность :
   for (;;) {
      if (!softirq_pending(cpu))
         schedule();
         __set_current_state(TASK_RUNNING);
      while (softirq_pending(cpu)) {
         do_softirq();
         if (current->need_resched)
            schedule
      }
      __set_current_state(TASK_INTERRUPTIBLE)
   }
   ·  ksoftirqd [kernel/softirq.c]
Kernel Threads
В монолитном линукс-ядре нужно выделить несколько ''kernel threads''. Они используюи KERNEL-память и у них 0-я привилегия . Они создаются функцией ''kernel_thread [arch/i386/kernel/process]''
   int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
   {
           long retval, d0;
           __asm__ __volatile__(
 "movl %%esp,%%esi\n\t"
 "int $0x80\n\t"         /* Linux/i386 system call */
 "cmpl %%esp,%%esi\n\t"  /* child or parent? */
 "je 1f\n\t"             /* parent - jump */
 /* Load the argument into eax, and push it.  That way, it does
 * not matter whether the called function is compiled with
 * -mregparm or not.  */
 "movl %4,%%eax\n\t"
 "pushl %%eax\n\t"
 "call *%5\n\t"          /* call fn */
 "movl %3,%0\n\t"        /* exit */
 "int $0x80\n"
 "1:\t"
 :"=&a" (retval), "=&S" (d0)
 :"0" (__NR_clone), "i" (__NR_exit),
 "r" (arg), "r" (fn),
 "b" (flags | CLONE_VM)
                   : "memory");
           return retval;
   }
Т.о. сгенерированная задача будет ожидать критичное по скорости выполнения событие типа свопа или usb-шное событие . Ниже приведен список наиболее важных kernel threads (''ps x''):
   PID      COMMAND
    1        init
    2        keventd
    3        kswapd
    4        kreclaimd
    5        bdflush
    6        kupdated
    7        kacpid
   67        khubd
Они , в свою очередь , могут вызывать другие пользовательские задачи (из /etc/inittab) типа консольных демонов , tty-демонов,сетевых демонов (скрипт ''rc'' ).

Пример Kernel Threads: kswapd [mm/vmscan.c].
 Процедуры инициализации :
   |do_initcalls
      |kswapd_init
         |kernel_thread
            |syscall fork (in assembler)
   do_initcalls [init/main.c]
   kswapd_init [mm/vmscan.c]
   kernel_thread [arch/i386/kernel/process.c]
Kernel Modules
Linux Kernel modules - это код (например: fs, net, hw - драйвера) , выполняющийся в пространстве ядра , который подключается после загрузки , по команде пользователя . Такие вещи , как шедулятор , управление прерываниями или сетевое ядро , не может быть загружено из модулей , поэтому оно жестко вкомпилировано в ядро . Модули , инсталлированные в вашей системе , можно найти в "/lib/modules/KERNEL_VERSION/" Для загрузки модуля , наберите команду :
 	  insmod MODULE_NAME parameters
       пример: insmod ne io=0x300 irq=9
Для загрузки можно использовать другую команду - modprobe , которая сама отыщет нужные параметры ,
 например в  /etc/conf.modules.
 Для выгрузки модуля :
 	   rmmod MODULE_NAME
  Модуль всегда включает в себя
    1. Функцию "init_module"
    2. Функцию "cleanup_module"
 Если таких функций в модуле нет , 
 тогда там должны быть по крайней мере 2 макроса :
 	  1. module_init(FUNCTION_NAME)
 	  2. module_exit(FUNCTION_NAME)
 Модуль видит переменную ядра с помощь макроса  EXPORT_SYMBOL.
  При разработке модулей нужно иметь в виду следующее : на стороне ядра мы имеем :
 	  void (*foo_function_pointer)(void *);
 	  if (foo_function_pointer)
     	(foo_function_pointer)(parameter);
   // на стороне модуля
   extern void (*foo_function_pointer)(void *);
   void my_function(void *parameter) {    //My code  }
   int init_module()
   {
     foo_function_pointer = &my_function;
   }
   int cleanup_module()
   {
     foo_function_pointer = NULL;
   }
Каталог /proc .
/proc - каталог , который позволяет напрямую "общаться" с ядром . Линукс использует его для поддержки т.н. direct kernel communications: можно например посмотреть основные структуры процессов , изменить максимальное число трэдов , отдебажить состояние шины данных типа ISA или PCI , узнать о том , какие карты проинсталированы и какие им назначены порты и прерывания .
   |-- bus
   |   |-- pci
   |   |   |-- 00
   |   |   |   |-- 00.0
   |   |   |   |-- 01.0
   |   |   |   |-- 07.0
   |   |   |   |-- 07.1
   |   |   |   |-- 07.2
   |   |   |   |-- 07.3
   |   |   |   |-- 07.4
   |   |   |   |-- 07.5
   |   |   |   |-- 09.0
   |   |   |   |-- 0a.0
   |   |   |   `-- 0f.0
   |   |   |-- 01
   |   |   |   `-- 00.0
   |   |   `-- devices
   |   `-- usb
   |-- cmdline
   |-- cpuinfo
   |-- devices
   |-- dma
   |-- dri
   |   `-- 0
   |       |-- bufs
   |       |-- clients
   |       |-- mem
   |       |-- name
   |       |-- queues
   |       |-- vm
   |       `-- vma
   |-- driver
   |-- execdomains
   |-- filesystems
   |-- fs
   |-- ide
   |   |-- drivers
   |   |-- hda -> ide0/hda
   |   |-- hdc -> ide1/hdc
   |   |-- ide0
   |   |   |-- channel
   |   |   |-- config
   |   |   |-- hda
   |   |   |   |-- cache
   |   |   |   |-- capacity
   |   |   |   |-- driver
   |   |   |   |-- geometry
   |   |   |   |-- identify
   |   |   |   |-- media
   |   |   |   |-- model
   |   |   |   |-- settings
   |   |   |   |-- smart_thresholds
   |   |   |   `-- smart_values
   |   |   |-- mate
   |   |   `-- model
   |   |-- ide1
   |   |   |-- channel
   |   |   |-- config
   |   |   |-- hdc
   |   |   |   |-- capacity
   |   |   |   |-- driver
   |   |   |   |-- identify
   |   |   |   |-- media
   |   |   |   |-- model
   |   |   |   `-- settings
   |   |   |-- mate
   |   |   `-- model
   |   `-- via
   |-- interrupts
   |-- iomem
   |-- ioports
   |-- irq
   |   |-- 0
   |   |-- 1
   |   |-- 10
   |   |-- 11
   |   |-- 12
   |   |-- 13
   |   |-- 14
   |   |-- 15
   |   |-- 2
   |   |-- 3
   |   |-- 4
   |   |-- 5
   |   |-- 6
   |   |-- 7
   |   |-- 8
   |   |-- 9
   |   `-- prof_cpu_mask
   |-- kcore
   |-- kmsg
   |-- ksyms
   |-- loadavg
   |-- locks
   |-- meminfo
   |-- misc
   |-- modules
   |-- mounts
   |-- mtrr
   |-- net
   |   |-- arp
   |   |-- dev
   |   |-- dev_mcast
   |   |-- ip_fwchains
   |   |-- ip_fwnames
   |   |-- ip_masquerade
   |   |-- netlink
   |   |-- netstat
   |   |-- packet
   |   |-- psched
   |   |-- raw
   |   |-- route
   |   |-- rt_acct
   |   |-- rt_cache
   |   |-- rt_cache_stat
   |   |-- snmp
   |   |-- sockstat
   |   |-- softnet_stat
   |   |-- tcp
   |   |-- udp
   |   |-- unix
   |   `-- wireless
   |-- partitions
   |-- pci
   |-- scsi
   |   |-- ide-scsi
   |   |   `-- 0
   |   `-- scsi
   |-- self -> 2069
   |-- slabinfo
   |-- stat
   |-- swaps
   |-- sys
   |   |-- abi
   |   |   |-- defhandler_coff
   |   |   |-- defhandler_elf
   |   |   |-- defhandler_lcall7
   |   |   |-- defhandler_libcso
   |   |   |-- fake_utsname
   |   |   `-- trace
   |   |-- debug
   |   |-- dev
   |   |   |-- cdrom
   |   |   |   |-- autoclose
   |   |   |   |-- autoeject
   |   |   |   |-- check_media
   |   |   |   |-- debug
   |   |   |   |-- info
   |   |   |   `-- lock
   |   |   `-- parport
   |   |       |-- default
   |   |       |   |-- spintime
   |   |       |   `-- timeslice
   |   |       `-- parport0
   |   |           |-- autoprobe
   |   |           |-- autoprobe0
   |   |           |-- autoprobe1
   |   |           |-- autoprobe2
   |   |           |-- autoprobe3
   |   |           |-- base-addr
   |   |           |-- devices
   |   |           |   |-- active
   |   |           |   `-- lp
   |   |           |       `-- timeslice
   |   |           |-- dma
   |   |           |-- irq
   |   |           |-- modes
   |   |           `-- spintime
   |   |-- fs
   |   |   |-- binfmt_misc
   |   |   |-- dentry-state
   |   |   |-- dir-notify-enable
   |   |   |-- dquot-nr
   |   |   |-- file-max
   |   |   |-- file-nr
   |   |   |-- inode-nr
   |   |   |-- inode-state
   |   |   |-- jbd-debug
   |   |   |-- lease-break-time
   |   |   |-- leases-enable
   |   |   |-- overflowgid
   |   |   `-- overflowuid
   |   |-- kernel
   |   |   |-- acct
   |   |   |-- cad_pid
   |   |   |-- cap-bound
   |   |   |-- core_uses_pid
   |   |   |-- ctrl-alt-del
   |   |   |-- domainname
   |   |   |-- hostname
   |   |   |-- modprobe
   |   |   |-- msgmax
   |   |   |-- msgmnb
   |   |   |-- msgmni
   |   |   |-- osrelease
   |   |   |-- ostype
   |   |   |-- overflowgid
   |   |   |-- overflowuid
   |   |   |-- panic
   |   |   |-- printk
   |   |   |-- random
   |   |   |   |-- boot_id
   |   |   |   |-- entropy_avail
   |   |   |   |-- poolsize
   |   |   |   |-- read_wakeup_threshold
   |   |   |   |-- uuid
   |   |   |   `-- write_wakeup_threshold
   |   |   |-- rtsig-max
   |   |   |-- rtsig-nr
   |   |   |-- sem
   |   |   |-- shmall
   |   |   |-- shmmax
   |   |   |-- shmmni
   |   |   |-- sysrq
   |   |   |-- tainted
   |   |   |-- threads-max
   |   |   `-- version
   |   |-- net
   |   |   |-- 802
   |   |   |-- core
   |   |   |   |-- hot_list_length
   |   |   |   |-- lo_cong
   |   |   |   |-- message_burst
   |   |   |   |-- message_cost
   |   |   |   |-- mod_cong
   |   |   |   |-- netdev_max_backlog
   |   |   |   |-- no_cong
   |   |   |   |-- no_cong_thresh
   |   |   |   |-- optmem_max
   |   |   |   |-- rmem_default
   |   |   |   |-- rmem_max
   |   |   |   |-- wmem_default
   |   |   |   `-- wmem_max
   |   |   |-- ethernet
   |   |   |-- ipv4
   |   |   |   |-- conf
   |   |   |   |   |-- all
   |   |   |   |   |   |-- accept_redirects
   |   |   |   |   |   |-- accept_source_route
   |   |   |   |   |   |-- arp_filter
   |   |   |   |   |   |-- bootp_relay
   |   |   |   |   |   |-- forwarding
   |   |   |   |   |   |-- log_martians
   |   |   |   |   |   |-- mc_forwarding
   |   |   |   |   |   |-- proxy_arp
   |   |   |   |   |   |-- rp_filter
   |   |   |   |   |   |-- secure_redirects
   |   |   |   |   |   |-- send_redirects
   |   |   |   |   |   |-- shared_media
   |   |   |   |   |   `-- tag
   |   |   |   |   |-- default
   |   |   |   |   |   |-- accept_redirects
   |   |   |   |   |   |-- accept_source_route
   |   |   |   |   |   |-- arp_filter
   |   |   |   |   |   |-- bootp_relay
   |   |   |   |   |   |-- forwarding
   |   |   |   |   |   |-- log_martians
   |   |   |   |   |   |-- mc_forwarding
   |   |   |   |   |   |-- proxy_arp
   |   |   |   |   |   |-- rp_filter
   |   |   |   |   |   |-- secure_redirects
   |   |   |   |   |   |-- send_redirects
   |   |   |   |   |   |-- shared_media
   |   |   |   |   |   `-- tag
   |   |   |   |   |-- eth0
   |   |   |   |   |   |-- accept_redirects
   |   |   |   |   |   |-- accept_source_route
   |   |   |   |   |   |-- arp_filter
   |   |   |   |   |   |-- bootp_relay
   |   |   |   |   |   |-- forwarding
   |   |   |   |   |   |-- log_martians
   |   |   |   |   |   |-- mc_forwarding
   |   |   |   |   |   |-- proxy_arp
   |   |   |   |   |   |-- rp_filter
   |   |   |   |   |   |-- secure_redirects
   |   |   |   |   |   |-- send_redirects
   |   |   |   |   |   |-- shared_media
   |   |   |   |   |   `-- tag
   |   |   |   |   |-- eth1
   |   |   |   |   |   |-- accept_redirects
   |   |   |   |   |   |-- accept_source_route
   |   |   |   |   |   |-- arp_filter
   |   |   |   |   |   |-- bootp_relay
   |   |   |   |   |   |-- forwarding
   |   |   |   |   |   |-- log_martians
   |   |   |   |   |   |-- mc_forwarding
   |   |   |   |   |   |-- proxy_arp
   |   |   |   |   |   |-- rp_filter
   |   |   |   |   |   |-- secure_redirects
   |   |   |   |   |   |-- send_redirects
   |   |   |   |   |   |-- shared_media
   |   |   |   |   |   `-- tag
   |   |   |   |   `-- lo
   |   |   |   |       |-- accept_redirects
   |   |   |   |       |-- accept_source_route
   |   |   |   |       |-- arp_filter
   |   |   |   |       |-- bootp_relay
   |   |   |   |       |-- forwarding
   |   |   |   |       |-- log_martians
   |   |   |   |       |-- mc_forwarding
   |   |   |   |       |-- proxy_arp
   |   |   |   |       |-- rp_filter
   |   |   |   |       |-- secure_redirects
   |   |   |   |       |-- send_redirects
   |   |   |   |       |-- shared_media
   |   |   |   |       `-- tag
   |   |   |   |-- icmp_echo_ignore_all
   |   |   |   |-- icmp_echo_ignore_broadcasts
   |   |   |   |-- icmp_ignore_bogus_error_responses
   |   |   |   |-- icmp_ratelimit
   |   |   |   |-- icmp_ratemask
   |   |   |   |-- inet_peer_gc_maxtime
   |   |   |   |-- inet_peer_gc_mintime
   |   |   |   |-- inet_peer_maxttl
   |   |   |   |-- inet_peer_minttl
   |   |   |   |-- inet_peer_threshold
   |   |   |   |-- ip_autoconfig
   |   |   |   |-- ip_conntrack_max
   |   |   |   |-- ip_default_ttl
   |   |   |   |-- ip_dynaddr
   |   |   |   |-- ip_forward
   |   |   |   |-- ip_local_port_range
   |   |   |   |-- ip_no_pmtu_disc
   |   |   |   |-- ip_nonlocal_bind
   |   |   |   |-- ipfrag_high_thresh
   |   |   |   |-- ipfrag_low_thresh
   |   |   |   |-- ipfrag_time
   |   |   |   |-- neigh
   |   |   |   |   |-- default
   |   |   |   |   |   |-- anycast_delay
   |   |   |   |   |   |-- app_solicit
   |   |   |   |   |   |-- base_reachable_time
   |   |   |   |   |   |-- delay_first_probe_time
   |   |   |   |   |   |-- gc_interval
   |   |   |   |   |   |-- gc_stale_time
   |   |   |   |   |   |-- gc_thresh1
   |   |   |   |   |   |-- gc_thresh2
   |   |   |   |   |   |-- gc_thresh3
   |   |   |   |   |   |-- locktime
   |   |   |   |   |   |-- mcast_solicit
   |   |   |   |   |   |-- proxy_delay
   |   |   |   |   |   |-- proxy_qlen
   |   |   |   |   |   |-- retrans_time
   |   |   |   |   |   |-- ucast_solicit
   |   |   |   |   |   `-- unres_qlen
   |   |   |   |   |-- eth0
   |   |   |   |   |   |-- anycast_delay
   |   |   |   |   |   |-- app_solicit
   |   |   |   |   |   |-- base_reachable_time
   |   |   |   |   |   |-- delay_first_probe_time
   |   |   |   |   |   |-- gc_stale_time
   |   |   |   |   |   |-- locktime
   |   |   |   |   |   |-- mcast_solicit
   |   |   |   |   |   |-- proxy_delay
   |   |   |   |   |   |-- proxy_qlen
   |   |   |   |   |   |-- retrans_time
   |   |   |   |   |   |-- ucast_solicit
   |   |   |   |   |   `-- unres_qlen
   |   |   |   |   |-- eth1
   |   |   |   |   |   |-- anycast_delay
   |   |   |   |   |   |-- app_solicit
   |   |   |   |   |   |-- base_reachable_time
   |   |   |   |   |   |-- delay_first_probe_time
   |   |   |   |   |   |-- gc_stale_time
   |   |   |   |   |   |-- locktime
   |   |   |   |   |   |-- mcast_solicit
   |   |   |   |   |   |-- proxy_delay
   |   |   |   |   |   |-- proxy_qlen
   |   |   |   |   |   |-- retrans_time
   |   |   |   |   |   |-- ucast_solicit
   |   |   |   |   |   `-- unres_qlen
   |   |   |   |   `-- lo
   |   |   |   |       |-- anycast_delay
   |   |   |   |       |-- app_solicit
   |   |   |   |       |-- base_reachable_time
   |   |   |   |       |-- delay_first_probe_time
   |   |   |   |       |-- gc_stale_time
   |   |   |   |       |-- locktime
   |   |   |   |       |-- mcast_solicit
   |   |   |   |       |-- proxy_delay
   |   |   |   |       |-- proxy_qlen
   |   |   |   |       |-- retrans_time
   |   |   |   |       |-- ucast_solicit
   |   |   |   |       `-- unres_qlen
   |   |   |   |-- route
   |   |   |   |   |-- error_burst
   |   |   |   |   |-- error_cost
   |   |   |   |   |-- flush
   |   |   |   |   |-- gc_elasticity
   |   |   |   |   |-- gc_interval
   |   |   |   |   |-- gc_min_interval
   |   |   |   |   |-- gc_thresh
   |   |   |   |   |-- gc_timeout
   |   |   |   |   |-- max_delay
   |   |   |   |   |-- max_size
   |   |   |   |   |-- min_adv_mss
   |   |   |   |   |-- min_delay
   |   |   |   |   |-- min_pmtu
   |   |   |   |   |-- mtu_expires
   |   |   |   |   |-- redirect_load
   |   |   |   |   |-- redirect_number
   |   |   |   |   `-- redirect_silence
   |   |   |   |-- tcp_abort_on_overflow
   |   |   |   |-- tcp_adv_win_scale
   |   |   |   |-- tcp_app_win
   |   |   |   |-- tcp_dsack
   |   |   |   |-- tcp_ecn
   |   |   |   |-- tcp_fack
   |   |   |   |-- tcp_fin_timeout
   |   |   |   |-- tcp_keepalive_intvl
   |   |   |   |-- tcp_keepalive_probes
   |   |   |   |-- tcp_keepalive_time
   |   |   |   |-- tcp_max_orphans
   |   |   |   |-- tcp_max_syn_backlog
   |   |   |   |-- tcp_max_tw_buckets
   |   |   |   |-- tcp_mem
   |   |   |   |-- tcp_orphan_retries
   |   |   |   |-- tcp_reordering
   |   |   |   |-- tcp_retrans_collapse
   |   |   |   |-- tcp_retries1
   |   |   |   |-- tcp_retries2
   |   |   |   |-- tcp_rfc1337
   |   |   |   |-- tcp_rmem
   |   |   |   |-- tcp_sack
   |   |   |   |-- tcp_stdurg
   |   |   |   |-- tcp_syn_retries
   |   |   |   |-- tcp_synack_retries
   |   |   |   |-- tcp_syncookies
   |   |   |   |-- tcp_timestamps
   |   |   |   |-- tcp_tw_recycle
   |   |   |   |-- tcp_window_scaling
   |   |   |   `-- tcp_wmem
   |   |   `-- unix
   |   |       `-- max_dgram_qlen
   |   |-- proc
   |   `-- vm
   |       |-- bdflush
   |       |-- kswapd
   |       |-- max-readahead
   |       |-- min-readahead
   |       |-- overcommit_memory
   |       |-- page-cluster
   |       `-- pagetable_cache
   |-- sysvipc
   |   |-- msg
   |   |-- sem
   |   `-- shm
   |-- tty
   |   |-- driver
   |   |   `-- serial
   |   |-- drivers
   |   |-- ldisc
   |   `-- ldiscs
   |-- uptime
   `-- version  
При этом можно не только получить информацию о переменных ядра , но и модифицировать некоторые из них - см. /proc/sys каталог :
   /proc/sys/
             acpi
             dev
             debug
             fs
             proc
             net
             vm
             kernel
Ниже перечислены некоторые значения каталога /proc/sys/kernel для модификации :
   overflowgid
   overflowuid
   random
   threads-max // Max number of threads, typically 16384
   sysrq // kernel hack: you can view istant register values and more
   sem
   msgmnb
   msgmni
   msgmax
   shmmni
   shmall
   shmmax
   rtsig-max
   rtsig-nr
   modprobe // modprobe file location
   printk
   ctrl-alt-del
   cap-bound
   panic
   domainname // domain name of your Linux box
   hostname // host name of your Linux box
   version // date info about kernel compilation
   osrelease // kernel version (i.e. 2.4.5)
   ostype // Linux!
/proc/sys/net Этот каталог позволяет изменить сетевые настройки ядра :
   core
   ipv4
   ipv6
   unix
   ethernet
   802
/proc/sys/net/core Ниже идет перечисление таких сетевых параметров , как "netdev_max_backlog" (300), длина пакета. Это может изменить производительность ваше системы по получению пакетов.
     300    *        100             =     30 000
   packets     HZ(Timeslice freq)         packets/s
   30 000   *       1000             =      30 M
   packets     average (Bytes/packet)   throughput Bytes/s
Если вы хотите увеличить производительность , вам нужно увеличить netdev_max_backlog,:
 	  echo 4000 > /proc/sys/net/core/netdev_max_backlog
   /proc/sys/net/ipv4
   "ip_forward" - разрешает или запрещает ip forwarding .
   /proc/sys/net/ipv4/conf/interface
 Позволяет поддерживать wireless networks
 "proxy_arp" - arp feature.
 "send_redirects" - посылка ICMP_REDIRECT
Multitasking Выполняемый процесс может быть в одном из следующих состояний : [include/linux.h]):
   1. TASK_RUNNING
   2. TASK_INTERRUPTIBLE - в ожидании прерывания
   3. TASK_UNINTERRUPTIBLE - в очереди
   4. TASK_ZOMBIE - процесс , потерявшиий родителя
   5. TASK_STOPPED - процесс под дебагом
 
   Graphical Interaction
          ______________     CPU Available     ______________
         |              |  ---------------->  |              |
         | TASK_RUNNING |                     | Real Running |
         |______________|  <----------------  |______________|
                              CPU Busy
               |   /|\
   Waiting for |    | Resource
    Resource   |    | Available
              \|/   |
       ______________________
      |                      |
      | TASK_INTERRUPTIBLE / |
      | TASK-UNINTERRUPTIBLE |
      |______________________|
Timeslice
Каждые 10 ms генерится IRQ0 comes, которое играет ведущую роль в многозадачности. Этот сигнал приходит от PIC 8259 :
       _____         ______        ______
      | CPU |<------| 8259 |------| 8253 |
      |_____| IRQ0  |______|      |___/|\|
                                       |_____ CLK 1.193.180 MHz
 
   // From include/asm/param.h
   #ifndef HZ
   #define HZ 100
   #endif
 
   // From include/asm/timex.h
   #define CLOCK_TICK_RATE 1193180 /* Underlying HZ */
 
   // From include/linux/timex.h
   #define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */
 
   // From arch/i386/kernel/i8259.c
   outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */
   outb_p(LATCH & 0xff , 0x40); /* LSB */
   outb(LATCH >> 8 , 0x40); /* MSB */
PIT,или Programmable Interval Timer , запрограммирован с LATCH = (1193180/HZ) = 11931.8 . LATCH = 11931.8 дает на выходе 8253 частоту 1193180 / 11931.8 = 100 Hz, поэтому период = 10ms. Это частота с которой шедулятор моет "перебирать" процессы , стоящие в очереди .

Linux Timer IRQ ICA
   Linux Timer IRQ
   IRQ 0 [Timer]
    |
   \|/
   |IRQ0x00_interrupt        //   wrapper IRQ handler
      |SAVE_ALL              ---
         |do_IRQ                |   wrapper routines
            |handle_IRQ_event  ---
               |handler() -> timer_interrupt
                  |do_timer_interrupt
                     |do_timer
                        |jiffies++;
                        |update_process_times
                        |if (--counter <= 0) { 
                           |counter = 0;       
                           |need_resched = 1;  
                        |}
            |do_softirq
            |while (need_resched) { // if necessary
               |schedule             //   reschedule
               |handle_softirq
            |}
      |RESTORE_ALL
   ·  IRQ0x00_interrupt, SAVE_ALL [include/asm/hw_irq.h]
   ·  do_IRQ, handle_IRQ_event [arch/i386/kernel/irq.c]
 timer_interrupt, do_timer_interrupt [arch/i386/kernel/time.c]
   ·  do_timer, update_process_times [kernel/timer.c]
   ·  do_softirq [kernel/soft_irq.c]
  RESTORE_ALL, while loop [arch/i386/kernel/entry.S]
У каждой задачи есть счетчик , и когда он уменьшается до нуля , автоматически происходит переключение на другую задачу .
     Scheduler
scheduler - это код , который выбирает , какая задача будет выполняться в данный момент .
   |schedule
      |do_softirq // manages post-IRQ work
      |for each task
         |calculate counter
      |prepare_to__switch // does anything
      |switch_mm // change Memory context (change CR3 value)
      |switch_to (assembler)
         |SAVE ESP
         |RESTORE future_ESP
         |SAVE EIP
         |push future_EIP *** push parameter as we did a call
            |jmp __switch_to (it does some TSS work)
            |__switch_to()
             ..
 |ret *** ret from call using future_EIP in place of call address
         new_task
Когда приходит прерывание от устройства, линукс выполняет переключение на задачу , которая начинает опрашивать это устройство . Для того , этот опрос не пожирал все ресурсы , начиная с версии 1.0 реализован механизм "bottom half" (BH). В более поздних версиях bh заменен на более динамичную очередь .
  Схема работы BH :
   1. Декларирование
   2. Маркировка
   3. Выполнение
 
 	Декларация:
   #define DECLARE_TASK_QUEUE(q) LIST_HEAD(q)
   #define LIST_HEAD(name) \
      struct list_head name = LIST_HEAD_INIT(name)
   struct list_head {
      struct list_head *next, *prev;
   };
   #define LIST_HEAD_INIT(name) { &(name), &(name) }
   ''DECLARE_TASK_QUEUE'' [include/linux/tqueue.h, include/linux/list.h]
"DECLARE_TASK_QUEUE(q)" - это макрос для декларирования структуры "q" , управляющей очередью заданий .
Маркировка
 Функция "mark_bh" [include/linux/interrupt.h]
   |mark_bh(NUMBER)
      |tasklet_hi_schedule(bh_task_vec + NUMBER)
         |insert into tasklet_hi_vec
            |__cpu_raise_softirq(HI_SOFTIRQ)
               |soft_active |= (1 << HI_SOFTIRQ)
                   ''mark_bh''[include/linux/interrupt.h]
Выполнение - функция "do_IRQ" [arch/i386/kernel/irq.c]
   |do_softirq
 |h->action(h)-> softirq_vec[TASKLET_SOFTIRQ]->action -> tasklet_action
         |tasklet_vec[0].list->func
  Список низко-уровневых подпрограмм :
   set_intr_gate
   set_trap_gate
   set_task_gate (not used).
 (*interrupt)[NR_IRQS](void) = { IRQ0x00_interrupt, IRQ0x01_interrupt,  ..}
Переключение задач Оно происходит в следующих случаях :
   ·  TimeSlice
      когда приложение начинает опрашивать какой-то ресурс , 
      оно засыпает
      когда процесс в ожидании пайпа от другого процесса , 
      нужно переключиться на этот другой процесс
 #define switch_to(prev,next,last) do {                            \
         asm volatile("pushl %%esi\n\t"                             \
                 "pushl %%edi\n\t"                                  \
                 "pushl %%ebp\n\t"                                  \
                 "movl %%esp,%0\n\t"        /* save ESP */          \
                 "movl %3,%%esp\n\t"        /* restore ESP */       \
                 "movl $1f,%1\n\t"          /* save EIP */          \
                 "pushl %4\n\t"             /* restore EIP */       \
                 "jmp __switch_to\n"                                \
                 "1:\t"                                             \
                 "popl %%ebp\n\t"                                   \
                 "popl %%edi\n\t"                                   \
                 "popl %%esi\n\t"                                   \
                 :"=m" (prev->thread.esp),"=m" (prev->thread.eip),  \
                         "=b" (last)                                \
                 :"m" (next->thread.esp),"m" (next->thread.eip),    \
                         "a" (prev), "d" (next),                    \
                         "b" (prev));                               \
   } while (0)
  Переключение происходит после метки 1 :
         U S E R   M O D E                 K E R N E L     M O D E
    |          |     |          |       |          |     |          |
    |          |     |          | Timer |          |     |          |
    |          |     |  Normal  |  IRQ  |          |     |          |
    |          |     |   Exec   |------>|Timer_Int.|     |          |
    |          |     |     |    |       | ..       |     |          |
    |          |     |    \|/   |       |schedule()|     | Task1 Ret|
    |          |     |          |       |_switch_to|<--  |  Address |
    |__________|     |__________|       |          |  |  |          |
                                        |          |  |S |          |
   Task1 Data/Stack   Task1 Code        |          |  |w |          |
                                        |          | T|i |          |
                                        |          | a|t |          |
    |          |     |          |       |          | s|c |          |
    |          |     |          | Timer |          | k|h |          |
    |          |     |  Normal  |  IRQ  |          |  |i |          |
    |          |     |   Exec   |------>|Timer_Int.|  |n |          |
    |          |     |     |    |       | ..       |  |g |          |
    |          |     |    \|/   |       |schedule()|  |  | Task2 Ret|
    |          |     |          |       |_switch_to|<--  |  Address |
    |__________|     |__________|       |__________|     |__________|
 
   Task2 Data/Stack   Task2 Code        Kernel Code  Kernel Data/Stack
. Fork
Fork - генерация нового процесса . Имеется родительский процесс , структуры данных которого используются для наследуемого процесса
                                  |         |
                                  | ..      |
            Task Parent           |         |
            |         |           |         |
            |  fork   |---------->|  CREATE |
            |         |          /|   NEW   |
            |_________|         / |   TASK  |
                               /  |         |
                ---           /   |         |
                ---          /    | ..      |
                            /     |         |
            Task Child     /
            |         |   /
            |  fork   |<-/
            |         |
            |_________|
Порождаемый процес отличается от родителя следующим :
1 PID
2. child-процесс возвращает 0, в то время как родительский вернет PID
3. память child-процесса помечена как ''READ + EXECUTE'', не "WRITE''
   Fork ICA
   |sys_fork
      |do_fork
         |alloc_task_struct
            |__get_free_pages
          |p->state = TASK_UNINTERRUPTIBLE
          |copy_flags
          |p->pid = get_pid
          |copy_files
          |copy_fs
          |copy_sighand
          |copy_mm // should manage CopyOnWrite (I part)
             |allocate_mm
             |mm_init
                |pgd_alloc -> get_pgd_fast
                   |get_pgd_slow
             |dup_mmap
                |copy_page_range
                   |ptep_set_wrprotect
                      |clear_bit // set page to read-only
             |copy_segments // For LDT
          |copy_thread
             |childregs->eax = 0
             |p->thread.esp = childregs // child fork returns 0
         |p->thread.eip = ret_from_fork // child starts from fork exit
          |retval = p->pid // parent fork returns child pid
          |SET_LINKS // insertion of task into the list pointers
          |nr_threads++ // Global variable
          |wake_up_process(p) // Now we can wake up just created child
          |return retval
 
                  fork ICA
   ·  sys_fork [arch/i386/kernel/process.c]
   ·  do_fork [kernel/fork.c]
   ·  alloc_task_struct [include/asm/processor.c]
   ·  __get_free_pages [mm/page_alloc.c]
   ·  get_pid [kernel/fork.c]
   ·  copy_files
   ·  copy_fs
   ·  copy_sighand
   ·  copy_mm
   ·  allocate_mm
   ·  mm_init
   ·  pgd_alloc -> get_pgd_fast [include/asm/pgalloc.h]
   ·  get_pgd_slow
   ·  dup_mmap [kernel/fork.c]
   ·  copy_page_range [mm/memory.c]
   ·  ptep_set_wrprotect [include/asm/pgtable.h]
   ·  clear_bit [include/asm/bitops.h]
   ·  copy_segments [arch/i386/kernel/process.c]
   ·  copy_thread
   ·  SET_LINKS [include/linux/sched.h]
   ·  wake_up_process [kernel/sched.c]
Copy on Write При попытке записать данные в память child-процесса возникает Page Fault, при этом будет выделена новая страница памяти
    | Page
    | Fault
    | Exception
    -----------> |do_page_fault
                    |handle_mm_fault
                       |handle_pte_fault
                          |do_wp_page
                          |alloc_page      // Allocate a new page
                          |break_cow
                          |copy_cow_page // Copy old page to new one
                          |establish_pte // reconfig Page Table pointers
                                   |set_pte
                 Page Fault ICA
   ·  do_page_fault [arch/i386/mm/fault.c]
   ·  handle_mm_fault [mm/memory.c]
   ·  handle_pte_fault
   ·  do_wp_page
   ·  alloc_page [include/linux/mm.h]
   ·  break_cow [mm/memory.c]
   ·  copy_cow_page
   ·  establish_pte
   ·  set_pte [include/asm/pgtable-3level.h]
Linux Memory Management Linux при управлении памятью использует комбинацию из сегментации + пэйджинга . Linux использует 4 сегмента :
   ·  2 сегмента (код и стек) для KERNEL SPACE
   ·  2 сегмента (код и стек) для USER SPACE 
      4 GB--->|                |    |
              |     Kernel     |    |  Kernel Space (Code + Data/Stack)
              |                |  __|
      3 GB--->|----------------|  __
              |                |    |
              |                |    |
      2 GB--->|                |    |
              |     Tasks      |    |  User Space (Code + Data/Stack)
              |                |    |
      1 GB--->|                |    |
              |                |    |
              |________________|  __|
    0x00000000
 
  Пэйджинг в 386-м линуксе 2-уровневый :
      ------------------------------------------------------------
  L    I    N    E    A    R         A    D    D    R    E    S    S
 ----------------------------------------------------------------
           \___/                 \___/                     \_____/
 
 PD offset              PF offset                 Frame offset
 [10 bits]              [10 bits]                 [12 bits]
         |                     |                          |
         |                     |     -----------          |
         |                     |     |  Value  |----------|---------
         |     |         |     |     |---------|   /|\    |        |
         |     |         |     |     |         |    |     |        |
         |     |         |     |     |         |    | Frame offset |
         |     |         |     |     |         |   \|/             |
         |     |         |     |     |---------|<------            |
         |     |         |     |     |         |      |            |
         |     |         |     |     |         |      | x 4096     |
         |     |         |  PF offset|_________|-------            |
         |     |         |       /|\ |         |                   |
         PD offset |_________|-----   |  |         |          _________|
         /|\ |         |    |   |  |         |          |
         |  |         |    |  \|/ |         |         \|/
 _____       |  |         |    ------>|_________|   PHYSICAL ADDRESS
 |     |     \|/ |         |    x 4096 |         |
 | CR3 |-------->|         |           |         |
 |_____|         | ....... |           | ....... |
                 |         |           |         |
 
                Page Directory          Page File
У различных процессов фактически сегментные адреса будут одинаковые , различаться будет лишь содержимое регистра CR3 . В User mode одновременно может быть запущено не более 768 процессов(768*4MB = 3GB). В Kernel Mode это справедливо для 256 процессов .
               ________________ _____
              |Other KernelData|___  |  |                |
              |----------------|   | |__|                |
              |     Kernel     |\  |____|   Real Other   |
     3 GB --->|----------------| \      |   Kernel Data  |
              |                |\ \     |                |
              |              __|_\_\____|__   Real       |
              |      Tasks     |  \ \   |     Tasks      |
              |              __|___\_\__|__   Space      |
              |                |    \ \ |                |
              |                |     \ \|----------------|
              |                |      \ |Real KernelSpace|
              |________________|       \|________________|
 
           Logical Addresses          Physical Addresses
Работа с памятью начинается при загрузке в kmem_cache_init (launched by start_kernel [init/main.c]
   |kmem_cache_init
      |kmem_cache_estimate
   kmem_cache_init [mm/slab.c]
   kmem_cache_estimate
 
   Далее -  mem_init [init/main.c])
   |mem_init
      |free_all_bootmem
         |free_all_bootmem_core
   mem_init [arch/i386/mm/init.c]
   free_all_bootmem [mm/bootmem.c]
   free_all_bootmem_core
 
   Пример выделения памяти в run-time :
   |copy_mm
      |allocate_mm = kmem_cache_alloc
         |__kmem_cache_alloc
            |kmem_cache_alloc_one
               |alloc_new_slab
                  |kmem_cache_grow
                     |kmem_getpages
                        |__get_free_pages
                           |alloc_pages
                              |alloc_pages_pgdat
                                 |__alloc_pages
                                    |rmqueue
                                    |reclaim_pages
Linux Networking Для каждой разновидности NIC имеется свой драйвер . Внутри них вызывается стандартный роутинг - "netif_rx [net/core/dev.c]". Что происходит , когда приходит TCP-пакет :
   |netif_rx
      |__skb_queue_tail
         |qlen++
         |* simple pointer insertion *
      |cpu_raise_softirq
         |softirq_active(cpu) |= (1 << NET_RX_SOFTIRQ) 
         // set bit NET_RX_SOFTIRQ in the BH vector
NET_RX_SOFTIRQ генерирует вызов ''net_rx_action [net/core/dev.c]''
   |net_rx_action
      |skb = __skb_dequeue (the exact opposite of __skb_queue_tail)
      |for (ptype = first_protocol; ptype < max_protocol; ptype++) 
         |if (skb->protocol == ptype)                              
            |ptype->func -> ip_rcv 
       **** NOW WE KNOW THAT PACKET IS IP ****
            |ip_rcv
               |NF_HOOK (ip_rcv_finish)
                  |ip_route_input 
                     |skb->dst->input -> ip_local_deliver 
                        |ip_defrag // reassembles IP fragments
                           |NF_HOOK (ip_local_deliver_finish)
                              |ipprot->handler -> tcp_v4_rcv 
 
        **** NOW WE KNOW THAT PACKET IS TCP ****
                              |tcp_v4_rcv
                                 |sk = __tcp_v4_lookup
                                 |tcp_v4_do_rcv
                                    |switch(sk->state)
 
 Packet can be sent to the task which uses relative socket ***
                                 |case TCP_ESTABLISHED:
                                 |tcp_rcv_established
                                 |__skb_queue_tail // enqueue packet to socket
                                 |sk->data_ready -> sock_def_readable
                                 |wake_up_interruptible
 
 
        *** Packet has still to be handshaked by 3-way TCP handshake ***
                                    |case TCP_LISTEN:
                                       |tcp_v4_hnd_req
                                          |tcp_v4_search_req
                                          |tcp_check_req
                                          |syn_recv_sock -> tcp_v4_syn_recv_sock
                                          |__tcp_v4_lookup_established
                                    |tcp_rcv_state_process
 
                 *** 3-Way TCP Handshake ***
                                 |switch(sk->state)
                                 |case TCP_LISTEN: // We received SYN
                                         |conn_request -> tcp_v4_conn_request
                                         |tcp_v4_send_synack // Send SYN + ACK
                                         |tcp_v4_synq_add // set SYN state
                                 |case TCP_SYN_SENT: // we received SYN + ACK
                                         |tcp_rcv_synsent_state_process
                                         tcp_set_state(TCP_ESTABLISHED)
                                         |tcp_send_ack
                                                 |tcp_transmit_skb
                                                 |queue_xmit -> ip_queue_xmit
                                                         |ip_queue_xmit2
                                                         |skb->dst->output
                                 |case TCP_SYN_RECV: // We received ACK
                                         |if (ACK)
                                         |tcp_set_state(TCP_ESTABLISHED)
Т.о. вначале мы определяем тип протокола (сначала IP , потом TCP) Функция NF_HOOK (function) управляет сетевым фильтром. После чего идет реализация классического TCP-рукопожатия - 3-way TCP Handshake :
   SERVER (LISTENING)                       CLIENT (CONNECTING)
                              SYN
                      <-------------------
                           SYN + ACK
                      ------------------->
                              ACK
                      <-------------------
                       3-Way TCP handshake
В случае успеха вызываем "tcp_rcv_established[net/ipv4/tcp_input.c]" .

  Стек используется для
   1. хранения глобальных переменных
   2. хранения локальных переменных
   3. возвращения адресов
 Например , при вызове функции c n-параметрами :
    |int foo_function (parameter_1, parameter_2, ..., parameter_n) {
       |variable_1 declaration;
       |variable_2 declaration;
         ..
       |variable_n declaration;
       |// Body function
       |dynamic variable_1 declaration;
       |dynamic variable_2 declaration;
        ..
       |dynamic variable_n declaration;
 
       |// Code in Code Segment
       |return (ret-type) value
    |}
   мы имеем :
             |                       |
             | 1. parameter_1 pushed | \
       S     | 2. parameter_2 pushed |  | Before
       T     | ...................   |  | the calling
       A     | n. parameter_n pushed | /
       C     | ** Return address **  | -- Calling
       K     | 1. local variable_1   | \
             | 2. local variable_2   |  | After
             | .................     |  | the calling
             | n. local variable_n   | /
             |                       |
            ...                     ...   Free
            ...                     ...   stack
             |                       |
       H     | n. dynamic variable_n | \
       E     | ...................   |  | Allocated by
       A     | 2. dynamic variable_2 |  | malloc & kmalloc
       P     | 1. dynamic variable_1 | /
             |_______________________|
Чем различаются понятия - приложение - и - процесс ? Приложение - это исполняемый код . Процесс - образ памяти , которую использует приложение . Синонимом для - процесса - являются понятия - задача - или - тред .

Copy_on_write - механизм для уменьшения используемой памяти . Например , когда какая-то задача порождает другой процесс , он выполняется внутри ее страниц памяти с правами read-only , и если только возникает необходимость для записи данных в память , тогда страницы памяти родителя будут скопированы с правами write .
 Схема механизма прерываний :
                                    |<-->  IRQ(0) [Timer]
                                    |<-->  IRQ(1) [Device 1]
                                    | ..
                                    |<-->  IRQ(n) [Device n]
       _____________________________|
        /|\      /|\          /|\
         |        |            |
        \|/      \|/          \|/
       Task(1)  Task(2) ..   Task(N)
 
Под линуксом , любой ресурс часто бывает задействован между несколькими обьектами , и нужна очередь для распределения доступа к этому ресурсу . Эта очередь называется "wait queue" ":
   ***   wait queue structure [include/linux/wait.h]  ***
   struct __wait_queue {
      unsigned int flags;
      struct task_struct * task;
      struct list_head task_list;
   }
   struct list_head {
      struct list_head *next, *prev;
   };
 Графическая схема :
           ***  wait queue element  ***
                                /|\
                                 |
          <--[prev *, flags, task *, next *]-->
 
                    ***  wait queue list ***
 
             /|\           /|\           /|\                /|\
              |             |             |                  |
       <--[task1]--> <--[task2]--> <--[task3]--> .... <--[taskN]
   |                                                           |
   _____________________________________________________|
 
 
                 ***   wait queue head ***
          task1 <--[prev *, lock, next *]--> taskN
"wait queue head" указывает на первый и последний элементы в очереди "wait queue list". Когда новый элемент добавляется , вызывается "__add_wait_queue" , затем "list_add":
   ***   function list_add [include/linux/list.h]  ***
   // classic double link list insert
   static __inline__ void __list_add (struct list_head * new,  \
                                      struct list_head * prev, \
                                      struct list_head * next) {
      next->prev = new;
      new->next = next;
      new->prev = prev;
      prev->next = new;
   }
Линукс написан на чистом ''C'' , и существуют следующие типы переменных :
   1. Локальные переменные
   2. Переменные модуля
   3. Глобальные статические переменные , 
         видимые во всех модулях
 
Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье
Natalia
  GOOD
2013-04-09 19:53:49