Книга: Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform
Структуры данных уровня POSIX
Разделы на этой странице:
Структуры данных уровня POSIX
К подпрограммам POSIX-уровня относятся три структуры данных. Отметьте, что пока речь идет о базовом уровне, вы можете использовать любые структуры данных, которые пожелаете; соответствия определенной структуре и содержанию требует именно уровень POSIX. Однако, преимущества, которые предоставляет POSIX-уровень, с лихвой окупают вносимые ограничения. Как мы увидим далее, вы также сможете дополнять эти структуры вашим собственным содержанием.
На рисунке приведены эти три структуры данных для случая, когда несколько клиентов используют администратора ресурсов, объявивший два устройства:
Структуры данных — общая схема.
Структурами данных являются:
iofunc_ocb_t
— OCB (блок открытого контекста)
Содержит информацию по каждому дескриптору файла.
iofunc_attr_t
— атрибутная запись
Содержит информацию по каждому устройству.
iofunc_mount_t
— запись точки монтирования
Содержит информацию по каждой точке монтирования.
Когда мы обсуждали таблицы функций установления соединения и ввода/вывода, мы уже видели блоки открытого контекста и атрибутные записи — в таблицах функций ввода/вывода OCB был последним передаваемым параметром. Атрибутная запись передавалась как параметр handle (третий по счету) в функциях установления соединения. Запись точки монтирования обычно представляет собой глобальную структуру и привязывается к атрибутной записи «вручную» (в инициализационном коде, написанном вами для вашего администратора ресурса).
Структура блока открытого контекста (OCB) iofunc_ocb_t
Структура блока открытого контекста (OCB) содержит информацию по каждому дескриптору файла. Это означает, что когда клиент выполняет вызов open() и получает в ответ дескриптор файла (в противоположность коду ошибки), администратор ресурсов создает OCB и связывает его с данным клиентом. Этот OCB будет существовать до тех пор, пока клиент держит данный дескриптор файла открытым. В действительности, OCB и дескриптор файла — всегда согласованная пара. По каждому сообщению ввода/вывода от клиента библиотека администратора ресурсов автоматически ищет нужный OCB и вместе с сообщением передает его нужной функции из таблицы функций ввода/вывода. Это ответ на вопрос, зачем всем функциям ввода/вывода параметр ocb. В конце концов клиент закроет дескриптор файла (применив close()), что заставит администратор ресурса отвязать OCB от дескриптора файла и от клиента. Заметьте, что клиентская функция dup() просто увеличивает счетчик связей. В этом случае OCB отделяется от дескриптора файла и от клиента только тогда, когда значение счетчика связей достигнет нуля (то есть когда число вызовов close() будет соответствовать числу open() и dup()).
Как вы, наверное, догадываетесь, в OCB содержатся важные вещи по каждому открытию ресурса и по каждому дескриптору файла. Вот его содержание (взято из <sys/iofunc.h>
):
typedef struct _iofunc_ocb {
IOFUNC_ATTR_T *attr;
int32_t ioflag;
CM НИЖЕ!!! offset;
uint16_t sflag;
uint16_t flags;
} iofunc_ocb_t;
Проигнорируем пока комментарий относительно поля offset; мы вернемся к этому вопросу сразу же после данного обсуждения.
Поля структуры iofunc_ocb_t
:
attr | Указатель на атрибутную запись, связанную с данным блоком OCB. В функциях ввода/вывода вы будете встречать устоявшуюся идиому «ocb->attr »; она используется для получения доступа к элементам атрибутной записи. |
ioflag | Режим открытия, то есть как был открыт ресурс (например, «только для чтения»). Заметьте, что поле ioflag содержит режим открытия (который был передан клиентской функции open()) плюс единица. Например, режим открытия O_RDONLY (значение 0) появится в поле ioflag, как значение, равное единице (1) (константа _READ из <stdio.h> ). Это позволяет трактовать два младших бита поля ioflag как флаги разрешения чтения и записи (ioflag & _READ указывает на право доступа по чтению; ioflag & _WRITE — по записи). |
offset | Текущее смещение lseek() в данном ресурсе. |
sflag | Флаг разделяемого использования (см. <share.h> ), используемый с клиентской функцией вызова sopen(). Возможны значения SH_COMPAT, SH_DENYRW, SH_DENYWR, SH_DENYRD, и SH_DENYN |
flags | Системные флаги. В настоящее время поддерживаются два флага: IOFUNC_OCB_PRIVILEGED, указывающий на то, что этот OCB был создан в результате сообщения установления соединения от привилегированного процесса, и IOFUNC_OCB_MMAP, указывающий, используется ли этот OCB функцией mmap() на стороне клиента. На настоящий момент никаких других флагов не определено. Вы можете использовать биты, заданные в IOFUNC_OCB_FLAGS_PRIVATE, по своему собственному усмотрению. |
Если вы хотите наряду со «стандартным» OCB сохранить какие-либо дополнительные данные, то будьте покойны — OCB можно «расширять». Мы обсудим это в разделе «Дополнительно».
Это странное поле offset
Поле offset, скажем так, как минимум любопытно. Посмотрите в <sys/iofunc.h>
, как оно реализовано. В зависимости от того, какие у вас заданы флаги препроцессора, вы можете получить одну из шести (!) возможных раскладок поля offset. Но не беспокойтесь особо по поводу реализации — реально есть смысл рассматривать только два случая, в зависимости от того, хотите вы поддерживать 64-разрядные смещения или нет:
• если да, то поле offset 64-разрядное;
• если нет (у вас 32-разрядные целые), то поле offset — это младшие 32 бита; старшие 32 бита хранятся в поле offset_hi.
Для наших целей, если речь не идет о явном противопоставлении 32- и 64-разрядных значений, мы будем предполагать, что все смещения являются 64-разрядными (типа off_t
), и платформа знает, что делать с 64-разрядными числами.
Атрибутная запись iofunc_attr_t
В то время как OCB был определен как структура данных по каждому дескриптору файла, атрибутная запись является структурой данных по каждому устройству. Вы видели, что стандартный OCB типа iofunc_ocb_t
имеет элемент, называемый attr, который представляет собой указатель на атрибутную запись. Это сделано для того, чтобы у OCB был доступ к информации об устройстве. Давайте посмотрим на атрибутную запись (взято из <sys/iofunc.h>
):
typedef struct _iofunc_attr {
IOFUNC_MOUNT_T *mount;
uint32_t flags;
int32_t lock_tid;
uint16_t lock_count;
uint16_t count;
uint16_t rcount;
uint16_t wcount;
uint16_t rlocks;
uint16_t wlocks;
struct _iofunc_mmap_list *mmap_list;
struct _iofunc_lock_list *lock_list;
void *list;
uint32_t list_size;
СМ_НИЖЕ!!! nbytes;
СМ_НИЖЕ!!! inode;
uid_t uid;
gid_t gid;
time_t mtime;
time_t atime;
time_t ctime;
mode_t mode;
nlink_t nlink;
dev_t rdev;
} iofunc_attr_t;
У полей nbytes и inode такой же набор директив условной компиляции, что и у поля offset в OCB (см. параграф «Это странное поле offset»).
Заметьте, что некоторые из полей атрибутной записи полезны только для вспомогательных функций POSIX.
Давайте рассмотрим поля в индивидуальном порядке:
mount | Указатель на необязательную запись точки монтирования (типа iofunc_mount_t ). Он применяется аналогично указателю на атрибутную запись, входящему в состав в OCB, за исключением того, что здесь это поле может принимать NULL — в этом случае для записи точки монтирования применяются установки по умолчанию (см. ниже параграф «Запись точки монтирования iofunc_mount_t »). Как уже было упомянуто, запись точки монтирования привязывается к атрибутной записи «вручную» в инициализационном коде вашего администратора ресурсов. |
flags | Содержит флаги, которые описывают состояние других полей атрибутной записи. Мы вскоре к ним вернемся. |
lock_tid | Для предупреждения проблем синхронизации доступ множества потоков к одной и той же атрибутной записи должен быть взаимно исключающим. Поле lock_tid содержит идентификатор потока (thread ID), которым данная атрибутная запись блокирована в настоящий момент. |
lock_count | Указывает, сколько потоков пытаются использовать данную атрибутную запись. Нулевое значение указывает на то, что структура не заблокирована. Значение, большее нуля (единица или более) указывает на то, что данную структуру используют один или более потоков. |
count | Указывает на число OCB, которые по какой-либо причине открыли эту атрибутную запись. Например, если у одного клиента есть OCB, открытый на чтение, у другого — другой OCB, открытый на чтение/запись, и оба эти OCB указывают на одну и ту же атрибутную запись, то значение count для нее должно быть равно 2. Это будет указывать на то, что данный ресурс открыт двумя клиентами. |
rcount | Число читателей. В примере, приведенном для count, rcount будет также иметь значение 2, потому что ресурс открыт на чтение двумя клиентами. |
wcount | Число писателей. В примере, приведенном для count, wcount будет иметь значение 1, потому что ресурс открыт на чтение только одним клиентом. |
rlocks | Показывает число OCB, наложивших на данный ресурс блокировки по чтению. Если значение этого поля равно нулю, это означает, что никаких блокировок по чтению нет, но могут быть блокировки по записи. |
wlocks | Аналогично rlocks, только для блокировок по записи. |
mmap_list | Для внутреннего использования POSIX-функцией iofunc_mmap_default(). |
lock_list | Для внутреннего использования POSIX-функцией iofunc_lock_default(). |
list | Зарезервировано. |
list_size | Размер области, зарезервированной под поле list. |
nbytes | Размер ресурса в байтах. Например, если ресурс описывает конкретный файл, и этот файл имеет размер 7756 байт, то поле nbytes будет содержать значение 7756. |
inode | Содержит порядковый номер файла или ресурса; он должен быть уникален для каждой точки монтирования. Значение поля inode никогда не должно быть нулевым, потому что нуль указывает на неиспользуемый файл. |
uid | Идентификатор пользователя владельца данного ресурса. |
gid | Идентификатор группы владельца данного ресурса. |
mtime | Время последней модификации файла, обновленное или как минимум ставшее недействительным вследствие обработки клиентской функции write(). |
atime | Время последнего доступа к файлу, обновленное или как минимум ставшее недействительным вследствие обработки клиентской функции read(), возвратившей ненулевое количество прочитанных байт. |
ctime | Время последнего изменения файла, обновленное или как минимум ставшее недействительным вследствие обработки клиентских функций write(), chown() или chmod(). |
mode | Режим доступа к файлу. Содержит стандартные значения S_* из <sys/stat.h> (например, S_IFCHR), или восьмеричные значения (например, 0664), указывающие на режим доступа для владельца объекта (owner), группы (group) и всех остальных (other). |
nlink | Число связей (линков) файла, возвращаемое клиентским вызовом stat(). |
rdev | Для специальных символьных устройств это поле состоит из старшего (major) и младшего (minor) кодов устройства (10 младших бит — младший код, старшие 6 бит — старший). Для устройств другого типа это поле содержит номер устройства (подробности см. ниже в параграфе «О номерах устройств, индексных дескрипторах и нашем друге rdev»). |
Как и в случае с OCB, вы можете расширять «стандартную» атрибутную запись вашими собственными данными — см. раздел «Дополнительно».
Запись точки монтирования iofunc_mount_t
Запись точки монтирования содержит информацию, общую для нескольких атрибутных записей.
Вот содержимое записи точки монтирования (взято из <sys/iofunc.h>
):
typedef struct _iofunc_mount {
uint32_t flags;
uint32_t conf;
dev_t dev;
int32_t blocksize;
iofunc_funcs_t *funcs;
} iofunc_mount_t;
Поле flags содержит только один флаг — IOFUNC_MOUNT_32BIT. Этот флаг указывает на то, что параметр offset в OCB и параметры nbytes и inode в атрибутной записи являются 32-разрядными. Заметьте, что вы можете определять ваши собственные флаги в поле flags, используя биты, определенные константой IOFUNC_MOUNT_FLAGS_PRIVATE.
Поле conf содержит следующие флаги:
IOFUNC_PC_CHOWN_RESTRICTED
Указывает, что файловая система является «chown-ограниченной», то есть никто, кроме суперпользователя (root), не может применять к файлам операцию chown().
IOFUNC_PC_NO_TRUNC
Указывает на то, что файловая система не выполняет усечение имен.
IOFUNC_PC_SYNC_IO
Указывает на то, что файловая система поддерживает синхронные операции ввода-вывода.
IOFUNC_PC_LINK_DIR
Указывает на то, что допускается создание/уничтожение связей (linking/unilnking) для каталогов.
Поле dev содержит номер устройства и описывается ниже в параграфе «О номерах устройств, индексных дескрипторах и нашем друге rdev».
Поле blocksize описывает типовой для данного устройства размер блока в байтах. Например, для дисковых устройств типовым значением будет 512.
И наконец, поле funcs указывает на следующую структуру (взято из <sys/iofunc.h>):
typedef struct _iofunc_funcs {
unsigned nfuncs;
IOFUNC_OCB_T *(*ocb_calloc)(
resmgr_context_t *ctp, IOFUNC_ATTR_T *attr);
void (*ocb_free)(IOFUNC_OCB_T *ocb);
} iofunc_funcs_t;
Как и в таблицах функций установления соединения и ввода/вывода, поле nfuncs должен содержать текущий размер таблицы. Используйте для этого константу _IOFUNC_NFUNCS.
Указатели на функции ocb_calloc и ocb_free могут быть заполнены адресами функций, которые следует вызывать всякий раз при создании и уничтожении OCB. Зачем вам могут понадобиться эти функции — мы обсудим это чуть позже, когда будем говорить о расширении OCB.
О номерах устройств, индексных дескрипторах и нашем друге rdev
Запись точки монтирования содержит поле с именем dev. Атрибутная запись содержит два поля: inode и rdev. Давайте рассмотрим их взаимосвязь на примере традиционной дисковой файловой системы. Файловая система монтируется на блок- ориентированном устройстве (которое представляет собой весь диск целиком). Это блок-ориентированное устройство может называться, скажем, /dev/hd0
(первый жесткий диск в системе). На этом диске может быть несколько разделов, один из которых вполне может называться /dev/hd0t77
(первый раздел файловой системы QNX на этом конкретном устройстве). И, наконец, на этом разделе может находиться произвольное число файлов, один из которых может иметь имя /hd/spud.txt
.
Поле dev («device number» — «номер устройства») содержит число, уникальное для узла, на котором зарегистрирован данный администратор ресурсов. Поле rdev — значение dev для корневого устройства (root device). И, наконец, поле inode — порядковый номер файла.
Попробуем соотнести это с нашим примером дисковой системы. В приведенной ниже таблице приведен ряд чисел; взглянем на таблицу, а потом посмотрим, откуда появились эти номера и как они соотносятся.
Устройство | dev | inode | rdev |
---|---|---|---|
/dev/hd0 | 6 | 2 | 1 |
/dev/hd0t77 | 1 | 12 | 77 |
/hd/spud.txt | 77 | 47343 | - |
Для «сырого» блок-ориентированного устройства /dev/hd0
значения dev и inode назначил администратор процессов (значения 6 и 2 в таблице, см. выше). Администратор ресурсов при старте получил для устройства уникальное значение rdev (число 1).
Для раздела /dev/hd0t77
значение dev взялось из значения rdev «сырого» блок-ориентированного устройства (та самая 1). Значение inode было выбрано администратором ресурсов как уникальное в пределах rdev. Так появилась цифра 12. Наконец, значение rdev было также выбрано администратором ресурсов — здесь разработчик администратора выбрал значение 77, потому что оно соответствует типу раздела.
И наконец, для файла /hd/spud.txt
, значение dev (77) было взято из значения rdev для раздела. Значение inode было выбрано администратором ресурса (в случае файла номер выбирается так, чтобы соответствовать некоторому внутреннему представлению файла — конкретное число не имеет значения, лишь бы оно было ненулевым и уникальным в пределах rdev). Отсюда число 47343. Для файла поле rdev не имеет значения.
- Резервное копирование базы данных InterBase
- Firebird РУКОВОДСТВО РАЗРАБОТЧИКА БАЗ ДАННЫХ
- Резервное копирование многофайловых баз данных
- Восстановление из резервных копий многофайловых баз данных
- Владелец базы данных
- ЧАСТЬ IV. База данных и ее объекты.
- Перевод базы данных InterBase 6.x на 3-й диалект
- Типы данных для работы с датой и временем
- Практическая работа 53. Запуск Access. Работа с объектами базы данных
- Обзор основных причин повреждения базы данных
- Ошибки проектирования базы данных
- Профилактика повреждений баз данных InterBase