Miscellaneous Kernel Services

Эта глава содержит информацию о разных услугах, предоставленных ядром OS X. Для большинства проектов Вы никогда не должны будете, вероятно, использовать большинство этих служб, но если Вы сделаете, то Вам будет трудно обойтись без них.

Эта глава содержит эти разделы: Используя Абстракции Времени Ядра, Обработку Параметра загрузки, Очереди и Установку Рычагов Завершения работы.

Используя абстракции времени ядра

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

Получение информации времени

Существует много способов получить основную информацию времени из ядра. Официально утвержденные методы - те, в которых экспортирует Мах kern/clock.h. Они включают следующее:

void clock_get_uptime(uint64_t *result);
 
void clock_get_system_microtime(            uint32_t *secs,
                                            uint32_t *microsecs);
 
void clock_get_system_nanotime(             uint32_t *secs,
                                            uint32_t *nanosecs);
void clock_get_calendar_microtime(          uint32_t *secs,
                                            uint32_t *microsecs);
 
void clock_get_calendar_nanotime(           uint32_t *secs,
                                            uint32_t *nanosecs);
 

Функция clock_get_uptime возвращает значение в AbsoluteTime модули. Для получения дополнительной информации об использовании AbsoluteTime, посмотрите Используя Маха Абсолютные Функции Времени.

Функции clock_get_system_microtime и clock_get_system_nanotime возвратите 32-разрядные целые числа, содержащие секунды и микросекунды или наносекунды, соответственно, представляя системное время работы.

Функции clock_get_calendar_microtime и clock_get_calendar_nanotime возвратите 32-разрядные целые числа, содержащие секунды и микросекунды или наносекунды, соответственно, представляя текущую календарную дату и время с эпохи (1 января 1970).

В некоторых частях ядра можно счесть другие функции тем типом возврата mach_timespec_t. Этот тип подобен традиционному BSD struct timespec, за исключением того, что части секунды измеряются в наносекундах вместо микросекунд:

struct mach_timespec {
    unsigned int tv_sec;
    clock_res_t tv_nsec;
};
typedef struct mach_timespec *mach_timespec_t;

В дополнение к традиционным функциям Маха, если Вы пишете код в частях BSD ядра, можно также получить текущий календарь (настенные часы) время как BSD timeval, а также узнайте календарное время, когда система была загружена путем выполнения следующего:

#include <sys/kernel.h>
struct timeval tv=time; /* calendar time */
struct timeval tv_boot=boottime; /* calendar time when booting occurred  */

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

Событие и таймер ожидают

Каждая часть ядра OS X имеет отличный API для ожидания определенного периода времени. В большинстве случаев можно вызвать эти функции от других частей ядра. Набор I/O обеспечивает IODelay и IOSleep. Мах обеспечивает функции на основе AbsoluteTime, а также некоторые на основе микросекунд. BSD обеспечивает msleep.

Используя IODelay и IOSleep

IODelay, если Набором I/O, абстрагирует синхронизированное вращение. Если Вы задержитесь в течение короткого периода времени, и если Вам нужно гарантировать, то то Ваше ожидание не будет остановлено преждевременно поставкой асинхронных событий, это - вероятно, лучший выбор. Если необходимо задержаться в течение нескольких секунд, однако, это - плохой выбор, потому что CPU, выполняющий ожидание, будет вращаться, пока время не протекло, неспособное обработать любую другую обработку.

IOSleep помещает в настоящее время выполняющийся поток для сна на определенный период времени. Нет никакой гарантии, что Ваш поток выполнится после того промежутка времени, и при этом нет гарантии, что Ваш поток не будет пробужден некоторым другим событием, прежде чем истекло время. Это примерно эквивалентно sleep вызовите от пространства пользователя в этом отношении.

Использование IODelay и IOSleep являются прямыми. Их прототипы:

IODelay(unsigned microseconds);
IOSleep(unsigned milliseconds);

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

Используя Маха абсолютные функции времени

Следующие функции времени Маха обычно используются. Несколько других описаны в osfmk/kern/clock.h.

void delay(uint64_t microseconds);
void clock_delay_until(uint64_t deadline);
void clock_absolutetime_interval_to_deadline(uint64_t abstime,
            uint64_t *result);
void nanoseconds_to_absolutetime(uint64_t nanoseconds, uint64_t  *result);
void absolutetime_to_nanoseconds(uint64_t abstime, uint64_t *result);

Эти функции являются обычно прямыми. Однако несколько точек заслуживают объяснения. Если в частности не утверждено, все случаи, сроки, и т.д., измеряются в abstime модули. abstime модуль равен длине одного цикла шины, таким образом, продолжительность зависит от скорости шины компьютера. Поэтому Мах обеспечивает подпрограммы преобразования между abstime модули и наносекунды.

Функции многого времени, однако, обеспечивают время в секундах с остатком наносекунды. В этом случае некоторое преобразование необходимо. Например, для получения текущего времени как Мах abstime значение Вы могли бы сделать следующее:

uint32_t secpart;
uint32_t nsecpart;
uint64_t nsec, abstime;
 
clock_get_calendar_nanotime(&secpart, &nsecpart);
nsec = nsecpart + (1000000000ULL * secpart); //convert seconds to  nanoseconds.
nanoseconds_to_absolutetime(nsec, &abstime);

Значение abstime теперь сохранено в переменной abstime.

Используя msleep

В дополнение к Маху и подпрограммам Набора I/O, обеспечивает BSD msleep, который является рекомендуемым способом задержаться в частях BSD ядра. В других частях ядра необходимо или использовать wait_queue функции или использование assert_wait и thread_wakeup функции, обе из которых близко связываются к планировщику Маха и описаны в Потоке ядра APIs. Поскольку эта функция более обычно используется для ожидания на событиях, это описано далее в Условных переменных.

Обработка зависимостей от версии

Много связанных со временем функций такой как clock_get_uptime измененный в результате перехода к KPIs в OS X v.10.4. В то время как эти изменения приводят к более чистому интерфейсу, это может оказаться сложным, если необходимо сделать расширение ядра, которое должно получить информацию времени через многократные версии OS X в расширении ядра, которое иначе не имело бы никаких зависимостей от версии (таких как Набор I/O KEXT).

Вот список связанных со временем функций, которые доступны и в pre-KPI и в версиях KPI OS X:

uint64_t mach_absolute_time(void);

Объявленный в: <mach/mach_time.h>

Зависимость: com.apple.kernel.mach

Эта функция возвращает Маха, из которого абсолютная временная стоимость для текущей стены показывает время в модулях uint64_t.

void microtime(struct timeval *tv);

Объявленный в: <sys/time.h>

Зависимость: com.apple.kernel.bsd

Эта функция возвращается, timeval структура, содержащая текущую стену, показывают время.

void microuptime(struct timeval *tv);

Объявленный в: <sys/time.h>

Зависимость: com.apple.kernel.bsd

Эта функция возвращает timeval структуру, содержащую текущее время работы.

void nanotime(struct timespec *ts);

Объявленный в: <sys/time.h>

Зависимость: com.apple.kernel.bsd

Эта функция возвращается, timespec структура, содержащая текущую стену, показывают время.

void nanouptime(struct timespec *ts);

Объявленный в: <sys/time.h>

Зависимость: com.apple.kernel.bsd

Эта функция возвращает timespec структуру, содержащую текущее время работы.

В дополнение к этому APIs отмечена функциональность __APPLE_API_UNSTABLE в <mach/time_value.h> был принят как есть в OS X v.10.4 и больше не отмечается нестабильный.

Обработка параметра загрузки

OS X обеспечивает простую подпрограмму синтаксического анализа, PE_parse_boot_arg, для основной передачи загрузочного аргумента. Это поддерживает оба флага и присвоение численного значения. Для получения значений Вы пишете код, подобный следующему:

unsigned int argval;
 
if (PE_parse_boot_arg("argflag", &argval)) {
    /* check for reasonable value */
    if (argval < 10 || argval > 37)
        argval = 37;
} else {
    /* use default value */
    argval = 37;
}

С тех пор PE_parse_boot_arg возвращает ненулевое значение, если флаг существует, можно проверить на присутствие флага при помощи флага, запускающегося с тире (-) и игнорирование значения, сохраненного в argvalue.

PE_parse_boot_arg функция может также использоваться для получения аргумента строки. Чтобы сделать это, необходимо передать в адресе массива типа char как второй параметр. Поведение PE_parse_boot_arg если строка передается в для числовой переменной или наоборот, не определено. Если строка превышает выделенное пространство памяти, его поведение также не определено. Обязательно предоставьте достаточно пространства для самой большой разумной строки включая нулевой разделитель. Никакая попытка не предпринята проверки границ, так как переполнение обычно является фатальной ошибкой и должно обоснованно предотвратить начальную загрузку.

Очереди

Как часть его инфраструктуры BSD, ядро OS X обеспечивает много основных макросов поддержки для упрощения обработки связанных списков и очередей. Они реализованы как C макросы и принимают стандарт C struct. Также, им, вероятно, не удовлетворяют для записи кода в C++.

Основные типы списков и очередей включали,

SLIST идеально для создания штабелей или для обработки больших наборов данных с немногими или никакими удалениями. Произвольное удаление, однако, требует O(n) обход списка.

STAILQ подобно SLIST за исключением того, что это поддерживает указатели на оба конца очереди. Это делает его идеальным для простых очередей FIFO путем добавления записей в хвосте и выбирающих записей от главы. Как SLIST, это неэффективно для удаления произвольных элементов.

LIST вдвойне соединенная версия SLIST. Дополнительные указатели требуют дополнительного пространства, но позволяют O(1) (постоянное время) удаление произвольных элементов и двунаправленного обхода.

TAILQ вдвойне соединенная версия STAILQ. Как LIST, дополнительные указатели требуют дополнительного пространства, но позволяют O(1) (постоянное время) удаление произвольных элементов и двунаправленного обхода.

Поскольку их функциональность относительно проста, их использование является одинаково прямым. Они макросы могут быть найдены в xnu/bsd/sys/queue.h.

Установка рычагов завершения работы

Несмотря на то, что OS X не имеет традиционных рычагов завершения работы стиля BSD, Набор I/O обеспечивает эквивалентную функциональность в последних версиях. Так как Набор I/O обеспечивает эту функциональность, необходимо вызвать его от кода C++.

Для регистрации для уведомления Вы вызываете registerSleepWakeInterest (описанный в IOKit/RootDomain.h) и регистр для уведомления сна. Если система собирается, закрываются, Ваш обработчик вызывают с типом сообщения kIOMessageSystemWillPowerOff. Если система собирается перезагрузка, Ваш обработчик получает тип сообщения kIOMessageSystemWillRestart. Если система собирается перезагрузка, Ваш обработчик получает тип сообщения kIOMessageSystemWillSleep.

Если Вы больше не должны получать уведомление (например, если Ваш KEXT разгружен), несомненно выпустят notifier с IONofitier::release для предотвращения ядра паникуют на завершении работы.

Например, когда уведомление сна происходит, следующие демонстрационные регистры KEXT для уведомлений сна, затем регистрирует сообщение с IOLog:

#include <IOKit/IOLib.h>
#include <IOKit/pwr_mgt/RootDomain.h>
#include <IOKit/pwr_mgt/IOPM.h>
#include <IOKit/IOService.h>
#include <IOKit/IONotifier.h>
 
#define ALLOW_SLEEP 1
 
IONotifier *notifier;
 
extern "C" {
 
IOReturn mySleepHandler( void * target, void * refCon,
    UInt32 messageType, IOService * provider,
    void * messageArgument, vm_size_t argSize )
{
    IOLog("Got sleep/wake notice.  Message type was %d\n", messageType);
#if ALLOW_SLEEP
    acknowledgeSleepWakeNotification(refCon);
#else
    vetoSleepWakeNotification(refCon);
#endif
    return 0;
}
 
kern_return_t sleepkext_start (kmod_info_t * ki, void * d) {
        void *myself = NULL; // Would pass the self pointer here if in a class instance
 
        notifier = registerPrioritySleepWakeInterest(
                &mySleepHandler, myself, NULL);
    return KERN_SUCCESS;
}
 
 
kern_return_t sleepkext_stop (kmod_info_t * ki, void * d) {
    notifier->remove();
    return KERN_SUCCESS;
}
 
} // extern "C"