Получение списка файловых дескрипторов процесса

Трудно найти приложение, которое работало бы само по себе и не нуждалось в обмене данными с другими процессами. QNX 6.x предоставляет мощный механизм обмена данными, основанный на передаче сообщений с помощью семейства функций MsgSend().

Сообщения передаются через так называемые каналы, создаваемые серверным приложением с помощью системного вызова ChannelCreate(). После того, как канал создан, к нему могут подключаться клиентские приложения с помощью вызова ConnectAttach(). ConnectAttach() возвращает клиенту идентификатор, который тот потом использует для отправки сообщений серверному приложению с помощью MsgSend(). Все это может происходить совершенно неявно для разработчика, скрываясь под такими библиотечными вызовами стандарта POSIX, как open(), read(), close()…

Условно говоря, идентификаторы каналов, полученные клиентом после выполнения вызова ConnectAttach(), можно разделить на три группы:

стандартные каналы для передачи данными, которые по сути своей являются обычными файловыми дескрипторами. Примеры: открыть файл на диске, создать сокет, открыть для чтения последовательный порт и т.д.;
дескрипторы каналов непосредственно подключенных к серверу. Применяются для прямой связи между программами, в поле index вызова ConnectAttach() добавляется _NTO_SIDE_CHANNEL. Очень распространенной ошибкой является объявление канала связи как обычного файлового дескриптора, без указания _NTO_SIDE_CHANNEL, при вызове ConnectAttach(). Такие приложения часто аварийно завершаются при параллельном запуске диагностических утилит.
дескриптор канала связи с ядром — дескриптор канала, получаемого каждым процессом при его создании;
Дескрипторы первой группы всегда меньше значения _NTO_SIDE_CHANNEL, второй — всегда больше, а в третью группу входит лишь один дескриптор — он равен _NTO_SIDE_CHANNEL. Непосредственно по нему выполняется взаимодействие процесса с ядром.

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

Системный вызов ConnectServerInfo() позволяет получить информацию о любом открытом канале любого процесса в системе. В качестве параметров ConectServerInfo() принимает три значения. Первые два определяют процесс и канал, о котором мы хотим получить информацию, а третий – указатель на структуру _server_info, которая заполняется если вызов завершается успешно. Вызывая ConnectServerInfo() в цикле мы можем просканировать все каналы связи процесса. Вот как это можно сделать:

struct _server_info info;
int fd = 0;

while(( fd = ConnectServerInfo(pid, fd, &info;)) != -1) {
// анализ структуры info
fd++;
}
Нужно отметить такую особенность работы ConnectServerInfo() – если запрашиваемый канал не существует, то будет возвращен следующий по номеру. То есть, если мы запрашивали информацию о fd = 5, а такого дескриптора у процесса нет, но есть дескриптор 7, то вызов вернет именно 7. Если fd больше или равен _NTO_SIDE_CHANNEL, то такой канал интереса для нас не представляет, так как является каналом для передачи системных сообщений, и никакой полезной дополнительной информации мы получить по нему не можем.