Работа с файлами устройств для устройств хранения
В этой главе описываются, как разработать приложение, использующее Набор I/O и функции POSIX, чтобы определить местоположение устройства хранения CDROM на OS X и открыть ее для чтения.
Фрагменты кода в этой главе основываются на примере приложения CDROMSample, доступный полностью в Примере кода> Аппаратные средства и Драйверы> Хранение.
Несмотря на то, что пример кода в этой главе был скомпилирован и протестирован до некоторой степени, Apple не рекомендует непосредственно включить этот код в коммерческое применение. Например, только ограниченная обработка ошибок показана — необходимо разработать собственные методы для обнаружения и ошибок из-за неправильного обращения.
Доступ устройства хранения в основанном на Intel Macintosh
Этот раздел кратко обрисовывает в общих чертах некоторые проблемы, связанные с разработкой универсальной версии двоичных файлов приложения Mac, использующего файлы устройств для доступа к устройству хранения. Перед чтением этого раздела, убедиться считать Универсальные Двоичные Инструкции по Программированию. Тот документ касается архитектурных различий, и порядок байтов форматирует и обеспечивает всесторонние инструкции для модификации кода и создания универсальных двоичных файлов. Инструкции в том документе применяются ко всем типам приложений, включая тех который аппаратные средства доступа.
Прежде чем Вы создадите свое приложение как универсальный двоичный файл, удостоверьтесь что:
Вы портируете свой проект на GCC 4 (XCode использует GCC 4 для предназначения для основанных на Intel компьютеров Macintosh),
Вы устанавливаете OS X v10.4 универсальный SDK
Вы разрабатываете свой проект в Xcode 2.1 или позже
Приложение, читающее из и пишущее в носители часто, обрабатывает структуры данных, содержащие многобайтовые целочисленные данные. Жизненно важно, чтобы эти структуры данных остались в корректном формате порядка байтов на диске, таким образом, диск может использоваться и с основанными на PowerPC и с основанными на Intel компьютерами Macintosh. В зависимости от собственного формата порядка байтов компьютера, в котором приложение работает, поэтому, приложение, возможно, должно к байту подкачать структуры данных, которые это обрабатывает.
Если Вы решили, что свопинг байта требуется в Вашем приложении, можно реализовать его одним из двух способов:
Выполните надлежащую подкачку байта на данных после того, как это читается в буфер, и выполните противоположную подкачку байта на данных, которые это готово быть выписанным к диску. Эта схема позволяет Вашему приложению получать доступ к буферам, не имея необходимость волноваться о формате порядка байтов данных в них.
Не делайте байт подкачивает данные в буферах, но выполняет надлежащую подкачку байта каждый раз Ваши доступы к приложениям буферы. Это сохраняет корректный формат порядка байтов данных, в то время как он находится в буферах, что означает, что Ваше приложение не имеет к подкачке байта данных при чтении его в или выписывания его.
Для предотвращения беспорядка, лучше выбрать только одну из этих двух схем и быть непротиворечивым в его реализации всюду по приложению. Какой бы ни Вы выбираете, однако, убедиться использовать условные подкачивающие байт макросы, определенные в libkern/OSByteOrder.h
(даже при том, что этот заголовочный файл находится в платформе Ядра, ее макросы доступны приложениям). При использовании их макросы компилятор оптимизирует код, таким образом, подпрограммы выполняются, только если они необходимы для архитектуры, в которой работает приложение.
Доступ к устройству хранения CDROM
Для передачи с устройством хранения (таким как устройство CDROM) из приложения Mac Вы используете функции Набора I/O, чтобы найти устройство и получить путь к его файлу устройств. Можно тогда использовать функции POSIX для выполнения таких операций как открытие и закрытие устройства и чтение от него.
Пример кода в этой главе демонстрирует, как найти все выбрасываемые носители CD, получить путь к файлу устройств для дисковода для компакт-дисков и использовать функции POSIX для открытия устройства, считайте сектор носителей и закройте устройство. Ваше приложение может считать данные с помощью функций POSIX, потому что, в зависимости от полномочий, файловая система может позволить многочисленным пользователям открывать файл для чтения. Однако Вы не должны предполагать, что можно использовать этот механизм для записи данных, потому что сама файловая система, возможно, открыла все перезаписываемые смонтированные устройства хранения со строгим доступом для записи.
Пример кода, показанный в этой главе, является от XCode “проектом” Инструмента CoreFoundation. Проект создает инструмент, не имеющий никакого пользовательского интерфейса и отправляющий его вывод в консоль. Можно просмотреть вывод или путем выполнения инструмента в XCode или путем выполнения утилиты Console, в которой можно найти /Applications/Utilities/Console
, прежде, чем запустить инструмент.
При использовании версии OS X до v10.1 этот инструмент должен быть выполнен с полномочиями пользователя root, потому что /dev/rdisk*
узлы принадлежат корню в тех версиях. В OS X v10.1 и позже, /dev/*disk*
узлы для съемных носителей принадлежат, в настоящее время входил в систему пользователь (узлы для несъемных носителей все еще принадлежат корню). Если необходимо, можно использовать sudo(8)
команда для запуска инструмента с полномочиями пользователя root как показано ниже (Вас попросят предоставить Ваш admin
пароль):
sudo open /YourDirectoryPath/CDROMSample.app
Включая заголовочные файлы и установку основной функции
Перечисление 1-1 показывает заголовочные файлы, которые необходимо будет включать в основной файл для примера кода в этой главе. (Некоторые из этих заголовков включают других; более короткий список возможен.) За исключением CoreFoundation.h
, эти заголовки обычно являются частью
IOKit.framework
или
System.framework
.
Заголовочные файлы перечисления 1-1 для включения для примера кода устройства хранения
#include <stdio.h> |
#include <string.h> |
#include <unistd.h> |
#include <fcntl.h> |
#include <sys/ioctl.h> |
#include <errno.h> |
#include <paths.h> |
#include <sys/param.h> |
#include <IOKit/IOKitLib.h> |
#include <IOKit/IOBSD.h> |
#include <IOKit/storage/IOCDMedia.h> |
#include <IOKit/storage/IOMedia.h> |
#include <IOKit/storage/IOCDTypes.h> |
#include <IOKit/storage/IOMediaBSDClient.h> |
#include <CoreFoundation/CoreFoundation.h> |
Перечисление 1-2 показывает a main
функция для нахождения устройства CDROM с Набором I/O и доступом к нему с функциями POSIX. main
функция выполняет свою работу путем вызывания следующих функций, показанных в других разделах:
MyFindEjectableCDMedia
(Находящий все выбрасываемые носители CD)MyGetDeviceFilePath
(Получение пути к файлу устройств для устройства CDROM)MyOpenDrive
(Открытие Устройства)MyReadSector
(Чтение сектора от устройства)MyCloseDrive
(Закрытие Устройства)
Тип kern_return_t
определяется в std_types.h
.
Константа KERN_SUCCESS
определяется в kern_return.h
.
Перечисление 1-2 , Находящее устройство CDROM и читающее сектор
int main( void ) |
{ |
kern_return_t kernResult; |
io_iterator_t mediaIterator; |
char deviceFilePath[ MAXPATHLEN ]; |
kernResult = MyFindEjectableCDMedia( &mediaIterator ); |
if ( kernResult != KERN_SUCCESS ) |
return 0; |
kernResult = MyGetDeviceFilePath( mediaIterator, deviceFilePath, |
sizeof( deviceFilePath ) ); |
if ( kernResult != KERN_SUCCESS ) |
return 0; |
// Now open the device we found, read a sector, and close the device. |
if ( deviceFilePath[ 0 ] != '\0' ) |
{ |
int fileDescriptor; |
fileDescriptor = MyOpenDrive( deviceFilePath ); |
if (fileDescriptor != -1 ) |
{ |
if ( MyReadSector( fileDescriptor ) ) |
printf( "Sector read successfully.\n" ); |
else |
printf( "Could not read sector.\n" ); |
MyCloseDrive( fileDescriptor ); |
printf( "Device closed.\n" ); |
} |
} |
else |
printf( "No ejectable CD media found.\n" ); |
// Release the iterator. |
IOObjectRelease( mediaIterator ); |
return 0; |
} |
main
функционируйте выпускает итератор, возвращенный MyFindEjectableCDMedia
функция, также выпускающая объекты итератора.
Нахождение всех выбрасываемых носителей CD
MyFindEjectableCDMedia
функция, показанная в Перечислении 1-3, устанавливает соединение с Набором I/O путем вызова IOMasterPort
функция, возвращающая порт Маха. Это тогда создает соответствующий словарь путем вызова IOServiceMatching
, передача константы kIOCDMediaClass
(определенный в IOCDMedia.h
). Это устанавливает словарь, соответствующий все устройства классу провайдера IOCDMediaClass; все устройства хранения данных CD в Реестре I/O являются экземплярами этого класса или подкласса.
Соответствующий словарь является словарем пар ключ/значение, описывающих свойства устройства Набора I/O или другой службы. Каждый объект IOMedia в Реестре I/O имеет свойство с ключом kIOMediaEjectableKey
и значение, которое является true
если носители являются действительно выбрасываемыми. В этой выборке мы интересуемся только выбрасываемыми носителями, таким образом, MyFindEjectableCDMedia
функция совершенствовала соответствующий словарь путем вызова CFDictionarySetValue
добавить ключ kIOMediaEjectableKey
и значение kCFBooleanTrue
.
Константы kIOMediaEjectableKey
и kIOCDMediaClass
определяются в IOMedia.h
в Kernel.framework
. Константа kCFBooleanTrue
Базовая постоянная Основа. При необходимости в большей информации о процессе использования соответствия словарей для нахождения устройств в Реестре I/O, посмотрите Аппаратные средства Доступа Из Приложений.
Затем, MyFindEjectableCDMedia
передает словарь функции Набора I/O IOServiceGetMatchingServices
получить объект итератора, идентифицирующий все устройства CDROM с выбрасываемыми носителями в Реестре I/O. Если успешный, MyFindEjectableCDMedia
использует его параметр указателя для возврата объекта итератора. Функция вызова ответственна за выпуск этого объекта.
Наконец, MyFindEjectableCDMedia
возвращает значение результата, указывающее, нашло ли оно какие-либо выбрасываемые носители CD. Константа KERN_SUCCESS
определяется в kern_return.h
.
Перечисление 1-3 , Находящее все выбрасываемые носители CD
kern_return_t MyFindEjectableCDMedia( io_iterator_t *mediaIterator ) |
{ |
mach_port_t masterPort; |
kern_return_t kernResult; |
CFMutableDictionaryRef classesToMatch; |
kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort ); |
if ( kernResult != KERN_SUCCESS ) |
{ |
printf( "IOMasterPort returned %d\n", kernResult ); |
return kernResult; |
} |
// CD media are instances of class kIOCDMediaClass. |
classesToMatch = IOServiceMatching( kIOCDMediaClass ); |
if ( classesToMatch == NULL ) |
printf( "IOServiceMatching returned a NULL dictionary.\n" ); |
else |
{ |
// Each IOMedia object has a property with key kIOMediaEjectableKey |
// which is true if the media is indeed ejectable. So add this |
// property to the CFDictionary for matching. |
CFDictionarySetValue( classesToMatch, |
CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue ); |
} |
kernResult = IOServiceGetMatchingServices( masterPort, |
classesToMatch, mediaIterator ); |
if ( (kernResult != KERN_SUCCESS) || (*mediaIterator == NULL) ) |
printf( "No ejectable CD media found.\n kernResult = %d\n", |
kernResult ); |
return kernResult; |
} |
Получение пути к файлу устройств для устройства CDROM
Перечисление 1-4 показывает MyGetDeviceFilePath
функция. Параметры к этой функции указывают итератор по выбрасываемым устройствам хранения данных CD, указателю на хранение для пути файла устройств и максимальному размеру пути. Функциональные возвраты, в deviceFilePath
параметр, путь к файлу устройств, включая имя файла, для первого такое устройство это находит в итераторе.
MyGetDeviceFilePath
функция исследует первый объект в переданном итераторе. Несмотря на то, что много компьютеров имеют всего одно устройство CDROM, итератор мог фактически содержать объекты для многократных устройств; однако, эта функция смотрит на только первое.
MyGetDeviceFilePath
функция выполняет следующие шаги:
Это вызывает функцию Набора I/O
IORegistryEntryCreateCFProperty
, передача ключаkIOBSDNameKey
(определенный вIOBSD.h
), для получения aCFTypeRef
к имени файла устройств.Если вызов к
IORegistryEntryCreateCFProperty
успешно,MyGetDeviceFilePath
создает путь устройства к устройству. Сделать это, функцию:Копирует строку‘
/dev/
’(определенный константой_PATH_DEV
в заголовкеpaths.h
) к месту хранения, указанномуdeviceFilePath
параметрСвязывает строку
‘r’
до конца пути устройства, чтобы гарантировать, что код получает доступ к неструктурированному устройствуВызовы
CFStringGetCString
функция для кодирования Базового представления Основы имени устройства как струна до
Если
MyGetDeviceFilePath
в состоянии создать имя файла устройств успешно, оно распечатывает строку и выпускаетCFTypeRef
. Полное имя файла устройств будет чем-то как/dev/rdisk0
.
IOIteratorNext
функция сохраняет каждый мультимедийный объект, который она возвращает, таким образом, MyGetDeviceFilePath
функционируйте выпускает объекты итератора, которые что это исследует. Функция вызова ответственна за выпуск самого итератора, также выпускающего объекты итератора.
Наконец, MyGetDeviceFilePath
возвращает значение результата, указывающее, получила ли функция успешно путь устройства для устройства CDROM.
Перечисление 1-4 , Получающее файл устройств, соединяет каналом для первых выбрасываемых носителей CD в переданном итераторе
kern_return_t MyGetDeviceFilePath( io_iterator_t mediaIterator, |
char *deviceFilePath, CFIndex maxPathSize ) |
{ |
io_object_t nextMedia; |
kern_return_t kernResult = KERN_FAILURE; |
*deviceFilePath = '\0'; |
nextMedia = IOIteratorNext( mediaIterator ); |
if ( nextMedia ) |
{ |
CFTypeRef deviceFilePathAsCFString; |
deviceFilePathAsCFString = IORegistryEntryCreateCFProperty( |
nextMedia, CFSTR( kIOBSDNameKey ), |
kCFAllocatorDefault, 0 ); |
*deviceFilePath = '\0'; |
if ( deviceFilePathAsCFString ) |
{ |
size_t devPathLength; |
strcpy( deviceFilePath, _PATH_DEV ); |
// Add "r" before the BSD node name from the I/O Registry |
// to specify the raw disk node. The raw disk node receives |
// I/O requests directly and does not go through the |
// buffer cache. |
strcat( deviceFilePath, "r"); |
devPathLength = strlen( deviceFilePath ); |
if ( CFStringGetCString( deviceFilePathAsCFString, |
deviceFilePath + devPathLength, |
maxPathSize - devPathLength, |
kCFStringEncodingASCII ) ) |
{ |
printf( "BSD path: %s\n", deviceFilePath ); |
kernResult = KERN_SUCCESS; |
} |
CFRelease( deviceFilePathAsCFString ); |
} |
} |
IOObjectRelease( nextMedia ); |
return kernResult; |
} |
Открытие устройства
Открыть устройство хранения данных CD, MyOpenDrive
функция, показанная в Перечислении 1-5, вызывает open
функция, передавая путь файла устройств и константу O_RDONLY
, который указывает, что устройство должно быть открыто для чтения только. open
функция и O_RDONLY
оба определяются в fcntl.h
, который является частью System.framework
. Можно получить больше информации о open
функция путем ввода man 2 open
в Окне терминала.
MyOpenDrive
функционируйте возвращает значение, которое это получает от open
функция; на ошибке это также распечатывает сообщение об ошибке.
Перечисление 1-5 , Открывающее устройство, указано его путем файла устройств
int MyOpenDrive( const char *deviceFilePath ) |
{ |
int fileDescriptor; |
fileDescriptor = open( deviceFilePath, O_RDONLY ); |
if ( fileDescriptor == -1 ) |
{ |
printf( "Error opening device %s: \n", deviceFilePath ); |
perror( NULL ); |
} |
return fileDescriptor; |
} |
Чтение сектора от устройства
Перечисление 1-6 показывает функцию, MyReadSector
, это читает сектор носителей. Вызывающая сторона этой функции передает дескриптор файла для файла устройств. Устройство, как предполагается, открыто. MyReadSector
первое использование DKIOCGETBLOCKSIZE
ioctl для получения предпочтительного размера блока для носителей. Затем это выделяет буфер предпочтительного размера блока и попыток считать сектор, с помощью read
функция, определяемая в unistd.h
.
Перечисление 1-6 Читая сектор носителей, учитывая дескриптор файла
Boolean MyReadSector( int fileDescriptor ) |
{ |
char *buffer; |
size_t numBytes; |
u_int32_t blockSize; |
if ( ioctl( fileDescriptor, DKIOCGETBLOCKSIZE, &blockSize ) == -1) |
{ |
perror( "Error getting preferred block size." ); |
// Set a reasonable block size instead. |
// kCDSectorSizeCDDA is defined in IOCDTypes.h as 2352. |
blockSize = kCDSectorSizeCDDA; |
} |
buffer = malloc( blockSize ); |
numBytes = read( fileDescriptor, buffer, blockSize ); |
free( buffer ); |
return numBytes == blockSize ? true : false; |
} |
Закрытие устройства
Перечисление 1-7 показывает MyCloseDrive
функция. Закрыть устройство CDROM, MyCloseDrive
вызовы close
функция (определенный в unistd.h
), передавая дескриптор файла для файла устройств. Дескриптор файла был получен MyOpenDrive
функция.
Перечисление 1-7 , Закрывающее устройство, учитывая его дескриптор файла
void MyCloseDrive( int fileDescriptor ) |
{ |
close( fileDescriptor ); |
} |