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 
 5.x 
 6.x 
 Интервью 
 Kernel
 HOW-TO 1
 Ptrace
 Kernel-Rebuild-HOWTO
 Runlevel
 Linux daemons
 FAQ
NEWS
Последние статьи :
  Тренажёр 16.01   
  Эльбрус 05.12   
  Алгоритмы 12.04   
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
 
TOP 20
 Linux Kernel 2.6...5175 
 Trees...951 
 Максвелл 3...877 
 Go Web ...832 
 William Gropp...816 
 Ethreal 3...792 
 Gary V.Vaughan-> Libtool...782 
 Ethreal 4...776 
 Rodriguez 6...772 
 Ext4 FS...766 
 Clickhouse...765 
 Steve Pate 1...762 
 Ethreal 1...747 
 Secure Programming for Li...737 
 C++ Patterns 3...724 
 Ulrich Drepper...706 
 Assembler...699 
 DevFS...670 
 Стивенс 9...657 
 MySQL & PosgreSQL...639 
 
  01.01.2024 : 3621733 посещений 

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