Совместное использование файловых дескрипторов между процессами

Моя цель — открыть устройство в одном процессе, а затем закрыть его в другом (не форк). Я не хочу использовать сокет UNIX. Я обнаружил, что начиная с ядра 5.6 появилось два новых системных вызова: syscall(SYS_pidfd_open, pid, flags) и syscall(SYS_pidfd_getfd, pidfd, 0, 0).

Я попробовал простой код, чтобы проверить это решение.

open_device.c:

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <errno.h>

#define pidfd_open(pid, flags) syscall(SYS_pidfd_open, pid, flags)

int main() {
    // Open the device
    int fd = open("/dev/null", O_RDWR);
    if (fd == -1) {
        perror("open");
        return EXIT_FAILURE;
    }

    printf("File descriptor for /dev/null: %d\n", fd);

    // Get the current process ID
    pid_t pid = getpid();

    // Obtain a pidfd for this process
    int pidfd = pidfd_open(pid, 0);
    if (pidfd == -1) {
        perror("pidfd_open");
        close(fd);
        return EXIT_FAILURE;
    }

    printf("Generated pidfd: %d\n", pidfd);

    // Write pidfd to a file
    FILE *pidfd_file = fopen("pidfd.txt", "w");
    if (pidfd_file == NULL) {
        perror("fopen");
        close(fd);
        close(pidfd);
        return EXIT_FAILURE;
    }
    fprintf(pidfd_file, "%d\n", pidfd);
    fclose(pidfd_file);

    // Keep the process alive for testing
    while (1) {
        sleep(10);
    }

    // Clean up
    close(fd);
    close(pidfd);

    return 0;
}

close_device.c

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <errno.h>

#ifndef SYS_pidfd_getfd
#define SYS_pidfd_getfd 438  // Replace with the correct syscall number for your architecture
#endif

int main() {
    // Read the pidfd from the file
    FILE *pidfd_file = fopen("pidfd.txt", "r");
    if (pidfd_file == NULL) {
        perror("fopen");
        return EXIT_FAILURE;
    }

    int pidfd;
    if (fscanf(pidfd_file, "%d", &pidfd) != 1) {
        perror("fscanf");
        fclose(pidfd_file);
        return EXIT_FAILURE;
    }
    fclose(pidfd_file);

    printf("Read pidfd: %d\n", pidfd);

    // Retrieve the file descriptor from the first process
    int target_fd = syscall(SYS_pidfd_getfd, pidfd, 0, 0);
    if (target_fd == -1) {
        perror("pidfd_getfd");
        close(pidfd);
        return EXIT_FAILURE;
    }

    printf("Successfully retrieved fd: %d\n", target_fd);

    // Perform the close operation
    if (close(target_fd) == -1) {
        perror("close");
        close(pidfd);
        return EXIT_FAILURE;
    }

    printf("Device successfully closed.\n");

    close(pidfd);
    return 0;
} 

Проблема в том, что я получаю сообщение об ошибке Bad file descriptor для pidfd_getfd.

Где может быть проблема?

Я раньше не использовал эти API, но вызов syscall(SYS_pidfd_getfd, ... выглядит немного странно. Согласно документации вторым параметром должен быть pid процесса открытия, а третьим параметром — целевой fd в этом процессе (если я неправильно понял). Это не то, что у вас есть.

G.M. 10.08.2024 12:26

@Г.М. Прочтите документацию еще раз. ОП использует его правильно.

stark 10.08.2024 12:34

@stark Хорошо, я все еще не совсем понимаю, но с радостью подчинюсь другим :-)

G.M. 10.08.2024 12:37

@stark Нет, он неправильно используется в коде OP, как G.M. отметил. pidfd должен быть дескриптор файла PID, а не дескриптор файла, который вы хотите дублировать.

Ted Lyngmo 10.08.2024 13:16
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
62
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы используете неправильные аргументы для SYS_pidfd_getfd. pidfd должен быть дескриптором PID, int pidfd = pidfd_open(remote_pid, 0);, где remote_pid — это идентификатор процесса, из которого вы хотите получить дубликат дескриптора файла.

Вы можете просто записать PID процесса открытия в тот же файл, а затем в close_device.c прочитать его и затем вызвать pidfd_open:

#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>

#ifndef SYS_pidfd_getfd
#    define SYS_pidfd_getfd \
        438 // Replace with the correct syscall number for your architecture
#endif

#define pidfd_open(pid, flags) syscall(SYS_pidfd_open, pid, flags)

int main(void) {
    // Read the pidfd from the file
    FILE* pidfd_file = fopen("pidfd.txt", "r");
    if (pidfd_file == NULL) {
        perror("fopen");
        return EXIT_FAILURE;
    }

    pid_t remote_pid;
    int remote_fd;
    // read both pid and the descriptor from the file:
    // Note: %d may not be appropriate for `pid_t`
    if (fscanf(pidfd_file, "%d %d", &remote_pid, &remote_fd) != 2) {
        perror("fscanf");
        fclose(pidfd_file);
        return EXIT_FAILURE;
    }
    fclose(pidfd_file);
    printf("Read pid:%d  remote_fd: %d\n", remote_pid, remote_fd);

    // now get the pidfd for the remote process:
    int pidfd = pidfd_open(remote_pid, 0);
    if (pidfd == -1) {
        perror("pidfd_open");
        return EXIT_FAILURE;
    }

    // Now you can retrieve the file descriptor from the remote process
    int target_fd = syscall(SYS_pidfd_getfd, pidfd, remote_fd, 0);
    if (target_fd == -1) {
        perror("pidfd_getfd");
        close(pidfd);
        return EXIT_FAILURE;
    }

    printf("Successfully retrieved fd: %d\n", target_fd);

    // Perform the close operation
    if (close(target_fd) == -1) {
        perror("close");
        return EXIT_FAILURE;
    }

    printf("Device successfully closed.\n");
}

Спасибо за помощь! Теперь я получаю следующую ошибку: Read pid:259760 remote_fd: 4 pidfd_getfd: Operation not permitted

Droll80 10.08.2024 13:55

Ну, ядро ​​вам сообщило... читали ли вы документацию, связанную с этими системными вызовами? (прочтение последнего абзаца моего ответа, вероятно, объяснит, почему вы получаете эту ошибку)

Luis Colorado 10.08.2024 14:02

@Droll80 Droll80 У процесса должно быть разрешение на дублирование файлового дескриптора другого процесса. Вы запускаете оба процесса от одного и того же пользователя? «Разрешение на дублирование файлового дескриптора другого процесса регулируется проверкой ptrace режима доступа PTRACE_MODE_ATTACH_REALCREDS (см. ptrace(2))».

Ted Lyngmo 10.08.2024 14:27

Дескрипторы файлов действительны в контексте процесса. Это позволяет каждому процессу в системе считать, что файловый дескриптор 0 связан со стандартным вводом, 1 со стандартным выводом и 2 со стандартной ошибкой, но это также добавляет возможность того, что они будут разными для разных процессов. Дескриптор файла — это частные виртуальные номера процесса, которые не имеют значения для другого процесса.

Таким образом, число вроде 18 не может быть файловым дескриптором системного ресурса в одном процессе и быть таким же числом в другом процессе (использовали вы fork или нет)

Единственным исключением из вышеизложенного является то, что система сохраняет (в дочернем процессе) те же значения дескриптора, которые были открыты в родительском процессе, в результате чего оба процесса имеют одинаковые значения для обоих процессов, но во всех других случаях это не может быть гарантировано. поскольку принимающий процесс уже может иметь такое же значение дескриптора для другого ресурса.

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

Существуют способы передачи файловых дескрипторов между процессами, давно реализованные в BSD unix. Они были экспортированы в другие версии Unix, включая Linux (сейчас я не знаю, являются ли они вообще частью POSIX). Сокеты Unix позволяют передавать дескриптор файла другому процессу, но это операция PUSH (предполагает взаимодействие обоих процессов и не совсем та же семантика, которую пытается реализовать Linux - при которой один процесс не знает, что другой процесс обманул свой файловый дескриптор и может управлять файлом) Это может стать проблемой безопасности, если не обращаться с ним осторожно. Судя по некоторым обновлениям, которые вы внесли в свои комментарии, вы получаете сообщение о том, что операция запрещена, поэтому, вероятно, для этого потребуются некоторые атрибуты процесса, которых у вас нет. Я могу только предложить вам запустить промежуточный процесс как root, чтобы проверить, так ли это. Но ИМХО, пока не будет полной документации по этим системным вызовам и они не будут полностью интегрированы/привязаны к стандартной библиотеке C, вам придется читать исходные коды ядра. Хорошей ссылкой может быть Отправка дескриптора файла через сокет unix.

Кстати, возможность дублировать (а это именно та операция, которую вы хотите) другого, несвязанного процесса — опасная проблема, поскольку два дескриптора ресурса будут вести себя так, как если бы дескриптор был dup локально. Это заставит оба процесса совместно использовать флаги открытия файла и, что еще хуже, указатель файла, поэтому каждый раз, когда какой-либо процесс записывает (или читает) файл, указатель файла будет перемещаться, что, вероятно, нарушает доступ другого процесса к нему. файл. Этого никогда не произойдет, если вы снова откроете тот же ресурс. Системные вызовы, о которых вы говорите, опасны, непереносимы и должны использоваться с особой осторожностью.

Процесс заключается в том, чтобы передать дескриптор файла PID другому процессу, а затем использовать его для запроса дублирования дескриптора файла из этого процесса. Это было бы очень выполнимо, как я показал в своем ответе. Что касается редактирования: ОП хочет дублировать дескриптор файла из другого процесса. Это очень ясно. dup/dup2 не очень помогает в настройке, описанной в вопросе. Не нужно гадать.

Ted Lyngmo 10.08.2024 13:42

Для этого есть решения, но вы получаете новый дескриптор (в виде целого числа) в новом процессе, число не будет прежним. Вы можете сделать это через сокет unix. Дескриптор файла, который вы получаете, связан с тем же ресурсом в том же состоянии (как если бы вы сделали дублирование), но ядро ​​не даст вам то же значение!!!

Luis Colorado 10.08.2024 13:48

Вы отвечаете на вопрос или спрашиваете? Со мной не нужно дискутировать. Ошибка, которую допускает ОП, заключается в том, что они думают, что цифры будут одинаковыми.

Luis Colorado 10.08.2024 13:51

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

Ted Lyngmo 10.08.2024 13:51

ОП совершает очень распространенную ошибку: думает, что дескрипторы глобальны и что он может получить доступ к дескриптору, если просто знает его номер. Он думает, что ему нужно получить такое же числовое значение. И мы с вами правы в том, что номер новый.

Luis Colorado 10.08.2024 13:53

Ре. "The OP is ... thinking descriptors are global and that he can access the descriptor if he just knows the number": ОП так не думает. Извините, но этот ответ полностью упускает суть того, что пытается сделать ФП и чего можно достичь.

G.M. 10.08.2024 13:57

Просто чтобы уточнить: мне нужно использовать общий дескриптор файла между процессами. Можете ли вы посоветовать мне лучший способ сделать это?

Droll80 10.08.2024 14:00

используйте сокеты unix и передайте дескриптор файла (и, возможно, гораздо больше информации через хорошо разработанный протокол) через сокет unix (man unix)

Luis Colorado 10.08.2024 14:15

Другие вопросы по теме