Недавно мне пришлось решить проблему, аналогичную описанной ОП.С этой целью я перешел к предложению выделенного системного вызова (очень простого, я мог бы добавить) для отправки файловых дескрипторов непосредственно на адреса взаимодействующих процессов и использования сигнальных очередей Posix.1b в качестве средства доставки (в качестве дополнительного преимущества, такого какэтот подход по своей природе невосприимчив к атаке "fd recursion", которая до некоторой степени поражает все механизмы, основанные на VFS.
Вот предлагаемый патч:
http://permalink.gmane.org/gmane.linux.kernel/1843084
(в настоящее времяпатч добавляет только новый системный вызов для архитектуры x86 / x86_64, но подключение его к другим архитектурам тривиально, функции, зависящие от платформы, не используются).
Теория работы выглядит следующим образом.И отправитель, и получатель должны согласовать один или несколько номеров сигналов для использования при передаче дескриптора.Это должны быть сигналы Posix.1b, которые гарантируют надежную доставку, таким образом, смещение SIGRTMIN
.Кроме того, меньшие номера сигналов имеют более высокий приоритет доставки, в случае, если требуется управление приоритетами:
int signo_to_use = SIGRTMIN + my_sig_off;
Затем исходящий процесс вызывает системный вызов:
int err = sendfd(peer_pid, signo_to_use, fd_to_send);
Вот и все, больше ничегонеобходимо на стороне отправителя.Очевидно, что sendfd()
будет успешным, только если исходный процесс имеет право сигнализировать о процессе назначения, а процесс назначения не блокирует / игнорирует сигнал.
Следует также отметить, что sendfd()
никогда не блокирует;он немедленно вернется, если очередь сигналов целевого процесса заполнена.В хорошо спроектированном приложении это будет указывать на то, что процесс назначения в любом случае находится в затруднительном положении, или слишком много работы, поэтому новые рабочие должны появляться / отбрасываться.Размер очереди сигналов процесса может быть настроен с использованием rlimit()
, так же как и количество доступных файловых дескрипторов.
Процесс приема может безопасно игнорировать сигнал (в этом случае ничего не произойдет и почти не будет служебных данныхбудет происходить на стороне ядра).Однако, если принимающий процесс хочет получить дескриптор доставленного файла, все, что ему нужно, это собрать информацию о сигнале, используя sigtimedwait()
/ sigwaitinfo()
или более универсальный signalfd()
:
/* First, the receiver needs to specify what it is waiting for: */
sigset_t sig_mask;
sigemptyset(&sig_mask);
sigaddset(&sig_mask, signo_to_use);
siginfo_t sig_info;
/* Then all it needs is to wait for the event: */
sigwaitinfo(&sig_mask, sig_info);
Послеуспешное возвращение sigwaitinfo()
, sig_info.si_int
будет содержать новый файловый дескриптор, указывающий на тот же объект ввода-вывода, что и файловый дескриптор, отправленный исходным процессом.sig_info.si_pid
будет содержать PID инициирующего процесса, а sig_info.si_uid
будет содержать UID инициирующего процесса.Если sig_info.si_int
меньше нуля (представляет неверный дескриптор файла), sig_info.si_errno
будет содержать errno
для фактической ошибки, возникшей в процессе дублирования fd.