如何解决如何停止inotify结果的重复?
在这段代码中,我试图同时监控两条路径。为此,我使用了 while(1)
。但我面临的问题是,每当我运行代码时,它都会给我两次相同的结果。
给出结果
Pathname1 "file" is modified
Pathname1 "file" is modified
预期结果
Pathname1 "file" is modified
我调试了代码。打破主函数并跳过它后,下一个命令停在这一行length = read(fd,buffer,EVENT_BUF_LEN )
。每当我在这个长度可变命令之后换行时,程序就会启动,修改文件后,程序会停在这一行 struct inotify_event *event = ( struct inotify_event *)&buffer[i];
虽然程序不应该中断。
我也用 IN_CLOSE_WRITE
代替 IN_MODIFY
但结果没有变化。
typedef struct{
int length,fd,wd1,wd2;
char buffer[4096] __attribute__ ((aligned(__alignof__(struct inotify_event))));
} notification;
notification inotify;
int getNotified(char *pathname1,char *pathname2){
inotify.fd = inotify_init();
inotify.wd1 = inotify_add_watch(inotify.fd,pathname1,IN_MODIFY);
inotify.wd2 = inotify_add_watch(inotify.fd,pathname2,IN_MODIFY);
while(1){
inotify.length = read(inotify.fd,inotify.buffer,EVENT_BUF_LEN);
int i = 0;
while(i < inotify.length){
struct inotify_event *event = (struct inotify_event *)&inotify.buffer[i];
if(event->len){
if(event->mask & IN_MODIFY){
if(event->wd == inotify.wd1){
printf("Pathname1 '%s' is modified\n",event->name);
break;
}
if(event->wd == inotify.wd2){
printf("Pathname2 '%s' is modified\n",event->name);
break;
}
}
}
i += EVENT_SIZE + event->len;
}
}
inotify_rm_watch(inotify.fd,inotify.wd1);
inotify_rm_watch(inotify.fd,inotify.wd2);
close(inotify.fd);
exit(0);
}
解决方法
一些说明:
- 当你走出内部“while”循环并再次调用read()时,你不应该“中断”
- IN_MODIFY 事件未填充 event->name 字段(即 event->len = 0)
- IN_MODIFY 事件的数量取决于您修改文件的方式(“echo xxx >> file” = 只写 = 1 IN_MODIFY;“echo xxx > file” = truncate + write = 2 IN_MODIFY)
这是对您的程序的建议(带有附加打印):
#include <stdio.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <stdlib.h>
#define EVENT_BUF_LEN 4096
#define EVENT_SIZE sizeof(struct inotify_event)
typedef struct{
int length,fd,wd1,wd2;
char buffer[EVENT_BUF_LEN] __attribute__ ((aligned(__alignof__(struct inotify_event))));
} notification;
notification inotify;
int getNotified(char *pathname1,char *pathname2){
inotify.fd = inotify_init();
inotify.wd1 = inotify_add_watch(inotify.fd,pathname1,IN_MODIFY);
printf("wd1 = %d\n",inotify.wd1);
inotify.wd2 = inotify_add_watch(inotify.fd,pathname2,IN_MODIFY);
printf("wd2 = %d\n",inotify.wd2);
while(1){
inotify.length = read(inotify.fd,inotify.buffer,EVENT_BUF_LEN);
int i = 0;
printf("read() = %d\n",inotify.length);
while(i < inotify.length){
struct inotify_event *event = (struct inotify_event *)&inotify.buffer[i];
printf("event->len = %u\n",event->len);
if(event->mask & IN_MODIFY){
if(event->wd == inotify.wd1){
printf("Pathname1 is modified\n");
} else if (event->wd == inotify.wd2){
printf("Pathname2 is modified\n");
}
}
i += (EVENT_SIZE + event->len);
printf("i=%d\n",i);
}
}
inotify_rm_watch(inotify.fd,inotify.wd1);
inotify_rm_watch(inotify.fd,inotify.wd2);
close(inotify.fd);
exit(0);
}
int main(void)
{
getNotified("/tmp/foo","/tmp/bar");
return 0;
} // main
这是一个执行示例:
$ gcc notif.c -o notif
$ > /tmp/foo
$ > /tmp/bar
$ ./notif
wd1 = 1
wd2 = 2
====== Upon "> /tmp/foo": 1 event (truncate operation)
read() = 16
event->len = 0
Pathname1 is modified
i=16
====== Upon "echo qwerty > /tmp/foo": 2 events (write operation,one event for truncate operation and one for the write of "qwerty" at the beginning of the file)
read() = 16
event->len = 0
Pathname1 is modified
i=16
read() = 16
event->len = 0
Pathname1 is modified
i=16
====== Upon "echo qwerty >> /tmp/foo": 1 event (write of "qwerty" at the end of the file)
read() = 16
event->len = 0
Pathname1 is modified
i=16
,
如果两个路径名指向同一个 inode,则 inotify.wd1 == inotify.wd2
。在那种情况下,因为你有
if (event->wd == inotify.wd1) {
printf("Pathname1 '%s' is modified\n",event->name);
}
if (event->wd == inotify.wd2) {
printf("Pathname2 '%s' is modified\n",event->name);
}
两个主体都将被执行;但输出会有所不同(Pathname1 ...
和 Pathname2 ...
)。
您确实应该监控文件所在的目录,而不是实际的路径名,这样您也可以捕获重命名事件。 (许多编辑器会创建一个临时文件,然后将临时文件重命名或硬链接到旧文件上,以便程序看到旧文件或新文件,而不是两者混合。)
考虑以下程序,它没有表现出任何不适当的重复事件:
// SPDX-License-Identifier: CC0-1.0
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/inotify.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
static volatile sig_atomic_t done = 0;
static void handle_done(int signum)
{
done = signum;
}
static int install_done(int signum)
{
struct sigaction act;
memset(&act,sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = handle_done;
act.sa_flags = 0;
return sigaction(signum,&act,NULL);
}
struct monitor {
/* Supplied by caller */
const char *directory;
const char *pathname;
int (*modified)(struct monitor *);
int (*completed)(struct monitor *);
/* Reserved for internal use */
int dirwatch;
};
int monitor_files(struct monitor *const list,const size_t count)
{
char *events_ptr = NULL;
size_t events_len = 65536;
size_t i;
int err;
/* Verify sane parameters */
if (count < 1) {
errno = ENOENT;
return -1;
} else
if (!list) {
errno = EINVAL;
return -1;
}
for (i = 0; i < count; i++) {
if (!list[i].directory || !list[i].directory[0]) {
errno = EINVAL;
return -1;
}
if (!list[i].pathname || !list[i].pathname[0]) {
errno = EINVAL;
return -1;
}
list[i].dirwatch = -1;
}
/* Obtain a descriptor for inotify event queue */
int queue = inotify_init1(IN_CLOEXEC);
if (queue == -1) {
/* errno set by inotify_init1() */
return -1;
}
/* Use a reasonable dynamically allocated buffer for events */
events_ptr = malloc(events_len);
if (!events_ptr) {
close(queue);
errno = ENOMEM;
return -1;
}
/* Add a watch for each directory to be watched */
for (i = 0; i < count; i++) {
list[i].dirwatch = inotify_add_watch(queue,list[i].directory,IN_CLOSE_WRITE | IN_MOVED_TO | IN_MODIFY);
if (list[i].dirwatch == -1) {
err = errno;
close(queue);
free(events_ptr);
errno = err;
return -1;
}
}
/* inotify event loop */
err = 0;
while (!done) {
ssize_t len = read(queue,events_ptr,events_len);
if (len == -1) {
/* Interrupted due to signal delivery? */
if (errno == EINTR)
continue;
/* Error */
err = errno;
break;
} else
if (len < -1) {
/* Should never occur */
err = EIO;
break;
} else
if (len == 0) {
/* No events watched anymore */
err = 0;
break;
}
char *const end = events_ptr + len;
char *ptr = events_ptr;
while (ptr < end) {
struct inotify_event *event = (struct inotify_event *)ptr;
/* Advance buffer pointer for next event */
ptr += sizeof (struct inotify_event) + event->len;
if (ptr > end) {
close(queue);
free(events_ptr);
errno = EIO;
return -1;
}
/* Call all event handlers,even duplicate ones */
for (i = 0; i < count; i++) {
if (event->wd == list[i].dirwatch && !strcmp(event->name,list[i].pathname)) {
if ((event->mask & (IN_MOVED_TO | IN_CLOSE_WRITE)) && list[i].completed) {
err = list[i].completed(list + i);
if (err)
break;
} else
if ((event->mask & IN_MODIFY) && list[i].modified) {
err = list[i].modified(list + i);
if (err)
break;
}
}
}
if (err)
break;
}
if (err)
break;
}
close(queue);
free(events_ptr);
errno = 0;
return err;
}
static int report_modified(struct monitor *m)
{
printf("%s/%s: Modified\n",m->directory,m->pathname);
fflush(stdout);
return 0;
}
static int report_completed(struct monitor *m)
{
printf("%s/%s: Completed\n",m->pathname);
fflush(stdout);
return 0;
}
int main(void)
{
struct monitor watch[2] = {
{ .directory = ".",.pathname = "file1",.modified = report_modified,.completed = report_completed },{ .directory = ".",.pathname = "file2",.completed = report_completed }
};
int err;
if (install_done(SIGINT) == -1 ||
install_done(SIGHUP) == -1 ||
install_done(SIGTERM) == -1) {
fprintf(stderr,"Cannot set signal handlers: %s.\n",strerror(errno));
return EXIT_FAILURE;
}
fprintf(stderr,"To stop this program,press Ctrl+C,or send\n");
fprintf(stderr,"INT,HUP,or TERM signal (to process %ld).\n",(long)getpid());
fflush(stderr);
err = monitor_files(watch,2);
if (err == -1) {
fprintf(stderr,"Error monitoring files: %s.\n",strerror(errno));
return EXIT_FAILURE;
} else
if (err) {
fprintf(stderr,"Monitoring files failed [%d].\n",err);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
如果您使用例如编译它gcc -Wall -Wextra -O2 example.c -o example
并通过 ./example
运行它,它将报告 file1
和 file2
在相同的目录(当前工作目录)。要退出程序,请按 Ctrl+C。
(注意,我们可能应该添加第三个事件类型,“Deleted”,对应于 IN_DELETE
和 IN_MOVED_FROM
事件。)
如果您在文本编辑器中打开 file1
或 file2
,保存文件会生成一个或多个修改 (IN_MODIFY) 事件,以及一个完成(IN_CLOSE_WRITE 或 IN_MOVED_TO)事件。这是因为每个导致文件被截断的 open()/truncate()/ftruncate() 系统调用,都会为该文件生成一个 IN_MODIFY 事件;与修改文件内容的每个底层 write() 系统调用一样。因此,当某个进程修改文件时,很自然地会收到多个 IN_MODIFY 事件。
如果列表中有多个 struct monitor
条目具有相同的有效目录和相同的路径名,示例程序将为它们提供多个事件。如果您想避免这种情况,只需确保每当 .pathname
匹配时,两者都会得到不同的 .dirwatch
es。
将 while()
循环重写为 for()
循环,并将 if()
变平”
while(1){
int i ;
struct inotify_event *event ;
inotify.length = read(inotify.fd,EVENT_BUF_LEN);
for(i=0; i < inotify.length; i += EVENT_SIZE + event->len ) {
event = (struct inotify_event *)&inotify.buffer[i];
if (!event->len) continue;
if (!(event->mask & IN_MODIFY)) continue;
if (event->wd == inotify.wd1){
printf("Pathname1 '%s' is modified\n",event->name);
continue;
}
if (event->wd == inotify.wd2){
printf("Pathname2 '%s' is modified\n",event->name);
continue;
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。