Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Системные часы: прошедшее время и таймеры
2 98 case RTC_UIE_OFF: /* отключение прерываний от обновлений RTC. */ 3 02 case RTC_UIE_ON: /* включение прерываний от обновлений RTC. */ 3 05 case RTC_RD_TIME: /* Чтение времени/даты из RTC */ 307 memset (& wtime, 0, sizeof (wtime) ); 308 get_rtc_time(& wtime); 309 310 return copy_to_user ( (void *) arg, & wtime, sizeof (wtime) )? -EFAULT: 0; 312 case RTC_SET_TIME: /* Set the RTC */ 313 return -EINVAL; 314 } 353 static int gen_rtc_open(struct inode *inode, struct file *file) 354 { 3 55 if (gen_rtc_status & RTC_IS_OPEN) 3 56 return -EBUSY; 3 57 gen_rtc_status |= RTC_IS_OPEN; Этот код реализует тот же набор команд ioctl. Так как мы выполняем вызов ioctl из проверочной программы пользовательского пространства с флагом RTC_RD_TIME, управление передается на строку 305. Следующий вызов в строке 308 - это get_rtc_time(& wtime) из rtc.h (см. соответствующий код). Перед тем как оставить этот блок кода, обратите внимание на строку 353. Она позволяет получать доступ к драйверу только для одного пользователя зараз с помощью open (), устанавливающей состояние драйвера в RTC_IS_OPEN: include/asm-ppc/rtc.h 45 static inline unsigned int get_rtc_time(struct rtc_time *time) 46 { 047 if (ppc_md.get_rtc_time) { 048 unsigned long nowtime; 050 nowtime = (ppc_md.get_rtc_time)(); 052 to_tm(nowtime, time); 054 time-> tm_year -= 1900; 055 time-> tm_mon -= 1; /* Make sure userland has a 0-based month */ 056 } Глава 7 • Планировщик и синхронизация ядра 057 return RTC_24H; 058 } Встроенная функция get_rtc_time () вызывает функцию, устанавливающую переменную-указатель в значение ppc_md. get_rtc_time на строке 50. Ранее при инициализации ядра эта переменная устанавливалась в chrp_setup. с: arch/ppc/platforms/chrp_setup.c 447 chrp_init(unsigned long r3, unsigned long r4, unsigned long r5, 448 unsigned long r6, unsigned long r7) 449 {
477 ppc_md.time_init = chrp_time_init; 478 ppc_md.set__rtc_time = chrp_set_rtc_time; 479 ppc_md.get_rtc_time = chrp_get_rtc_time; 480 ppc_md.calibrate_decr = chrp_calibrate_decr; Функция chrp_get_rtc_time () (в строке 479) определена в chrp__time.c в следующем блоке кода. Так как информация о времени в памяти CMOS обновляется на периодической основе, блокировка кода чтения включена в цикл for, который пересчитывает обновляемые в прогрессе блоки: arch/ppc/platforms/chrp_time.с 122 unsigned long _ chrp chrp_get__rtc_time(void) 123 {
124 unsigned int year, mon, day, hour, min, sec; 125 int uip, i; 141 for ( i = 0; i< 1000000; i++) { 142 uip = chrp_cmos_clock_read(RTC_FREQ_SELECT); 143 sec = chrp_cmos__clock_read(RTC_SECONDS); 144 min = chrp_cmos_clock__read(RTC_MINUTES); 145 hour = chrp_cmos_clock_read(RTC_HOURS); 146 day = chrp_cmos_clock_read(RTC_DAY_OF_MONTH); 147 mon = chrp_cmos_clock_read(RTC_MONTH); 148 year = chrp_cmos_clock_read(RTC_YEAR); 149 uip |= chrp_cmos_clock_read(RTC_FREQ_SELECT); 150 if ((uip & RTC_UIP)==0) break;
151 } 152 if (! (chrp_cmos_clock_read(RTC_CONTROL) 153 & RTC_DM_BINARY) || RTC_ALWAYS_BCD) 154 { 7.4 Системные часы: прошедшее время и таймеры 155 BCD_TO_BIN(sec); 156 BCD_TO_BIN(min); 157 BCD_TO_BIN(hour); 158 BCD_TO_BIN(day); 159 BCD_TO_BIN(mon); 160 BCD_TO_BIN(year); 161 } 54 int __ chrp chrp_cmos_clock_read(int addr) 55 { if (nvram_asl! = 0)
56 outb(addr»8/ nvram_asl); 57 outb(addr, nvram_as0); 58 return (inb(nvram_data)); 059 } Наконец, в chrp_get_rtc_time () значения отдельных компонентов структуры времени считываются с устройства RTC с помощью функции chrp_cmos_clock_ read. Эти значения форматируются и возвращаются в структуру rtc_tm, передаваемую в обратном вызове ioctl в пространство проверочной программы. Чтение из часов реального времени на х86 Метод чтения RTC на платформе х86 довольно похож, но немного более компактен и удобен по сравнению с методом на РРС. Еще раз мы открываем драйвер /dev/rtc, но на этот раз при сборке компилируется файл rtc. с для архитектуры х86. Далее обсуждается вариант исходного кода для х86. drivers/char/rtc.с 352 static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel) 353 { switch (cmd) { 482 case RTC_RD_TIME: /* Read the time/date from RTC */ 483 {
484 rtc_get_rtc_time (ScWtime); 485 break; 486 } 1208 void rtc_get_rtc_tinie (struct rtc_time *rtc_tm) 1209 { Глава 7 • Планировщик и синхронизация ядра 123 8 spin_lock_irq(& rtc_lock); 1239 rtc_tm-> tm_sec = CMOS_READ(RTC_SECONDS); 1240 rtc_tm-> tm_min = CMOS_READ(RTC_MINUTES); 1241 rtc_tm-> tm_hour = CMOS_READ(RTC_HOURS); 1242 rtc_tm-> tm_mday = CMOS_READ(RTC_DAY_OF_MONTH); 1243 rtc_tm-> tm_mon = CMOS_READ(RTC_MONTH); 1244 rtc_tm-> tm__year = CMOS_READ(RTC_YEAR); 1245 Ctrl = CMOS_READ(RTC_CONTROL); 1249 spin_unlock_irq(& rtc_lock); 12 51 if (! (ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) 1252 { 1253 BCD_TO_BIN(rtc_tm-> tm_sec); 1254 BCD_TO_BIN(rtc_tm-> tm_min); 1255 BCD_TO_BIN(rtc_tm-> tm_hour); 1256 BCD_TO_BIN(rtc_tm-> tm_mday); 1257 BCD_TO_BIN(rtc_tm-> tm_mon); 1258 BCD_TO_BIN(rtc_tm-> tm_year); 1259 } Программа проверки использует флаг RTC_RD_TIME в вызове ioctl () драйвера в rtc.c; ioctl переключает состояние и затем заполняет структуру из памяти CMOS для RTC. Далее приведена реализация аппаратного чтения из RTC для х86. include/asm-i38б/mcl46818rtc.h 018 #define CMOS_READ(addr) ({ \ 019 outb_p((addr), RTC_PORT(0)); \ 021 }) Резюме В этой главе описаны планировщик Linux, приоритетное прерывание обслуживания, системные часы и таймер Linux. Точнее говоря, мы обсудили следующие темы: • мы представили вашему вниманию новый планировщик Linux 2.6 и рассмотрели его особенности; • мы обсудили, как планировщик выбирает новую задачу из множества задач и как используется алгоритм работы планировщика и т. д.; Упражнения • мы обсудили переключение контекста, используемого планировщиком для переключения процессов, и проследили соответствующие функции до уровня аппаратно-специфического кода; • мы описали, как процессы в Linux могут передавать управление процессором другому процессу с помощью вызова schedule () и как ядро маркирует процессы, требующие «перепланировки»; • мы углубились в подсчет ядром Linux динамических приоритетов на основе предыдущего поведения отдельного процесса и в то, как процесс удаляется из очереди планировщика; • далее мы перешли к описанию явных приоритетных прерываний обслуживания пользовательского уровня и уровня ядра и их реализации в ядре Linux 2.6; • и наконец, мы рассмотрели таймеры, системные часы и реализацию системных часов на архитектурах х86 и РРС. Упражнения 1. Как Linux сообщает планировщику, чтобы тот периодически запускался? 2. Опишите различие между интерактивными и неинтерактивными процессами. 3. С точки зрения планировщика какими отличиями обладают процессы реального времени? 4. Что происходит, когда у процесса заканчиваются запланированные тики? 5. В чем преимущество 0(1)-планировщика? 6. Какие структуры данных использует планировщик для управления приоритетами запущенных в системе процессов? 7. Что произойдет, если вы вызовите schedule () во время циклической блокировки? 8. Как ядро решает, возможно ли выполнить неявное приоритетное прерывание обработки задачи? Глава Загрузка ядра В этой главе: ? 8.1 BIOS и Open Firmware ? 8.2 Загрузчики ? 8.3 Архитектурно-зависимая инициализация памяти ? 8.4 Диск инициализации в памяти ? 8.5 Начало: start_kernel() ? 8.6 Поток init (или процесс 1) ? Резюме ? Вопросы для самопроверки Глава 8 • Загрузка ядра
а данный момент мы рассмотрели подсистемы ядра Linux и используемые в их операциях структуры. Каждая глава подразумевала, что подсистема была настроена и запущена, а мы фокусировались на типичном управлении подсистемами ядра и обработке их операций. Тем не менее каждая из подсистем должна быть инициализирована перед использованием. Эта инициализация происходит во время загрузки ядра, после того как загрузчик завершит загрузку образа ядра в память и передаст ему управление. Мы выбрали путь следования процессу инициализации ядра в линейном порядке. Начнем мы с обсуждения того, что при этом происходит, начиная с включения и вызова первой архитектурно-зависимой функции, start_kernel (), и заканчивая вызовом процесса инициализации /sbin/init. Рис. 8.1 иллюстрирует порядок сообщений начиная с включения и заканчивая выключением.
Рис. 8.1. Старт ядра и процесс загрузки Мы начнем с обсуждения BIOS и Open Firmware, являющихся первым кодом, запускаемым на системах х86 и РРС при включении соответственно. Далее мы обсудим наиболее распространенные загрузчики, используемые в Linux, и то, как они загружают ядро и передают ему управление. После этого мы подробно обсудим шаги инициализации ядра (kernel initialization), во время которой инициализируются все подсистемы. В конце инициализация ядра происходит вызов /sbin/init как процессом 1. Программа init продолжается тем, что называется инициализацией системы (system initialization), с помощью включения процессов, необходимых перед регистрацией пользователя в системе. Вскоре станет ясно, что часть инициализации ядра состоит из вложенных инициализаций подсистем. Это затрудняет попытки проследить непрерывный процесс инициализации подсистем с начала и до конца. Тем не менее дальнейший линейный порядок загрузки ядра Linux позволяет проследить настройку подсистем ядра и по мере их появления и иллюстрирует сложность процесса загрузки. Мы коснемся многих структур, представленных в предыдущих главах, по мере их загрузки и инициализации. Мы начнем с рассмотрения первого шага: BIOS и Open Firmware. BIOS и Open Firmware При включении процессор сначала получает доступ к адресам, которые обычно находятся в доступной для чтения области памяти. Эта доступная только для чтения память обычно располагается в Flash ROM (или просто Flash). Там располагается первый код, Загрузчики который выполняется при каждом запуске системы. Этот код отвечает за включение минимума систем, необходимых для загрузки ядра. На х86-системах он полностью находится в BIOS (Basic Inpit Output System)1 -блоке аппаратно-зависимого кода инициализации системы, загружающего систему. На системах х86 загрузчик, и соответственно Linux, зависит от BIOS, приводящего систему в определенное состояние. Интерфейс BIOS составляет унифицированный набор функций, известных как npepbmaHHH(interrupts). Во время загрузки Linux использует эти прерывания для запроса доступных ресурсов системы. После того как BIOS закончит инициализацию, он копирует первые 512 байт с устройства загрузки (описываемого в следующем разделе) в адрес 0х7с00 и переходит в него. Несмотря на то что в некоторых случаях BIOS загружает операционную систему через сетевое соединение, мы будем рассматривать процесс загрузки Linux с жесткого диска. После загрузки Linux BIOS все равно находится в памяти и его функции доступны через прерывания. На PowerPC тип кода инициализации зависит от возраста соответствующей архитектуры PowerPC. Старые системы IBM используют PowerPC Reference Platform (PreP)2, тогда как более новые системы IBM используют Common Hardware Reference Platform (CHRP)3. Системы G4 и позднейшие называются «Новым миром» и используют Open Firmware(OF) в границах реализации конкретной архитектуры. (Более подробную информацию об этих процессорах и системно-зависимом загрузочном firmware, а также совместимости их форматов вам стоит получить с домашней страницы Open Firmware на www. openf irmware. org.) Загрузчики Загрузчики - это программы, находящиеся на загрузочном диске компьютера. Первым устройством загрузки обычно является первый жесткий диск системы. Загрузчик вызывается BIOS (x86) или firmware (PPC) после того, как инициализация системы обеспечит поддержку памяти, прерываний и ввода-вывода, требуемых для загрузки ядра. После загрузки ядро инициализируется и конфигурируется операционной системой. Для систем х86 BIOS позволяет пользователю установить последовательность устройств загрузки для его системы. Такими устройствами загрузки обычно являются флоппи-дисководы, CD-ROM и жесткие диски. Форматирование диска (например, с помощью f disk) создает на диске Master Boot Record (MBR)4, располагающуюся в первом секторе (сектор 0, цилиндр 0, головка 0) загрузочного диска. MBR содержит небольшую программу и таблицу разделов из четырех элементов. Конец загрузочного сектора помеча- L Базовая система ввода-вывода. Примеч. пер. 2 Эталонная платформа PowerPC. Примеч. пер. 3 Простая эталонная аппаратная платформа. Примеч. пер. 4 Главная загрузочная запись. Примеч. пер. Глава 8 • Загрузка ядра ется шестнадцатеричных значением 0хАА55 в позиции 510. Табл. 8.1 демонстрирует компоненты MBR. Таблица 8.1. Компоненты MBR
Таблица разделов MBR хранит информацию, относящуюся к каждому из главных разделов жестких дисков. Табл. 8.2 демонстрирует, как выглядит каждая 16-битовая запись в разделе MBR. Таблица 8.2. 16-битовые записи MBR Отступ Длина Назначение
Популярное:
|
Последнее изменение этой страницы: 2016-03-25; Просмотров: 778; Нарушение авторского права страницы