правильное использование linux inotify-открывать каждый раз?

Я отлаживаю проблему загрузки системы, с которой сталкивается клиент в своей производственной системе, и они сделали тестовое приложение, которое имитирует нагрузку для воспроизведения проблемы:

sexy graph

в этой конкретной рабочей нагрузке одна из вещей, которую кодер сделал, была:

while(1)
  initialize inotify
  watch a directory for events
  receive event
  process event
  remove watch
  close inotify fd

как ни странно, высокая загрузка системы происходит от close() из inotify fd:

inotify_init()                          = 4 <0.000020>
inotify_add_watch(4, "/mnt/tmp/msys_sim/QUEUES/Child_032", IN_CREATE) = 1 <0.059537>
write(1, "Child [032] sleepingn", 21)  = 21 <0.000012>
read(4, "SrcFile.b8tlfT", 512) = 32 <0.231012>
inotify_rm_watch(4, 1)                  = 0 <0.000044>
close(4)                                = 0 <0.702530>
open("/mnt/tmp/msys_sim/QUEUES/Child_032", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 4 <0.000031>
lseek(4, 0, SEEK_SET)                   = 0 <0.000010>
getdents(4, /* 3 entries */, 32768)     = 88 <0.000048>
getdents(4, /* 0 entries */, 32768)     = 0 <0.000009>
write(1, "Child [032] dequeue [SrcFile.b8t"..., 37) = 37 <0.000011>
unlink("/mnt/tmp/msys_sim/QUEUES/Child_032/SrcFile.b8tlfT") = 0 <0.059298>
lseek(4, 0, SEEK_SET)                   = 0 <0.000011>
getdents(4, /* 2 entries */, 32768)     = 48 <0.000038>
getdents(4, /* 0 entries */, 32768)     = 0 <0.000009>
close(4)                                = 0 <0.000012>
inotify_init()                          = 4 <0.000020>
inotify_add_watch(4, "/mnt/tmp/msys_sim/QUEUES/Child_032", IN_CREATE) = 1 <0.040385>
write(1, "Child [032] sleepingn", 21)  = 21 <0.000903>
read(4, "SrcFile.mQgUSh", 512) = 32 <0.023423>
inotify_rm_watch(4, 1)                  = 0 <0.000012>
close(4)                                = 0 <0.528736>

что может быть причиной вызова close (), чтобы взять такой огромное количество времени? Я могу определить две возможные вещи:--8-->

  • закрытие и повторная инициализация inotify каждый времени
  • есть 256K файлов (плоский) в /mnt/tmp/msys_sim/SOURCES и конкретный файл в /mnt/tmp/msys_sim/QUEUES/Child_032 жестко связан с одним в этом каталоге. Но источники никогда не открываются вышеуказанным процессом

это артефакт использования inotify неправильно? На что я могу указать, чтобы сказать: "то, что вы делаете, неправильно!"?


выход из perf top (Я искал это!)

Events: 109K cycles
 70.01%  [kernel]      [k] _spin_lock
 24.30%  [kernel]      [k] __fsnotify_update_child_dentry_flags
  2.24%  [kernel]      [k] _spin_unlock_irqrestore
  0.64%  [kernel]      [k] __do_softirq
  0.60%  [kernel]      [k] __rcu_process_callbacks
  0.46%  [kernel]      [k] run_timer_softirq
  0.40%  [kernel]      [k] rcu_process_gp_end

сладкий! Я подозреваю, что где-то прядильщик и всей системы идет очень латентно, когда это происходит.

3 ответов


обычно псевдо код inotify цикл будет выглядеть так:

initialize inotify
watch a directory | file for events

while(receive event) {
  process event
}

[ remove watch ]
close inotify fd

нет необходимости удалять часы и повторно инициализировать inotify на каждом цикле.


Я попытался дублировать вашу проблему. Я не получаю тех же результатов, что вы видите. Но да, неправильно использовать inotify таким образом. Обычно вы инициализируете inotify, а затем читаете / опрашиваете его дескриптор часов.

я запустил это с strace -T и не приближайтесь к этому уровню производительности на close().

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <err.h>
#include <sysexits.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/inotify.h>
#include <errno.h>

#define WATCHDIR "./watched"

void child_run(void)
{
    printf("Child spawned..\n");
    int fd;
    if (chdir(WATCHDIR))
        err(EX_OSERR, "Cannot chdir in child");

    /* Care not if this fails.. */
    unlink("myfile.dat");

    while (1) {
        fd = open("myfile.dat", O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
        if (fd < 0) {
            warn("Cannot create necessary file.. sleeping");
            sleep(1);
        }
        close(fd);
        fd = -1;
        if (unlink("myfile.dat") < 0)
            err(EX_OSERR, "Cannot unlink file in watched directory");
    }

}

int main() 
{
    int watch_fd = -1;
    int watched = -1;
    struct inotify_event ev[128];
    memset(ev, 0, sizeof(&ev)*128);

    if (mkdir(WATCHDIR, S_IRWXU) < 0) {
        if (errno != EEXIST) {
            err(EX_OSERR, "Cannot create directory");
        }
    }

    if (fork() == 0) {
        child_run();
        exit(0);
    }

    while (1) {
        if ((watch_fd = inotify_init1(IN_CLOEXEC)) < 0)
            err(EX_OSERR, "Cannot init inotify");

        if (watch_fd < 0)
            err(EX_OSERR, "Cannot init watch");

        if ((watched = inotify_add_watch(watch_fd, WATCHDIR, IN_CREATE)) < 0)
            err(EX_OSERR, "Cannot add watched directory");

        if (read(watch_fd, ev, sizeof(ev)*128) < 0)
            err(EX_OSERR, "Cannot read from watcher");

        if (inotify_rm_watch(watch_fd, watched) < 0)
            err(EX_OSERR, "Cannot remove watch");

        close(watch_fd);
    }
    return 0;
}

Если вы запустите это, вы получите такую же производительность на этом хосте?


Я нашел дымящийся пистолет. Из профилирования ядра (perf top-это то, что я искал):

Events: 109K cycles
 70.01%  [kernel]      [k] _spin_lock
 24.30%  [kernel]      [k] __fsnotify_update_child_dentry_flags
  2.24%  [kernel]      [k] _spin_unlock_irqrestore
  0.64%  [kernel]      [k] __do_softirq
  0.60%  [kernel]      [k] __rcu_process_callbacks
  0.46%  [kernel]      [k] run_timer_softirq
  0.40%  [kernel]      [k] rcu_process_gp_end

http://lxr.free-electrons.com/source/fs/notify/fsnotify.c?a=sh#L52

без тщательного анализа кода, похоже, что с тестом при условии, что этот код будет петлей по всем записям каталога 262K в источниках внутри блокировку ядра. Это поведение, вероятно, неверно и происходит от неправильного использования API inotify.

вызов FS remount (с тестом все еще работает) заставляет его вести себя лучше:

Events: 38K cycles                                                                                                          
 20.41%  [kernel]      [k] _spin_lock
 17.43%  [kernel]      [k] _spin_unlock_irqrestore
 12.40%  [kernel]      [k] __fsnotify_update_child_dentry_flags
  6.44%  [kernel]      [k] run_timer_softirq
  5.65%  [kernel]      [k] __do_softirq          
  5.18%  [kernel]      [k] update_shares
  5.02%  [kernel]      [k] __rcu_process_callbacks

но все же не идеально.