Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Похожесть кода 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 • Загрузка ядра
Рис. 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); 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); 477 signals_init(); 478 /* содержимое rootfs нужно перезаписать обратно */ 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; 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()
Рис. 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; 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 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; 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(); 1129 copy_edd(); 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; 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(); 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 *); 1163 setup_early_printk(s); 1164 printk(" early console enabled\n" ); 1165 } 1166 } 1167 #endif 1170 dmi_scan_machine(); 1172 #ifdef CONFIG_X86_GENERICARCH 1173 generic_apic_probe(*cmdline_p); 1174 #endif 1175 if (efi_enabled) 1176 e f i_map_memmap(); 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 1188 register_memory(max_low_pfn); 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/setup, с.) Строка 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; 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; 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; 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 массивы
best_expired_prio MAX_PRIQ Рис. 8.13. Инициализированная очередь выполнения rq где приведен обзор того, как планировщик управляет очередями выполнения.) Этот блок кода просто проверяет, что все готово для первого процесса. В строке 3947 планировщик знает, что процессы не существуют; он игнорирует текущий и ожидающий процессы.
Рис. 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; 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; 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; Просмотров: 802; Нарушение авторского права страницы