Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Похожесть кода PowerPC и х86



Обратите внимание, что код для PowerPC и х86 объединяется вместе в функции start_kernel () в init /main. с. Эта функция, расположенная в архитектурно-неза­висимой части код, вызывает архитектурно-зависимые функции для завершения инициа­лизации памяти.

Первой вызываемой инструкцией в этом файле является setup_arch(), которая определена для платформы х86 в arch/i3 8 б /kernel/setup. с и которая далее вызы­вает pagetable_init () в этом же файле. Остаток системной памяти выделяется для составления последней таблицы страниц.

В мире PowerPC многое уже сделано. Функция setup_arch() в файле arch/ ррс/kernel/setup. с вызывает paging__init () в arch/ppc/mm/init. с. Одна из важных функций для РРС paging_init () устанавливает все страницы в зоне DMA.


432 Глава 8 • Загрузка ядра

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

                                 
  Logical Address    
  15 С ) 32 ( )  
  Selector   Offset    
                 
(   Y      
      Descriptor Base Address +   Linear Address    
  gdtr      
   
           
        32 22 21 < |r лг л i    
bioDai uescripior iaoie г (cpu_gdt_desc) [ PDE РТЕ | Offset    
           
  I      
  I      
           
      > j Physical Address  
         
       
Page Table Entry Physical Pages    
           
  cr3 h —► Directory Entry    
    Page Table    
      J IDUUI  
        Page Directory (swapper_pg_dir)                        
                                     

Рис. 8.10. Загрузочные страницы

Диск начальной загрузки

LILO, GRUB и Yaboot поддерживают диск начальной загрузки (initrd), работающий как корневая файловая система до того, как настоящая файловая система загружается и инициализируется. Завершением загрузки настоящей файловой системы мы считаем ее монтирование к корню.

Начальные шаги позволяют Linux загрузить несколько скомпилированных модулей и динамически загрузить другие модули и драйверы из initrd. Главное отличие от за-


8.5 Начало: start_kernel() 433

грузчика заключается в том, что он загружается как минимальное ядро и диск в оператив­ной памяти во время шага 2. Ядро инициализирует использование диска в памяти, мон­тирует финальную корневую файловую систему и затем удаляет initrd. initrd позволяет нам:

• настроить ядро во время загрузки;

• сохранить ядро минимально функциональным;

• обладать ядром для нескольких аппаратных конфигураций.

Предыдущие строки являются основными при загрузке с помощью Yaboot, GRUB и LILO. Каждый загрузчик имеет богатый набор команд для своих настроечных файлов. Для измененного или использующего специальные функции загрузочного процесса, бы­строго поиска в веб в GRUB и использования настроечных файлов LILO требуется допол­нительная информация.

Теперь, когда мы увидели, как ядро загружается и как начинается инициализация па­мяти, давайте рассмотрим процессы инициализации ядра.

8.5 Начало: start_kernel()

Это обсуждение началось с перехода в функцию start_kernel () (init/main. с), которая является первой вызываемой архитектурно-зависимой частью кода.

Во время перехода в start_kernel () мы выполняем процесс 0, также известный как root thread (корневой процесс). Процесс 0 порождает процесс 1, известный как про­цесс инициализации. Процесс 0 становится ожидающим потоком процессора. Когда вы­зывается /sbin/init, у нас есть только два запущенных процесса:

init/main.c

396 asmlinkage void _ init start_kernel(void)

397 {

3 98 char * command_line;

3 99 extern char saved_command_line[];

400 extern struct kernel_param____ start__ param[], _ stop__ param[];

405 lock_kernel();

406 page_address_init();

407 printk(linux_banner);

408 setup_arch(& command_line);

409 setup_per_cpu_areas();

415 smp_prepare_boot_cpu ();



sched_init();



Глава 8 • Загрузка ядр


424 build_all_zonelists();

425 page_alloc_init();

426 printk(" Kernel command line: %s\n", saved_command_line);

427 parse_args(" Booting kernel", command_line, _ start__ param,

428 __ stop__ param - _ start__ param,

429 & unknown__bootoption);
43 0 sort_main_extable();

431 trap_init();

432 rcu_init();

433 init_IRQ();

434 pidhash_init();

435 init_timers (); 43 6 softirq_init(); 437 time_init();

 

444 console__init ();

445 if (panic_later)

446 panic (panic_later, panic_param)

447 profile_init();

448 local_irq_enable();

449 #ifdef CONFIG_BLK_DEV_INITRD

450 if (initrd_start & & ! initrd_below__start__ok & &

451 initrd_start < min_low_pfn « PAGE_SHIFT) {

452 printk(KERN_CRIT " initrd overwritten (0x%081x < 0x%081x) - "

453 " disabling it.\n", initrd_start, min_low_pfn « PAGE_SHIFT);

454 initrd_start = 0;

455 }

456 #endif

457 mem_init();

458 kmem__cache_init ();

459 if (late_time_init)

460 late_time_init();

461 calibrate_delay();

462 pidmap__init ();

463 pgtable_cache_init();

464 prio_tree_init();

465 anon_vma_init();

466 #ifdef CONFIG_X86

467 if (efi_enabled)

468 efi_enter_yirtual_mode();

469 #endif

470 fork_init(num_physpages);

471 proc_caches_init();

472 buffer_init();

473 unnamed_dev_init();


8.5 Начало: start_kemel()



474 security_scaffolding_startup();

475 vf s_caches_init (num__physpages);
47 6 radix_tree_init();

477 signals_init();

478 /* содержимое rootfs нужно перезаписать обратно */
47 9 page_writeback_init();

480 #ifdef CONFIG_PROC_FS

481 proc_root_init();

482 #endif

483 check_bugs();

490 init_idle(current, smp_processor_id());

493 rest_init();

494 }

8.5.1 Вызов lock_kernel()

Строка 405

В ядре Linux 2.6 конфигурация по умолчанию представляет собой вытесняющее ядро. Вытесняющее ядро означает, что само ядро может быть прервано высо­коприоритетной задачей, такой, как аппаратное прерывание, и управление будет передано высокоприоритетной задаче. Ядро должно сохранять достаточно состоя­ний, чтобы продолжать выполнение после обработки высокоприоритетной задачи.

Ранние версии реализации Linux включали приоритетное прерывание обработки ядра и блокировку SMP с помощью Big Kernel Lock (BKL)1. Позднейшие версии Linux абстрагировали приоритетную обработку прерываний в различные вызовы, такие, как preenipt_disable(). Во время инициализации BKL используется до сих пор. Это рекурсивная циклическая блокировка, отбирающая некоторое время у данного процес­сора. Подобным эффектом использования BKL является отключение приоритетного прерывания обработки, что во время инициализации крайне важно.

Блокировка ядра предотвращает его от того, что оно будет прервано или вытеснено какой-либо другой задачей. Linux использует для этой цели BKL. Когда ядро блокируется, другие процессы не выполняются. Этот антитезис приоритетного прерывания обработки ядра может быть прерван в любой момент. В ядре Linux 2.6 мы используем BKL для бло­кировки ядра во время загрузки и инициализации различных объектов ядра без опасения, что они будут прерваны. Ядро разблокирует строку 493 в функции rest_init (). Поэто­му start_kernel () возникает при заблокированном ядре. Давайте рассмотрим, что происходит в lock_kernel ().

1 Большая блокировка ядра. Примеч. пер.



Глава 8 • Загрузка ядра


include/linux/smp_lock.h

42 static inline void lock_kernel(void)

43 {

 

44 int depth = current-> lock_depth+l;

45 if (likely(! depth))

46 get_kernel_lock();

47 current-> lock_depth = depth;

48 }

Строки 44-48

Задача init имеет специальную lock_depth -1. Это позволяет мультипроцес­сорным системам не пытаться заблокировать выполнение ядра сразу на нескольких процессорах. Так как задачу init выполняет только один процессор, только он мо­жет блокировать выполнение ядра, так как только init имеет depth 0 (в против­ном случае depth больше 0). Похожий трюк используется в unlock_kernel (), где мы тестируем (--current-> lock_depth < 0). Давайте рассмотрим, что происходит в get_kernel_lock ().

include/linux/smp_lock.h

10 extern spinlock_t kernel_flag;

12 #define kernel_locked() (current-> lock_depth > = 0)

14 #define get_kernel_lock() spin_lock(& kernel_flag)

15 tdefine put_kernel_lock () spin_unlock(& kernel_flag)

 

59 tdefine lock_kernel() do { } while(0)

60 #define unlock_kernel() do { } while(O)

61 #define release_kernel_JLock(task) do { } while(0)

62 #define reacquire_kernel_lock(task) do { } while(0)

63 #define kernel_locked() 1

Строки 10-15

Этот макрос описывает большую блокировку ядра, использующую стандартную функцию циклической блокировки. На многопроцессорных системах возможна си­туация, когда два процессора могут попытаться получить доступ к одной и той же структуре данных. Циклический блок, описанный в гл. 7, предотвращает этот тип соглашения.


8.5 Начало: start_kernel()



Строки 59-63

В этом случае ядро не вытесняется и не работает на многопроцессорных системах, мы просто ничего не делам для lock_kernel (), так как прерывания не поступают.

Теперь ядро исчерпало BKL и избавляется от него до конца start_kernel (), в результате все следующие команды не могут быть вытеснены.

8.5.2 Вызов page_addressjnit()

Строка 406

Вызов page_address_init () является первой функцией, связанной с инициали­зацией подсистемы памяти в архитектурно-зависимой части кода. Определение page_address_init О варьируется в зависимости от определения трех параметров компиляции. Первые два отключают page_address_init (), заме­щая тело функции на do {} while (0), как показано в следующем коде. Третью операцию мы рассмотрим подробнее. Давайте рассмотрим их определение и способ включения.

include/linux/mm.h

376 #if defined(WANT_PAGE_VIRTUAL)

382 #define page_address__init () do { } while (0)

385 #if defined(HASHED_PAGE_VIRTUAL)

388 void page_address_init(void);

3 91 #if! defined (HASHED_PAGE_VIRTUAL) & & ! defined (WANT_PAGE_VIRTUAL)

3 94 #define page_address__init () do { } while (0)

#define для WANT__PAGE_VIRTUAL устанавливается, когда система использует прямое отображение памяти, и при этом просто рассчитывает виртуальный адрес в памяти для доступа к адресу в памяти. В случае, когда вся память не отображается в пространство адресов ядра (как часто бывает при использовании himem), нам нужен более удобный способ получения адресов. Поэтому инициализация странниц адресов определяется толь­ко в случае установленного HASHED_PAGE_VIRTUAL.

Теперь рассмотрим случай, когда ядру указывается использование HASHED_PAGE_ VIRTUAL и когда нам нужно инициализировать применяемую ядром виртуальную па­мять. Имейте в виду, что это происходит, только если настроена himem; при этом количе­ство памяти, к которому может обратиться ядро, может быть больше, чем отображено в адресное пространство ядра (обычно 4 Гб).



Глава 8 • Загрузка ядра


В процессе последующих определений функций создаются и пересматриваются раз­личные объекты ядра. Табл. 8.3 иллюстрирует объекты ядра, представленные в процессе рассмотрения page_address_init ().

Таблица 8.3. Представленные при вызове page_addr-ess_init() объекты

Объект Описание

page_address_map Структура

page_address_slot Структура

page_address_pool Глобальная переменная

page_address_maps Глобальная переменная

page_address__htable Глобальная переменная

mm/highmem.c

510 static struct page_address_slot {

511 struct list_head lh;

512 spinlock_t lock;

513 } __ cacheline_aligned_in_smp page_address_htable[l«PA_HASH_ORDER];

591 static struct page_address_map page_address_maps[LAST_PKMAP]; 592

593 void _ init page_address_init(void)

594 {

595 int i;
596

597 INIT_LIST_HEAD(& page_address_pool);

598 for (i = 0; i < ARRAY_SIZE(page_address_maps); i++)

599 list_add(& page_address_maps [i].list, & page_address_pool);

600 for (i = 0; i < ARRAY_SIZE(page_address_htable); i++) {

601 INIT_LIST_HEAD(& page_address_htable[i].lh);

602 spin_lock_init(& page_address_htable[i].lock);

 

603 }

604 spin_lock_init(& pool_lock);

605 }

Строка 597

Главное назначение этой строки заключается в инициализации глобальной перемен­ной page_address_global (), являющейся структурой типа list_head и ука­зывающей на список свободных выделенных из page_address_maps (строка 591) страниц. Рис. 8.11 иллюстрирует page_address_pool.


8.5 Начало: start_kernel()



 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

                                   
      struct page   struct page   struct page    
  static struct list head ] page_address_pool j 1—n      
             
     
                     
    struct page_address_map       struct page_address_map         struct page_address_map      
  struct page* page     struct page 'page     struct page'page    
               
                       
  void 'virtual     void 'virtual     void 'virtual    
                   
  struct listjiead list     struct list_head list     struct listjiead list        
    *   > *   >        
                   
               
       
                                       

Рис. 8.11. Структуры данных, связанные с пулом карты адресов страниц

Строки 598-599

Мы добавляем каждый список страниц из page___address_maps в двусвязный список в page_address__pool. Далее мы подробно опишем структуру page_address_maps.

Строки 600-603

Мы инициализируем каждый list_head хеш-таблицы адресов страниц и цик­лические блокировки. Переменная page__address_htable хранит список эле­ментов хеша для тех же участков памяти. Рис. 8.12 иллюстрирует хеш-таблицу адресов страниц.

Строка 604

Мы инициализируем циклическую блокировку page_address_pool.

Давайте рассмотрим структуру page_address_map, чтобы лучше понять, что за список мы только что инициализировали. Главное назначение этой структуры - под­держание связи между страницами и их виртуальными адресами. Если страницы отображаются в виртуальные адреса линейно, это совершенно не нужно. Необходимость в этой структуре появляется только при хешировании адресов:

mm/highmem.c

490 struct page_address__map {

491 struct page *page;

492 void *virtual;



Глава 8 • Загрузка ядра


struct page_address_htable struct page_address_slot

struct list_head Ih


раде


раде


раде


spinlockj lock


struct page_address_slot

struct list_head Ih


раде


spinlockj lock

Рис. 8.12. Хеш-таблица адресов страниц

493 struct list_head list;

494 };

Как вы можете видеть, объект хранит указатель на структуру страницы, связанную с данной страницей, указатель на виртуальный адрес и структуру list_head для обо­значения позиции в двусвязном списке адресов страниц.

8.5.3 Вызов printk(linux_banner)

Строка 407

Вызов отвечает за первый вывод в консоль, выполняемый ядром Linux. Он пред­ставлен глобальной переменной linux_banner:

init/version.с

31 const char *linux_banner =

32 " Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY " @"

LINUX__COMPILE_HOST " ) (" LINUX_COMPILER " ) " UTS_VERSION " \n";

Файл version, с определяет linux_banner, как показано выше. Эта строка ин­формирует пользователя о версии ядра Linux, версии дсс, с помощью которой оно отком­пилировано, и название релиза.


8.5 Начало: start_kernel()



8.5.4 Вызов setup_arch

Строка 408

Функция setup_arch() в arch/i386/kernel/setup.c преобразуется в тип

__ init (обратитесь к описанию____ init в гл. 2), запускаемый только один раз во

время инициализации системы. Функция setup_arch () получает указатель на за­данные во время загрузки в командной строке Linux данные и инициализирует мно­жество архитектурно-зависимых подсистем, таких, как память, ввод-вывод, процес­соры и консоли.

arch/i3 86/kernel/setup.c

1083 void _ init setup_arch(char **cmdline_p)

1084 {

1085 unsigned long max_low_pfn;
1086

1087 memcpy(& boot_cpu_data, & new_cpu_data, sizeof(new_cpu_data));

1088 pre_setup_arch_hook();

1089 early_cpu_init(); 1090

1091 /*

1092 * FIXME: сейчас это неофициальный loader_type

1093 * хотя он и работает с elilo.

1094 * Если мы настраиваем ядро EFI, нужно проверить,

1095 * что загрузка из elilo прошла успешно и что системная

1096 * таблица верна. Если нет, инициализация проводится как обычно.

1097 */

1098 #ifdef CONFIG_EFI

1099 if ((LOADER_TYPE == 0x50) & & EFI_SYSTAB)

1100 efi_enabled = 1;

1101 #endif
1102

1103 ROOT_DEV = old_decode_dev(ORIG_ROOT_DEV);

1104 drive_info = DRIVE_INFO;

1105 screen_info = SCREEN_INFO;

1106 edid_info = EDID_INFO;

1107 apm_info.bios = APM_BIOS_INFO;

1108 ist_info = IST_INFO;

1109 saved_videomode = VIDEO_MODE;

1110 if( SYS_DESC_TABLE.length! = 0 ) {

 

1111 MCA_bus = SYS_DESC_TABLE. table [3] & 0x2;

1112 machine_id = SYS_DESC_TABLE.table[0];

1113 machine_submodel_id = SYS_DESC_TABLE.table[1];

1114 BIOS_revision = SYS_DESC_TABLE.table[2];

1115 }



Глава 8 • Загрузка ядра


1116 aux_device_present = AUX_DEVICE__INFO;
1117

1118 #ifdef CONFIG_BLK_DEV__RAM

1119 rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE__START__MASK;

1120 rd__prompt = ( (RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG)! = 0);

1121 rd_doload = ( (RAMDISK_FLAGS & RAMDISK_LOAD_FLAG)! = 0);

1122 #endif

1123 ARCH_SETUP

1124 if (efi_enabled)

1125 efi_init();

1126 else

1127 setup_memory_region();
1128

1129 copy_edd();
1130

1131 if (! MOUNT_ROOT_RDONLY)

1132 root__mountflags & = ~MS__RDONLY;

1133 init_mm.start_code = (unsigned long) __text;

1134 init_mm.end_code = (unsigned long) _etext;

1135 init_mm.end_data = (unsigned long) _edata;

113 6 init_mm.brk = init_pg_tables_end + PAGE_OFFSET;
1137

1138 code_resource.start = virt_to_phys(_text);

1139 code__resource.end = virt__to_phys (_etext)-1;

1140 data_resource.start = virt_to_phys(_etext);

1141 data_resource.end = virt_to_phys(_edata)-1; 1142

1143 parse_cmdline_early(cmdline_p);

1145 max.low pfn = setup__memory ();

1147 /*

1148 * ПРИМЕЧАНИЕ. Перед этой точкой _nobody_ позволено выделить

1149 * всю память, используя выделитель памяти bootmem.

1150 */ 1151

1152 #ifdef CONFIG_SMP

1153 smp_alloc_jnemory(); /* Стеки реального режима процессора АР1

в нижней памяти*/

1154 #endif

1155 paging_init();
1156

1 АР (application processor) - любой процессор SMP-системы, который не является BSP (bootstrap processor)-процессором, т. е. не является процессором, которому передается управление в момент загрузки. Примеч. науч. ред.


8.5 Начало: start_kernel()



1157 #ifdef CONFIG_EARLY_PRINTK

1158 {

1159 char *s = strstr(*cmdline_p/ " earlyprintk^');

1160 if (s) {

1161 extern void setup_early_printk(char *);
1162

1163 setup_early_printk(s);

1164 printk(" early console enabled\n" );

1165 }

1166 }

1167 #endif

1170 dmi_scan_machine();
1171

1172 #ifdef CONFIG_X86_GENERICARCH

1173 generic_apic_probe(*cmdline_p);

1174 #endif

1175 if (efi_enabled)

1176 e f i_map_memmap();
1177

1178 /*

1179 * Парсинг таблицы ACPI для возможной конфигурации SMP

1180 * времени загрузки. */

1181 acpi_boot_init(); 1182

1183 #ifdef CONFIG_X86__LOCAL_APIC

1184 if (smp_found_config)

1185 get_smp__conf ig();

1186 #endif
1187

1188 register_memory(max_low_pfn);
1188

1190 #ifdef CONFIG_VT

1191 #if defined(CONFIG_VGA_CONSOLE)

1192 if (! efi_enabled || (efi_mem_type(OxaOOOO)! =

EFI_CONVENTIONAL_MEMORY))

1193 conswitchp = & vga_con;

1194 #elif defined(CONFIG_DUMMY_CONSOLE)

1195 conswitchp = & dummy_con;

1196 #endif

1197 #endif

1198 }



Глава 8 • Загрузка ядра


Строка 1087

Получение boot_cpu_date, являющегося указателем на структуру cpuinfo_ х8б, заполняемую во время загрузки. Это похоже на то, что происходит на РРС.

Строка 1088

Активация любых машинно-специфических функций идентификации. Можно найти в arch/xxx/machine-ddefault/setup.с.

Строка 1089

Идентификация специфического процессора.

Строки 1103-1116

Получение параметров загрузки системы.

Строки 1118-1122

Получение диска в памяти при установке в arch/< arch> /def conf ig.

Строки 1124-1127

Инициализация расширенного интерфейса Firmware (если установлено в /def con-fig) или просто вывод карты памяти BIOS.

Строка 1129

Сохранение параметров с загрузочного расширенного привода диска.

Строки 1133-1141

Инициализация структуры менеджера памяти из предоставленной BIOS карты па­мяти.

Строка 1143

Запуск парсинга командной строки Linux. (См. arch/< arch> /kernel/set­up, с.)

Строка 1145

Инициализация/резервирование загрузочной памяти. (См. arch/i3 8 б /kernel/ setup.с.)

Строки 1153-1155

Получение страницы для инициализации SMP или инициализации страниц за преде­лами 8 Мб памяти, уже инициализированной в head.S. (См. arch/i3 8 б/mm/ init.с.)

Строки 1157-1167

Получение printk (), запущенной, даже если консоль еще не полностью инициа­лизирована.


8.5 Начало: start_kernel()



Строка 1170

Это строка Desktop Management Interface (DMI)1, собирающего информацию о спе­цифических аппаратно-зависимых конфигурациях из BIOS. (См. arch/i386/ kernel /dmi_s can. с.)

Строки 1172-1174

Если конфигурация его вызывает, просматривается APIC для заданной командной строки. (См. arch/ i3 8 б /kernel /probe, с.)

Строки 1175-1176

Если используется интерфейс расширяемого Firmware, то перезаполнить карту па­мяти EFI. (См. arch/i386/kernel/efi.c.)

Строка 1181

Просмотр APIC локального и ввода-вывода. (См. arch/i386/kernel/acpi/ boot. с.) Поиск и проверка контрольной суммы таблицы описания системы. (См. drivers/acpi/tables. с.) Для лучшего понимания ACPI обратитесь к проекту ACPI4LINUX в сети.

Строки 1183-1186

Сканирование конфигурации SMP. (См. arch/i386/kernel/mpparse.c.) Этот раздел также использует настроечную информацию ACPI.

Строка 1188

Запрос ввода-вывода и пространства памяти для стандартных ресурсов. (См. реги­страцию ресурсов в arch/i3 86/kernel/std_resources.c.)

Строки 1190-1197

Настройка структуры переключения VBGA консоли (См. drivers/video/con-sole/vgacon. с.)

Похожую, но более короткую версию setup_arch () для PowerPC можно найти и в arch/ppc /kernel /setup. с. Эта функция инициализирует большую часть структуры ppc_md. Вызов pmac_f eature_init () в arch/ppc /plat f orm/pmac_f eature. с вызывает начальную проверку и инициализацию оборудования ртас.

8.5.5 Вызов setup_per_cpu_areas()

Строка 409

Функция setup_per_cpu_areas () существует для настройки многопроцес­сорного окружения. Если ядро Linux скомпилировано без поддержки SMP, setup_per_cpu_areas () замещается ничегонеделанием следующим образом:

1 Интерфейс управления рабочим столом. Примеч. пер.



Глава 8 • Загрузка ядра


init/main.c

317 static inline void setup_per_cpu_areas(void) { }

Если ядро Linux компилируется с поддержкой SMP, setup__per_cpu_areas () определяется следующим образом:

init/main.с

327 static void _ init setup_per_cpu_areas(void)

328 {

329 unsigned long size, i;
33 0 char *ptr;

331 /* Создание с помощью магии компоновщика */

332 extern char _ per_cpu_start[], _ per_cpu_end[ ];

333 /* Копирование выбранного для каждого процессора (оригинал

334 * затирается) */

335 size = ALIGN(_ per_cpu_end - _ per_cpu_start/ SMP_CACHE_BYTES);

33 6 #ifdef CONFIG_MODULES

337 if (size< PERCPU_ENOUGH_ROOM)

338 size= PERCPU_ENOUGH_ROOM;
33 9 #endif

341 ptr = alloc_bootmem(size * NR_CPUS);

343 for (i = 0; i < NR_CPUS; i++, ptr += size) {

344 ___ per__cpu_of fset [i] = ptr - ___ per_cpu_start;

345 memcpy(ptr/ __ per_cpu_start/ _ per_cpu_end - _ per_cpu_start);

346 }

347 }

Строки 329-332

Инициализируются переменные для управления последовательными блоками памя­ти. Переменные «магического связывания» определяются во время связывания в со­ответствующих каталогах архитектуры ядра (например, arch/i386/kernel/ vbmlinux. Ids. S).

Строки 334-341

Мы определяем размер памяти, требуемой одному процессору, и выделяем эту па­мять для каждого процессора системы в виде единого последовательного блока па­мяти.


8.5 Начало: start_kernel()



Строки 343-346

Мы проходим по всей новой выделенной памяти и инициализируем блок памяти для каждого процессора. Концептуально мы берем блок данных, предназначенный для

отдельного процессора (_ per_spu_start в_______ per_cpu_end), и копируем его

для каждого процессора системы. Таким способом каждый процессор получает дан­ные, с которыми он может работать.

8.5.6 Вызов smp_prepare_boot_cpu()

Строка 415

Аналогично smp_per_cpu_areas (), smp_per_boot_cpu () заменяется в случае, если ядро Linux не поддерживает SMP:

include/1inux/smp.h

106 #define smp_prepare_boot_cpu() do {} while (0)

Тем не менее, если ядро Linux откомпилировано без поддержки SMP, нам нужно по­зволить загрузочному процессору получить доступ к драйверу консоли и хранилищу для процессора, которое мы только что инициализировали. Это выполняется с помощью би­товой маски для процессора.

Битовая маска процессора определяется следующим образом:

include/asm-generic/cpumask.h

10 #if NR_CPUS > BITS_PER_LONG & & NR_CPUS! = 1

11 tdefine CPU_ARRAY_SIZE BITS_TO_LONGS(NR_CPUS) 12

 

13 struct cpumask

14 {

15 unsigned long mask[CPU_ARRAY_SIZE];

16 };

Это означает, что у нас есть платформонезависимая битовая маска, содержащая та­кое же количество битов, как и количество процессоров в системе.

smp_prepare_boot_cpu() реализована как архитектурно-зависимый блок ядра Linux, однако, как мы вскоре увидим, для х86 и РРС она совпадает:

arch/i38 б/kernel/smpboot.с

66 /* карта для включенных процессоров */

67 cpumask_t cpu_online_map;



Глава 8 • Загрузка ядра


70 cpumask_t cpu_callout_map;

1341 void _ devinit smp_prepare_boot_cpu(void)

1342 {

 

1343 cpu_set(smp_processor__id(), cpu_online_map);

1344 cpu_set (smp__processor_id(), cpu_callout_map);

1345 }

arch/ppc/kernel/smp.с

49 cpumask_t cpu_online_map;

50 cpumask_t cpu_possible_map;

 

331 void _ devinit smp__prepare_boot_cpu(void)

332 {

 

333 cpu_set(smp_processor__id(), cpu_online_map);

334 cpu_set(smp_processor_id(), cpu_possible_map);

335 }

Для обеих функций cpu_set () просто устанавливает бит smp_proces sor_id в битовой карте cpumask_t. Установка бита предполагает, что значение установленного бита равно 1.

Вызов schedjnit()

Строка 422

Вызов sched_init () помечает инициализацию всех объектов, с которыми рабо­тает планировщик, для назначения процессорного времени системным процессам. Имейте в виду, что в этой точке существует только один процесс, а именно процесс init, выполняемый shed_init ().

kernel/sched.с

3896 void _ init sched_init(void)

3897 {

3898 runqueue_t * rq;
3 899 int i, j, k;
3900

3919 for (i = 0; i < NR_CPUS; i++) {

3 92 0 prio_array_t *array;

3 922 rq = cpu_rq(i);

3923 spin_lock_init(& rq-> lock);


8.5 Начало: start_kemel()



3 924 rq-> active = rq-> arrays;

3 925 rq-> expired = rq-> arrays + 1;

3 92 6 rq-> best_expired_prio = MAX_PRIO;

3938 for (j = 0; j < 2; j++) {

3 93 9 array = rq-> arrays + j;

3940 for (k = 0; к < MAX_PRIO; k++) {

3941 INIT_LIST_HEAD(array-> queue + k);

3942 ____ clear_bit(k, array-> bitmap);

3943 }

3944 // разделитель для битового поиска

3945 __ setjDit(MAX_PRIO, array-> bitmap);

3946 }

3947 }

3948 /*

3 949 * Мы применяем небольшую магию для получения первого

3 950 * потока в SMP-режиме.

3951 */

3952 rq = this_rq();

3 953 rq-> curr = current;

3 954 rq-> idle = current;

3955 set_task_cpu(current, smp_processor_id());

3 956 wake__up_f orked_process (current);

3958 /*

3 959 * Загрузка ожидающего потока, выполняющего ленивое переключение

MMU:

3960 */

3961 atomic_inc(& init_mm.iran_count);

3962 enter_lazy_tlb(& init_mm, current);

3963 }

Строки 3919-3926

Инициализируется очередь выполнения для каждого из процессоров: активная очередь, очередь истекших и циклическая блокировка инициализируются именно в этом блоке. Вспомните из гл. 7, что spin_lock_init () устанавливают цик­лическую блокировку в 1, что означает, что данные объекта разблокированы.

Рис. 8.13 иллюстрирует инициализированную очередь выполнения.

Строки 3938-3947

Для каждого возможного приоритета мы инициализируем связанный с ним список и очищаем все биты для обозначения того, что в очереди выполнения нет процессов. (Если вы во всем этом запутались, обратитесь к рис. 8.14. Также вернитесь к гл. 7,



Глава 8 • Загрузка ядра


runqueuej

блокировка 1

массивы

prio_array_t—«rio_array_t

best_expired_prio MAX_PRIQ

Рис. 8.13. Инициализированная очередь выполнения rq

где приведен обзор того, как планировщик управляет очередями выполнения.) Этот блок кода просто проверяет, что все готово для первого процесса. В строке 3947 пла­нировщик знает, что процессы не существуют; он игнорирует текущий и ожи­дающий процессы.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

          /      
  runqueuej массивы pfo_array_1 pfo_array_1  
rq—> блокировка 1 ( |r r_active г r_active    
   
к арта битов к арта битов    
  Fbitmap_size -{   j-BITMAP_SIZE H    
    активные  
    истекшие  
массивы  
  Jprio_array_t з ► pno_array_i чередь " 1 к чередь    
  best_expired prio MAX_PRI0   bMAX.PRlO Ч   hMAX_PRI0 Н    
   
       
             
   
               

Рис. 8.14. Инициализированная очередь выполнения rq

Строки 3952-3956

Мы добавляем текущий процесс в очередь выполнения текущего процессора и вы­зываем wake_up_f orked_process () для самого текущего процесса с целью его


8.5 Начало: start_kernel()



инициализации в планировщике. Теперь планировщик знает, что у нас есть как ми­нимум один процесс - init.

Строки 3961-3962

Когда переключатель ленивого MMU включен, многопроцессорная система Linux может выполнять переключение контекстов быстрее. TLB является буфером предпросмотра транзакций, содержащим последние преобразования адресов страниц. Обработка TLB занимает много времени, так что, если это возможно, мы ее заменяем; enter_lazy__tlb() проверяет, что структура inin_struct_init_mm не используется для нескольких процессоров и может быть лениво переключена. На однопроцессорной системе эта функция приравнивается NULL.

8.5.8 Вызов build_all_zonelists()

Строка 424

Функция build_all_zonelists () разделяет память в зависимости от типа зоны ZONE_DMA, ZONE_NORMAL и ZONE_HIGHMEM. Как упоминалось в гл. 6, «Файловые системы», зоны - это линейное разделение физической памяти, исполь­зуемое в основном для адресации аппаратных ограничений. Стоит заметить, что все зоны строятся именно в этой функции. После того как зоны построены, страницы сохраняются во фреймах страниц, расположенных в зонах.

Вызов build_all_zonelists () представляет numnodes и NODE_DATA. Гло­бальная переменная numnodes хранит количество узлов (или разделов) физической па­мяти.

Разделы определяются в зависимости от скорости доступа для процессора. Обратите внимание, что в этой точке таблица страниц полностью настроена.

mm/page_alloc.с

1345 void ____ init build_all_zonelists(void)

1346 {

1347 int i;
1348

1349 for(i = 0; i < numnodes; i++) 13 50 build_zonelists(NODE_DATA(i));

1351 printk(" Built %i zonelists\n", numnodes);

1352 }

build_all_zonelists () вызывает build_zonelists() для каждого узла и заканчивает распечаткой количества созданных зон. Эта книга не рассматривает подробностей, связанных с узлами. Стоит заметить, что для примера с одним процессором



Глава 8 • Загрузка ядра


numnodes равно 1 и каждый узел содержит зоны всех трех типов. Макрос NODE_DATA возвращает описатель узла из списка описателей узлов.

8.5.9 Вызов page_allocJnit

Строка 425

Функция page_alloc_init () просто регистрирует функцию в цепи уведомле­ний1. Зарегистрированная функция page_alloc_cpu_notify () является функ­цией утечки страниц2, связанной с динамической настройкой процессора.

Динамическая настройка процессора связана с добавлением и удалением процес­соров во время работы системы Linux при наступлении события «горячее подключение» процессора; несмотря на то что технически процессоры не вставляются и не извлекаются физически во время работы машины, в некоторых системах они могут включаться и вы­ключаться, как в IBM p-Series 690. Давайте рассмотрим эту функцию.

mm/page_alloc.с

1787 tifdef CONFIG_HOTPLUG_CPU

1788 static int page_alloc_cpu_notify(struct notifier_block *self,

1789 unsigned long action, void *hcpu)

1790 {

1791 int cpu = (unsigned long)hcpu;
17 92 long *count;

if (action == CPU_DEAD) {

1796 count = & per_cpu(nr__pagecache_JLocal, cpu);

1797 atomic__add(*count, & nr_pagecache);

1798 *count = 0;

1799 local_irq_disable();

1800 __ drain__pages (cpu);

1801 local_irq_enable();

1802 }

1803 return NOTIFY_OK;

 

1804 }

1805 #endif /* CONFIG_HOTPLUG_CPU */ 1806

 

1807 void _ init page_alloc_init(void)

1808 {

1809 hotcpu__notifier(page_alloc_cpu_notify, 0);

1810 }

L Цепь уведомлений обсуждается в гл. 2.

2. утечка страниц связана с удалением больше не используемых процессором страниц.


8.5 Начало: start_kernel()



Строка 1809

В этой строке регистрируется функция page_alloc_cpu_notify () в цепи уве­домлений hotcpu_notifier. Функция page_alloc_cpu_notify () создает notif ier_block, указывающую на функцию page_alloc_cpu_notify (), и затем регистрирует объект в цепи уведомления с приоритетом 0 (kernel/cpu. с).

Строка 1788

page_alloc__cpu_notify () имеет параметры, связанные с вызовом уведомите­ля, как описано в гл. 2. Системно-зависимый указатель указывает на целое, оз­начающее номер процессора.

Строки 1794-1802

Если процессор мертв, его страницы освобождаются. При выключении процессора переменная действия устанавливается в CPU_DEAD. (См. drain_pages () в том же файле.)

8.5.10 Вызов parse_args()

Строка 427

Функция parse_args () выполняет парсинг аргументов, передаваемых в ядро Linux.

Например, nfsroot- это параметр


Поделиться:



Популярное:

Последнее изменение этой страницы: 2016-03-25; Просмотров: 754; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.34 с.)
Главная | Случайная страница | Обратная связь