Гарантируется ли, что дескриптор файла будет создан, когда процесс обращается к файлу? В частности, в /proc/pid/fd.

Я создаю программу ebpf, которая имеет точку трассировки системного вызова read() .

Цель программы — подсчитать, сколько раз был прочитан файл.

Мой текущий метод отслеживания этого заключается в следующем:

  1. Когда достигается точка трассировки для read(), она получает доступ к дескриптору файла (fd) и идентификатору процесса (pid). Мой код обратного вызова c выполняется с этой информацией.
  2. Код получает индексный дескриптор из каждого файла в /proc/{pid}/fd. Если индексный дескриптор соответствует конкретному файлу (файлам), который нужно отслеживать, я увеличиваю счетчик. Все это происходит до завершения системного вызова read().

Иногда это работает отлично и отлично... Однако я заметил кое-что странное...

  1. Файлы меньшего размера (например, всего 2 строки) обычно не подбираются таким образом, а файлы большего размера - да.

Например:

кот small.txt = Пропущен

кот big.txt = Виден

  1. Если я добавлю команду strace перед вызовом cat, она сработает

Например:

strace -q cat small.txt = Просмотрено

Код ebpf, который это обрабатывает, можно увидеть здесь:

      void handle_event(void *ctx, void *data, unsigned int data_sz)
{
    //struct that holds data like pid, and fd
    struct data_t *m = data;

    //get /proc/pid/fd as string
    char path_str[100];
    sprintf(path_str, "/proc/%d/fd", m->pid);
    

    //for traversing the proc dir
    DIR *mydir;
    struct dirent *myfile;
    struct stat mystat;

    //traverse /proc/pid/fd
    char buf[512];
    mydir = opendir(path_str);
    if(mydir != NULL){

        while((myfile = readdir(mydir)) != NULL)
        {
            //stat each file
            sprintf(buf, "%s/%s", path_str, myfile->d_name);
            stat(buf, &mystat);
            
            //check if inode matches from list
            if(mystat.st_ino == 1396301 || mystat.st_ino == 1319264 || mystat.st_ino == 5768513 || mystat.st_ino == 1318781){
                //alert message to signify file was read
                printf("Read file!\n");
                printf("inode = %d \n", mystat.st_ino);
            }


        }
    }
    //close the dir
    if(mydir != NULL){
        closedir(mydir);
    }

    
}

Я заметил, что cat big.txt всегда имеет fd в /proc/pid/fd для big.txt, как и strace -q small.txt для small.txt.

Однако для cat small.txt, похоже , никогда не было fd .

Я предполагаю, что это как-то связано с кешированием, но я не могу понять, как осуществляется доступ к файлу в случае cat small.txt , потому что даже если он обращается к файлу через кеш, не будет ли это также создать fd и обновить /proc/pid/fd?

Если нет, то почему? И какой механизм вместо этого будет использоваться для доступа к содержимому файла?

Любая помощь приветствуется.

1 ответ

Спасибо @user1686 за ваш вопрос. Это помогло мне понять, что по сути ничто не гарантирует, что мой обратный вызов точки трассировки (который выполняется в пользовательском пространстве) завершится раньше системного вызова read(), который выполняется в пространстве ядра.

Поэтому, скорее всего, происходит то, что процесс закрывает файл до того, как мой обратный вызов сможет получить доступ к /proc/pid/fd, и поэтому нормальное поведение, при котором fd добавляется в /proc/pid/fd, происходит, как и ожидалось.

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