Ошибки из-за неправильного обращения
Все задачи программирования переносят потенциал для ошибки. Кроме стандартных погрешностей синтаксиса или логики, однако, приложения, что аппаратные средства доступа могут встретиться с определенными типами ошибок, которые являются вне опыта многих разработчиков приложений.
В этой главе рассматривается некоторых ошибок, которые Вы могли бы видеть при разработке приложения, использующего Набор I/O для доступа к аппаратным средствам в системе OS X. Это описывает, как «считать» коды ошибки Набора I/O и затем предоставляет некоторую информацию об ошибке эксклюзивного доступа.
Интерпретация Ошибочных Возвращаемых значений Набора I/O
Поскольку Вы будете использовать функции Набора I/O экстенсивно при поиске устройств в Реестре I/O и исследовании объектов, представляющих их, необходимо быть знакомы со структурой ошибочных возвращаемых значений Набора I/O. В этом разделе описывается Ваше приложение может интерпретировать ошибочные значения, возвращенные функциями Набора I/O.
Набор I/O использует ошибочный механизм возврата, определенный платформой ядра, в которой 32-разрядное, возвращаемое значение без знака предоставляет информацию в трех отдельных битовых полях, как показано на рисунке 5-1. Это:
Высокие 6 битов указывают систему, в которой произошла ошибка.
Следующие 12 битов указывают подсистему.
Заключительные 14 битов указывают сам код ошибки.
Работать с этими ошибочными возвращаемыми значениями, заголовочным файлом IOReturn.h
определяет следующий тип:
typedef kern_return_t IOReturn; //kern_return_t is an int |
Можно использовать kern_return_t
и IOReturn
получить возвращаемые значения из функций Набора I/O то использование любой тип.
Перечисление 5-1 показывает некоторые ошибочные значения общей системы, определяющиеся в заголовке Kernel.framework/Headers/mach/error.h
.
Перечисление 5-1 значения системной ошибки Error.h
#define err_kern err_system(0x0) /* kernel */ |
#define err_us err_system(0x1) /* user space library */ |
#define err_server err_system(0x2) /* user space servers */ |
#define err_ipc err_system(0x3) /* old ipc errors */ |
#define err_mach_ipc err_system(0x4) /* mach-ipc errors */ |
#define err_dipc err_system(0x7) /* distributed ipc */ |
#define err_local err_system(0x3e) /* user defined errors */ |
#define err_ipc_compat err_system(0x3f) /* (compatibility) mach-ipc errors */ |
#define err_max_system 0x3f |
Этот заголовок также определяет макросы для определения системы и ошибочных значений подсистемы и для извлечения системы, подсистемы и значений кодов от ошибочного возвращаемого значения, как показано в Перечислении 5-2.
Перечисление 5-2 макросы Error.h для работы с ошибочными возвращаемыми значениями
#define err_system(x) (((x)&0x3f)<<26) |
#define err_sub(x) (((x)&0xfff)<<14) |
#define err_get_system(err) (((err)>>26)&0x3f) |
#define err_get_sub(err) (((err)>>14)&0xfff) |
#define err_get_code(err) ((err)&0x3fff) |
Дополнительные системные значения, а также подсистема и значения кодов, определяются в заголовочных файлах для определенных систем. Например, заголовочный файл Kernel.framework/Headers/IOKit/IOReturn.h
определяет значения, показанные в Перечислении 5-3 для Набора I/O.
Перечисление 5-3 ошибочные возвращаемые значения IOReturn.h
#ifndef sys_iokit |
#define sys_iokit err_system(0x38) |
#endif /* sys_iokit */ |
#define sub_iokit_common err_sub(0) |
#define sub_iokit_usb err_sub(1) |
#define sub_iokit_firewire err_sub(2) |
#define sub_iokit_reserved err_sub(-1) |
#define iokit_common_err(return) (sys_iokit|sub_iokit_common|return) |
#define kIOReturnSuccess KERN_SUCCESS // OK |
#define kIOReturnError iokit_common_err(0x2bc) // general error |
#define kIOReturnNoMemory iokit_common_err(0x2bd) // can't allocate memory |
#define kIOReturnNoResources iokit_common_err(0x2be) // resource shortage |
#define kIOReturnIPCError iokit_common_err(0x2bf) // error during IPC |
#define kIOReturnNoDevice iokit_common_err(0x2c0) // no such device |
// ... (many more individual error codes) |
Ваше приложение может быть в состоянии использовать эти ошибочные значения непосредственно, не имея необходимость извлекать систему, подсистему или значения кодов. Например, Вы могли использовать код как следующий для проверки ни на какую ошибку устройства:
IOReturn returnVal; |
returnVal = IOKitSomeFunction(...); |
if (returnVal == kIOReturnNoDevice) |
{ |
// "No device returned" error in I/O Kit system, common subsystem. |
} |
Однако в некоторых случаях можно хотеть изолировать значение кода ошибки или определить, из какой системы или подсистемы ошибочное значение прибыло. Из определений, показанных в Перечислении 5-3, и немного вычисления, Вы видите что ошибочное возвращаемое значение ни для какой ошибки устройства (kIOReturnNoDevice
) в системе Набора I/O и Наборе I/O общая подсистема имела бы следующие значения битового поля:
Высокие 6 битов (31–26) имеют значение
11 1000
, или0x38
.Следующие 12 битов (25–14) имеют значение
00 0000 0000 00
, или0x0
.Заключительные 14 битов (13–0) имеют значение
00 0010 1010 0000
, или0x2c0
.
Полностью собранное разрядное представление 1110 0000 0000 0000 0000 0010 1011 1100
, получающийся в шестнадцатеричном значении 0xe00002c0
. Для извлечения системы, подсистемы или значения кода от такого ошибочного возвращаемого значения, Вы используете макросы, показанные в Перечислении 5-2 вместе с константами, определенными в Перечислении 5-3. Например:
IOReturn returnVal; |
returnVal = IOKitSomeFunction(...); |
if (err_get_system(returnVal) == err_get_system(sys_iokit)) |
{ |
// The error was in the I/O Kit system |
UInt32 codeValue = err_get_code(returnVal); |
// Can now perform test on error code, display it to user, or whatever. |
} |
Обработка ошибок эксклюзивного доступа
Много типов устройств разработаны, чтобы позволить только одному процессу за один раз получать доступ к устройству. Сканер, например, поддерживает доступ только одним приложением за один раз. Соответственно, семьи I/O Kit осуществляют политику доступа для своих устройств, или монопольный или совместно используемый. Кроме того, Классика (Mac OS 9 сред, которые можно выполнить в OS X) может ожидать, что его драйверы будут иметь эксклюзивный доступ к некоторым устройствам, таким как USB-устройства. В ходе разработки приложения, что аппаратные средства доступов, можно получить ошибку эксклюзивного доступа, даже когда Вы верите, Ваш - единственный процесс, пытающийся открыть устройство. Когда это происходит, если нет никакого высокоуровневого арбитража, который можно нанять, Вы, возможно, должны представить диалоговое окно пользователю и или попытаться получить доступ к другому устройству того же класса или попытаться получить доступ к тому же устройству позже или при различных обстоятельствах.
Определенный в IOReturn.h
(в платформе Набора I/O), kIOReturnExclusiveAccess
ошибка говорит Вашему приложению, что устройство, к которому это пытается получить доступ, было уже открыто другим объектом. В большинстве случаев пользовательский клиент осуществляет эксклюзивный доступ в open
метод. Когда приложение использует интерфейс устройства open
функция, интерфейс устройства выходит open
команда пользовательскому клиенту. Пользовательский клиент отвечает путем попытки открыть его провайдера (кусок устройства) и, если он перестал работать, он возвращается kIOReturnExclusiveAccess
ошибка. (Если пользовательский клиент найдет, что провайдер завершается, то он, вероятно, возвратится kIOReturnNotAttached
ошибка.)
Различные семейства устройства Набора I/O обрабатывают проблему эксклюзивного доступа по-разному. Семья FireWire, например, осуществляет эксклюзивный доступ к своим устройствам, но также и позволяет многократным интерфейсам устройства открывать различные объекты в штабеле драйвера FireWire. Это делает это путем использования понятия ссылки сеанса для обращения к существующим соединениям ядра пространства пользователя. Рассмотрите заявление, использующее интерфейс устройства семьи FireWire для открытия модуля AV/C на устройстве FireWire. Если то приложение также хочет получить доступ к объекту модуля FireWire, поддерживающему модуль AV/C, это может получить ссылку сеанса от интерфейса устройства AV/C и использовать его для получения интерфейса устройства модуля FireWire. Для получения дополнительной информации об этом процессе посмотрите Руководство по Интерфейсу Устройства FireWire.
Семья Model архитектуры SCSI, с другой стороны, не позволяет многократным интерфейсам устройства одновременно открывать различные объекты в штабеле драйвера, но это действительно позволяет приложениям получать информацию об устройствах, в драйверах ядра в настоящее время имеющих открытый. Это также позволяет приложению получать эксклюзивный доступ к авторской разработке или осваивающему носители устройству, разъединяя верхние уровни штабеля и требуя, чтобы драйвер логической единицы в ядре привел к управлению приложению. Для получения дополнительной информации об этом процессе, посмотрите Руководство по Интерфейсу Устройства модели архитектуры SCSI.
Для последовательных устройств и устройств хранения можно получить доступ через файлы устройств, приложение может не открыть устройство по следующим причинам: