правильное использование linux inotify-открывать каждый раз?
Я отлаживаю проблему загрузки системы, с которой сталкивается клиент в своей производственной системе, и они сделали тестовое приложение, которое имитирует нагрузку для воспроизведения проблемы:
в этой конкретной рабочей нагрузке одна из вещей, которую кодер сделал, была:
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
но все же не идеально.