Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
 Kernels
 Boot 
 Memory 
 File system
 0.01
 1.0 
 2.0 
 2.4 
 2.6 
 3.x 
 4.x 
 Интервью 
 Kernel
 HOW-TO 1
 Ptrace
 Kernel-Rebuild-HOWTO
 Runlevel
 Linux daemons
 FAQ
NEWS
Последние статьи :
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
  SQL 30.07   
  JFS 10.06   
  B-trees 01.06   
 
TOP 20
 Go Web ...763 
 Httpd-> История Ap...496 
 2.0-> Linux IP Networking...385 
 MySQL & PosgreSQL...347 
 Trees...345 
 Secure Programming for Li...336 
 Assembler...269 
 TCP 2...245 
 Secure Programming for Li...238 
 Steve Pate 3...235 
 Mod_perl 1...234 
 Cluster 3...215 
 Максвелл 3...214 
 Advanced Bash Scripting G...213 
 Linux Inline Assembly...207 
 Rubni-Corbet -> Глав...207 
 Python...206 
 Steve Pate 1...201 
 Mod_perl 2...198 
 Rodriguez 8...198 
 
  01.06.2017 : 2215600 посещений 

iakovlev.org

Linux Kernel 2.4 Internals Tigran Aivazian tigran@veritas.com

Когда мы набираем команду 'make zImage' или 'make bzImage' , результат должен записаться в каталоге arch/i386/boot/zImage или arch/i386/boot/bzImage . Порядок компиляции следующий :
 1. Файлы с расширением .c и .s нужно откомпилировать 
    в файлы с расширением .o и сгруппировать
    в архивы - файлы с расширением .a  , используя ar.
 2. С помощью ld слинковать полученные обьектные файлы в файл vmlinux
 3. С помощью команды nm vmlinux получаем System.map
 4. Переходим в каталог  arch/i386/boot
 5. bootsect.S и setup.S конвертируется в формат 'raw binary'
 6. Переходим в каталог arch/i386/boot/compressed и конвертируем  
    /usr/src/linux/vmlinux  в $tmppiggy
 7. gzip -9 < $tmppiggy > $tmppiggy.gz
 8. Линкуем $tmppiggy.gz в piggy.o с помощью (ld -r).
 9. Компилируем head.S и misc.c
 10. Линкуем head.o, misc.o и piggy.o в bvmlinux
 11. Конвертируем  bvmlinux в 'raw binary' bvmlinux.out путем удаления comments .
 12. С помощью tools/build из файлов bbootsect, bsetup и compressed/bvmlinux.out 
     получаем  zImage .
Размер бутсектора=512 байт . Размер setup должен быть больше 4-х секторов , но не более 12 КБ : 0x4000 bytes >= 512 + setup_sects * 512 + stack Верхняя граница для zImage - 2.5M .
  Процесс загрузки БИОС-а можно разбить на следующие шаги :
 1. При включении питания тактовый генератор отсылает на шину 
    сигнал  #POWERGOOD.
 2. Включается CPU
 3. Обнуляются основные регистры :
     %ds=%es=%fs=%gs=%ss=0, %cs=0xFFFF0000,%eip = 0x0000FFF0 (ROM BIOS POST code).
 4. Блокировка прерываний
 5. IVT (Interrupt Vector Table) инициализируется в адрес 0.
 6. Вызывается функция биоса BIOS Bootstrap Loader , которая загружает 
    физический адрес 0x7C00.
 
 После чего начинается загрузка бут-сектора .
 Бут-сектор может быть : нативным линуксовым , лило-шным , 
 или же загрузчик вообще может быть loadlin.
 Рассмотрим код в bootsect.s :
 
  SETUPSECS = 4                /* default nr of setup-sectors */
  BOOTSEG   = 0x07C0           /* original address of boot-sector */
  INITSEG   = DEF_INITSEG      /* we move boot here - out of the way */
  SETUPSEG  = DEF_SETUPSEG     /* setup starts here */
  SYSSEG    = DEF_SYSSEG       /* system loaded at 0x10000 (65536) */
  SYSSIZE   = DEF_SYSSIZE
 Значения констант берутся из хидера boot.h :
 #define DEF_INITSEG     0x9000
 #define DEF_SYSSEG      0x1000
 #define DEF_SETUPSEG    0x9020
 #define DEF_SYSSIZE     0x7F00
 
     54          movw    $BOOTSEG, %ax
     55          movw    %ax, %ds
     56          movw    $INITSEG, %ax
     57          movw    %ax, %es
     58          movw    $256, %cx
     59          subw    %si, %si
     60          subw    %di, %di
     61          cld
     62          rep
     63          movsw
     64          ljmp    $INITSEG, $go
 
 65  # bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde).  We
 66  # wouldn't have to worry about this if we checked the top of memory.  Also
 67  # my BIOS can be configured to put the wini drive tables in high memory
 68  # instead of in the vector table.  The old stack might have clobbered the
 69  # drive table.
 
 70  go:     movw    $0x4000-12, %di         # 0x4000 is an arbitrary value >=
 71                                          # length of bootsect + length of
 72                                          # setup + room for stack;
 73                                          # 12 is disk parm size.
 74          movw    %ax, %ds                # ax and es already contain INITSEG
 75          movw    %ax, %ss
 76          movw    %di, %sp                # put stack at INITSEG:0x4000-12.
 
В строчках с номера 54-63 код бутсектора копируется с 0x7C00 в 0x90000 . Достигается это следующим образом : в регистр si пишем адрес-источник 0x7C0:0 , в регистр di пишем адрес-назначение 0x90000 , пишем в cx=256 - или 512 байт - размер сектора , очищаем флаг DF и копируем . Дальше в строках 70-76 идет настройка стека . Затем нужно настроить таблицу секторов , которая позволит работать сразу с 36 секторами.
 89  # Segments are as follows: ds = es = ss = cs - INITSEG, fs = 0,
 90  # and gs is unused.
 
     91          movw    %cx, %fs                # set fs to 0
     92          movw    $0x78, %bx   # fs:bx is parameter table address
     93          pushw   %ds
     94          ldsw    %fs:(%bx), %si          # ds:si is source
     95          movb    $6, %cl                 # copy 12 bytes
     96          pushw   %di                     # di = 0x4000-12.
     97          rep                  # don't need cld -> done on line 66
     98          movsw
     99          popw    %di
    100          popw    %ds
    101          movb    $36, 0x4(%di)           # patch sector count
    102          movw    %di, %fs:(%bx)
    103          movw    %es, %fs:2(%bx)
Дальше грузимся с дискеты по адресу 0x90200 ($INITSEG:0x200), что делается с помощью прерывания 0x13 .
    107  load_setup:
    108          xorb    %ah, %ah                # reset FDC 
    109          xorb    %dl, %dl
    110          int     $0x13   
    111          xorw    %dx, %dx                # drive 0, head 0
    112          movb    $0x02, %cl              # sector 2, track 0
    113          movw    $0x0200, %bx            # address = 512, in INITSEG
    114          movb    $0x02, %ah              # service 2, "read sector(s)"
    115          movb    setup_sects, %al # (assume all on head 0, track 0)
    116          int     $0x13                   # read it
    117          jnc     ok_load_setup           # ok - continue
 
    118          pushw   %ax                     # dump error code
    119          call    print_nl
    120          movw    %sp, %bp
    121          call    print_hex
    122          popw    %ax
    123          jmp     load_setup
    124  ok_load_setup:
 
После чего попадаем на строку 124 . Затем грузим kernel image в физический 0x10000 . Это в нижней памяти . После загрузки , переходим в $SETUPSEG:0 (arch/i386/boot/setup.S). Загруженный kernel перемещается с 0x10000 в 0x1000 . Далее вызывается функция decompress_kernel() . Kernel разворачивается по адресу 0x100000 и запускается . Для этого используется прерывание 0x15.

Для загрузки бут-сектора можно использовать другой путь - ЛИЛО . Этот загрузчик позволяет выбирать между несколькими версиями линукса , при этом можно передавать внешние командные параметры .

Далее происходит т.н. высокоуровневая инициализация :
 устанавливаются сегментные регистры 
 %ds = %es = %fs = %gs = __KERNEL_DS = 0x18 ,
 инициализируются page tables ,
 разрешается paging путем установки бита  PG в %cr0 ,
 очищается BSS ,
 копируются 2kб  bootup-параметров ,
 проверяется тип цпу ,
 вызывается start_kernel(), которая написана уже на си .
 Она блокирует ядро , выполняет первичный setup , 
 на экран выводится версия линукс ,
 инициализируются таблицы traps , irqs , шедулятор ,
 инициализируется консоль ,
 если поддерживаются модули , инициализируется их динамическая поддержка ,
 инициализируется профайлер ,
 разрешаются прерывания ,
 вычисляется BogoMips,
 вычисляется mem_init(),
 инициализация базовых структур procfs,
 fork_init(),
 инициализация VFS, VM, buffer cache,
 создается kernel thread init() и запускаетс с pid=0.
 Этот трэд вызывает do_basic_setup(),который вызывает do_initcalls() 
 с вызовом базовых независимых функций.
 Порядок вызова этих функций зависит от того , в каком порядке 
 они записаны в makefile .
 Если 2 функции зависят друг от друга и при этом статически 
 вкомпилены в ядро ,
 становится важным порядок их компиляции .
При инициализаци операционной системы , большинство кода и данных впоследствии становится ненужным . Ядро компилируется как ELF binary. В ядре есть 2 макроса для инициализации ядра , которые определены в include/linux/init.h : #define __init __attribute__ ((__section__ (".text.init"))) #define __initdata __attribute__ ((__section__ (".data.init"))) Это означает , что код будет размещен в специальную ELF-секцию памяти .text.init . При загрузке будет вызвана функция free_initmem(), которая освободит память между __init_begin и __init_end. Это около 260 КБ памяти .

Процессы .

Каждый процесс создает структуру task_struct . Максимальное количество процессов ограничено только размером физической памяти :
  max_threads = mempages / (THREAD_SIZE/PAGE_SIZE) / 2
или что то же самое - число физических страниц , разделенное на 4 . Можно набрать команду :
  cat /proc/sys/kernel/threads-max
Яковлев С: При обьеме памяти в 400 метров на моем компьютере эта команда выдала число 6143 Процессы обьединены в хэш-таблицу по id-шникам , а также в двойной список - linked list , состоящий из указателей p->next_task И p->prev_task . Хэш-таблица называется pidhash[] и прописана в include/linux/sched.h :
   #define PIDHASH_SZ (4096 >> 2)
   extern struct task_struct *pidhash[PIDHASH_SZ];
   #define pid_hashfn(x)   ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
По этой таблице легко найти процесс по его id-шнику с помощью find_task_pid() , которая определена в include/linux/sched.h :
 	static inline struct task_struct *find_task_by_pid(int pid)
 	{
          struct task_struct *p, **htable = &pidhash[pid_hashfn(pid)];
          for(p = *htable; p && p->pid != pid; p = p->pidhash_next);
          return p;
 	}
Вставка и удаление процесса выполняется с помощью hash_pid() и unhash_pid() Двойной список реализован для прохода по всем процессам . Для этого существует макрос for_each_task() , прописанный в include/linux/sched.h :
     #define for_each_task(p) \
         for (p = &init_task ; (p = p->next_task) != &init_task ; )
Для модификации нужно изменить параметр tasklist_lock , кокотрый по умолчанию установлен на чтение . На время модификации все прерывания должны быть запрещены , что очень важно . Сама структура task_struct является комбинацией 2-х структур - 'struct proc' и 'struct user' . Обычно первая половинка постоянно висит в памяти , а вторая подгружается при работе , хотя it depends . Структура task_struct продекларирована в include/linux/sched.h , размер 1680 bytes
  volatile long state;    /* -1 unrunnable, 0 runnable, >0 stopped */
  #define TASK_RUNNING            0
  #define TASK_INTERRUPTIBLE      1
  #define TASK_UNINTERRUPTIBLE    2
  #define TASK_ZOMBIE             4
  #define TASK_STOPPED            8
  #define TASK_EXCLUSIVE          32
volatile говорит о том , что изменение состояния процесса может быть асинхронным , например по прерыванию .
 TASK_RUNNING - процесс может быть поставлен в очередь , 
                 по умолчанию эта опция выключена
 TASK_INTERRUPTIBLE - процесс в спячке и может быть активирован
 TASK_UNINTERRUPTIBLE  - процес в спячке , причем навсегда
 TASK_ZOMBIE - процесс завершился , но родитель это не отловил
 TASK_STOPPED - процесс остановлен
 TASK_EXCLUSIVE - при пробуждении из спячки этот процесс из общей кучи 
                 будет выдернут вне очереди .
  Процесс имеет флаги :
 unsigned long flags;    /* per process flags, defined below */
 /*
  * Per process flags
  */
 #define PF_ALIGNWARN    0x00000001      /* Print alignment warning msgs */
                                         /* Not implemented yet, only for 486*/
 #define PF_STARTING     0x00000002      /* being created */
 #define PF_EXITING      0x00000004      /* getting shut down */
 #define PF_FORKNOEXEC   0x00000040      /* forked but didn't exec */
 #define PF_SUPERPRIV    0x00000100      /* used super-user privileges */
 #define PF_DUMPCORE     0x00000200      /* dumped core */
 #define PF_SIGNALED     0x00000400      /* killed by a signal */
 #define PF_MEMALLOC     0x00000800      /* Allocating memory */
 #define PF_VFORK        0x00001000      /* Wake up parent in mm_release */
 #define PF_USEDFPU      0x00100000      /* task used FPU this quantum (SMP) */
Поле p->mm есть адрес структуры процесса , p->active_mm - то же самое для процесса ядра . Эти поля позволяют минимизировать переключения между тасками . Поле p->fs структуры включает информацию о рутовой директории и текущей директории . Структура также имеет reference count в случае , если процесс клонируется с помощью системного вызова clone(2).

  Создание процессов и kernel threads
 
 В линуксе есть 3 типа процессов :
   фоновые задачи - idle thread(s),
   kernel threads,
   user tasks.
 
idle thread создаются при загрузке и имеют pid=0 и затем "вручную" размножается для каждого процессора вызовом fork_by_hand() из arch/i386/kernel/smpboot.c. kernel threads создается с помощью clone(2) , не имеют адреса p->mm = NULL и имеют напрямую доступ к kernel address space . user tasks создаются как clone(2) , так и fork(2) .

Функции системных вызовов под линукс имеют префикс sys_ и обладают вложенностью , например sys_exit() вызывает в свою очередь do_exit() , которую можно найти в kernel/exit.c. do_exit() блокирует ядро , вызывает шедулер , устанавливает TASK_ZOMBIE , уведомляет об этом все порожденные процессы , закрывает все файлы .

Роль шедулера - распределять ресурсы процессора между процессами . Его реализация лежит в kernel/sched.c . В структуре процесса есть поля , с которыми работает шедулер :
  p->need_resched - подготовка процесса к вызову
  p->counter - счетчик , при установке которого в 0 
                 включается p->need_resched
  p->priority - приоритет процесса , который может быть изменен 
                 через системный вызов nice(2)
  p->rt_priority - real-time приоритет
  p->policy - тип процесса : может быть SCHED_OTHER , SCHED_FIFO , 
  SCHED_RR , SCHED_YIELD .
Несмотря на кажущуюся простоту , шедулер имеет много функций . В каждой версии он переписывается практически с нуля . Особенности шедулера :
 текущий процесс должен иметь конкретный p->active_mm , иначе что-то не то
 переменные prev и this_cpu - это текущий процесс и текущий процессор
 функция schedule() не может быть вызвана из прерывания

Реализация linked list

Очередь процессов построена с помощью doubly-linked list , которые определены в include/linux/list.h. Базовая структура - list_head :
 
   struct list_head {
         struct list_head *next, *prev;
   };
   #define LIST_HEAD_INIT(name) { &(name), &(name) }
   #define LIST_HEAD(name) \
         struct list_head name = LIST_HEAD_INIT(name)
   #define INIT_LIST_HEAD(ptr) do { \
         (ptr)->next = (ptr); (ptr)->prev = (ptr); \
   } while (0)
   #define list_entry(ptr, type, member) \
         ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
   #define list_for_each(pos, head) \
         for (pos = (head)->next; pos != (head); pos = pos->next)
 
Первые 3 макроса служат для инициализации пустого списка,при этом оба указателя указывают на себя . Они создают 3 экземпляра , каждый из которых служит для различных целей. 4-й макрос list_entry() дает доступ к индивидуальным элементам списка , например :
 
  struct super_block {
    ...
    struct list_head s_files;
    ...
  } *sb = &some_super_block;
 
  struct file {
    ...
    struct list_head f_list;
    ...
  } *file;
 
  struct list_head *p;
 
  for (p = sb->s_files.next; p != &sb->s_files; p = p->next) {
      struct file *file = list_entry(p, struct file, f_list);
      do something to 'file'
  }
 
 С помощью list_for_each() шедулер просматривает очередь процессов :
  static LIST_HEAD(runqueue_head);
  struct list_head *tmp;
  struct task_struct *p;
 
  list_for_each(tmp, &runqueue_head) {
     p = list_entry(tmp, struct task_struct, run_list);
     if (can_schedule(p)) {
         int weight = goodness(p, this_cpu, prev->active_mm);
         if (weight > c)
             c = weight, next = p;
     }
  }
 
Модернизация task list выполняется с помощью list_del()/list_add()/list_add_tail() . Следующий пример показывает реализацию механизма этого процесса :
  static inline void del_from_runqueue(struct task_struct * p)
  {
         nr_running--;
         list_del(&p->run_list);
         p->run_list.next = NULL;
  }
 
  static inline void add_to_runqueue(struct task_struct * p)
  {
         list_add(&p->run_list, &runqueue_head);
         nr_running++;
  }
 
  static inline void move_last_runqueue(struct task_struct * p)
  {
         list_del(&p->run_list);
         list_add_tail(&p->run_list, &runqueue_head);
  }
 
  static inline void move_first_runqueue(struct task_struct * p)
  {
         list_del(&p->run_list);
         list_add(&p->run_list, &runqueue_head);
  }
 

Очередь

Процесс посылает запрос ядру на собственную активацию . Этот запрос может быть либо принят , либо отклонен , во втором случае он становится "спящим" и ждет лучших времен . Механизм ожидания называется 'wait queue'. Имеется специальный флаг TASK_EXCLUSIVE . Для работы с очередью можно использовать
 sleep_on/sleep_on_timeout/interruptible_sleep_on/interruptible_sleep_on_timeout,
 add/remove_wait_queue , wake_up/wake_up_interruptible .
Например , когда page allocator нужно освободить память , вызывается из спячки демон kswapd . В качестве примера автономной очереди можно рассмотреть взаимодействие процессов пользователя , работающих с некими ресурсами , с ядром с использованием механизма прерываний :
 
  static DECLARE_WAIT_QUEUE_HEAD(rtc_wait);
  void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
  {
         spin_lock(&rtc_lock);
         rtc_irq_data = CMOS_READ(RTC_INTR_FLAGS);
         spin_unlock(&rtc_lock);
         wake_up_interruptible(&rtc_wait);
  }
 
В функции данные получают за счет прерывания с какого-то специфического порта , после чего проверяют очередь rtc_wait Далее идет реализация системного вызова read(2):
  ssize_t rtc_read(struct file file, char *buf, size_t count, loff_t *ppos)
  {
         DECLARE_WAITQUEUE(wait, current);
         unsigned long data;
         ssize_t retval;
 
         add_wait_queue(&rtc_wait, &wait);
         current->state = TASK_INTERRUPTIBLE;
         do {
                 spin_lock_irq(&rtc_lock);
                 data = rtc_irq_data;
                 rtc_irq_data = 0;
                 spin_unlock_irq(&rtc_lock);
 
                 if (data != 0)
                         break;
 
                 if (file->f_flags & O_NONBLOCK) {
                         retval = -EAGAIN;
                         goto out;
                 }
                 if (signal_pending(current)) {
                         retval = -ERESTARTSYS;
                         goto out;
                 }
                 schedule();
         } while(1);
         retval = put_user(data, (unsigned long *)buf);
         if (!retval)
                 retval = sizeof(unsigned long);
 
  out:
         current->state = TASK_RUNNING;
         remove_wait_queue(&rtc_wait, &wait);
         return retval;
  }
 
Здесь мы сначала декларируем указатель на текущий пользовательский процесс , который добавляем в очередь rtc_wait . Данный процесс помечается как TASK_INTERRUPTIBLE. Если есть данные , они копируются в буффер , маркируем TASK_RUNNING , удаляем себя из очереди . Также проверяются системные вызовы sigaction(2). Далее , процесс засыпает до следующего прерывания .

  Механизм system calls реализован двояко :
   вызов lcall7/lcall27
   прерывание int 0x80
Первый вариант используется в солярисе . Второй вариант - основной . При загрузке системы функция arch/i386/kernel/traps.c:trap_init() инициализирует таблицу IDT , при этом 0x80 настраивается на system_call в arch/i386/kernel/entry.S . При вызове системной функции приложение выполняет команду 'int 0x80' . При этом :
  читаются регистры
  регистры  %ds и %es устанавливаются в KERNEL_DS
  читаются аргументы из стека
 Для системных вызовов может быть до 6 аргументов .
 Они передаются через регистры  %ebx, %ecx, %edx, %esi, %edi , %ebp .
Spinlocks
На начальном этапе развития линукса , актуальной была проблема расшаренных ресурсов . Поддержка SMP была добавлена в версии 1.3.42 в 1995 году . При возникновении коллизии между процессом и прерыванием , применяется обычный механизм сохранения :
  unsigned long flags;
  save_flags(flags);
  cli();
  /* critical code */
  restore_flags(flags);
 
Это работает для одного ЦПУ , но не работает для нескольких. Для этого существуют spinlocks.
 Существуют 3 типа spinlocks
  vanilla (basic)
  read-write
  big-reader spinlocks
Read-write spinlocks используется тогда , когда к малому числу ресурсов обращается большое число процессов, Если происходит запись , то писать может только один процесс , все остальные читающие при этом полностью блокируются .
  Spinlocks бывают 3 типов :
  1. spin_lock()/spin_unlock() - используются тогда , когда нет прерываний
  2. spin_lock_irq()/spin_unlock_irq() - используются с прерываниями
  3. spin_lock_irqsave()/spin_unlock_irqrestore() - комбинированный вариант
 Пример :
 
  spinlock_t my_lock = SPIN_LOCK_UNLOCKED;
  my_ioctl()
  {
         spin_lock_irq(&my_lock);
         /* critical section */
         spin_unlock_irq(&my_lock);
  }
  my_irq_handler()
  {
         spin_lock(&lock);
         /* critical section */
         spin_unlock(&lock);
  }
 
Semaphores
  Семафоры служат для блокировки пользовательских процессов .
 Существуют 2 типа семафоров :
   basic
   read-write
 Пример исользования семафора в kernel/sys.c :
 
 asmlinkage long sys_sethostname(char *name, int len)
 {
         int errno;
 
         if (!capable(CAP_SYS_ADMIN))
                 return -EPERM;
         if (len < 0 || len > __NEW_UTS_LEN)
                 return -EINVAL;
         down_write(&uts_sem);
         errno = -EFAULT;
         if (!copy_from_user(system_utsname.nodename, name, len)) {
                 system_utsname.nodename[len] = 0;
                 errno = 0;
         }
         up_write(&uts_sem);
         return errno;
 }
 
 asmlinkage long sys_gethostname(char *name, int len)
 {
         int i, errno;
 
         if (len < 0)
                 return -EINVAL;
         down_read(&uts_sem);
         i = 1 + strlen(system_utsname.nodename);
         if (i > len)
                 i = len;
         errno = 0;
         if (copy_to_user(name, system_utsname.nodename, i))
                 errno = -EFAULT;
         up_read(&uts_sem);
         return errno;
 }
 
Loading Modules
Linux есть и всегда будет монолитным, это означает, что все подсистемы работают в привелигированном режиме и используют общее адресное пространство; связь между ними выполняется через обычные C-функции. Однако, не смотря на то, что выделение функциональности ядра в отдельные "процессы" (как это делается в ОС на микро-ядре) - определенно не лучшее решение, тем не менее, в некоторых случаях, желательно наличие поддержки динамически загружаемых модулей (например: на машинах с небольшим объемом памяти или для ядер, которые автоматически подбирают (auto-probing) взаимоисключающие драйверы для ISA устройств). Поддержка загружаемых модулей устанавливается опцией CONFIG_MODULES во время сборки ядра. Поддержка автозагружаемых модулей через механизм request_module() определяется отдельной опцией (CONFIG_KMOD).
Ниже приведены функциональные возможности, которые могут быть реализованы как загружаемые модули:
    1. Драйверы символьных и блочных устройств.
    2. Terminal line disciplines.
    3. Виртуальные (обычные) файлы в /proc и 
       в devfs (например /dev/cpu/microcode и /dev/misc/microcode).
    4. Обработка двоичных форматов файлов (например ELF, a.out, и пр.).
    5. Обработка доменов исполнения (например Linux, UnixWare7, Solaris, и пр.).
    6. Файловые системы.
    7. System V IPC.
А здесь то, что нельзя вынести в модули (вероятно потому, что это не имеет смысла):
    1. Алгоритмы планирования.
    2. Политики VM (VM policies).
    3. Кэш буфера, кэш страниц и другие кзши.
Решение о поддержки модулей принимается на этапе компиляции и определяется опцией CONFIG_MODULES . В линуксе есть несколько системных вызовов для работы с модулями :
  1 caddr_t create_module
  2 long init_module
  3 long delete_module
  4 long query_module
 
  Из командной строки возможны команды :
   insmod   - загрузка 1 модуля
   modprobe - загрузка дерева модулей
   rmmod    - выгрузка модуля
   modinfo: - получение информации о модуле
 kernel-функция request_module(name) создает при этом трэд
 Пример загрузки модуля в вызове mount(2) system call :
 
 static struct file_system_type *get_fs_type(const char *name)
 {
         struct file_system_type *fs;
         read_lock(&file_systems_lock);
         fs = *(find_filesystem(name));
         if (fs && !try_inc_mod_count(fs->owner))
                 fs = NULL;
         read_unlock(&file_systems_lock);
         if (!fs && (request_module(name) == 0)) {
                 read_lock(&file_systems_lock);
                 fs = *(find_filesystem(name));
                 if (fs && !try_inc_mod_count(fs->owner))
                         fs = NULL;
                 read_unlock(&file_systems_lock);
         }
         return fs;
 }
 
 
 
Оставьте свой комментарий !

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

 Автор  Комментарий к данной статье
Sezon
  Нормалек !!!
2007-06-20 22:35:25