📄 inotify-kernel.c
字号:
/* Copyright (C) 2005 John McCutchan The Gnome Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The Gnome Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the Gnome Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Authors:. John McCutchan <john@johnmccutchan.com>*/#include "config.h"#include <stdio.h>#include <sys/ioctl.h>#include <unistd.h>#include <errno.h>#include <string.h>#include <glib.h>#include "inotify-kernel.h"#include <sys/inotify.h>/* Timings for pairing MOVED_TO / MOVED_FROM events */#define PROCESS_EVENTS_TIME 1000 /* milliseconds (1 hz) */#define DEFAULT_HOLD_UNTIL_TIME 0 /* 0 millisecond */#define MOVE_HOLD_UNTIL_TIME 0 /* 0 milliseconds */static int inotify_instance_fd = -1;static GQueue *events_to_process = NULL;static GQueue *event_queue = NULL;static GHashTable * cookie_hash = NULL;static GIOChannel *inotify_read_ioc;static GPollFD ik_poll_fd;static gboolean ik_poll_fd_enabled = TRUE;static void (*user_cb)(ik_event_t *event);static gboolean ik_read_callback (gpointer user_data);static gboolean ik_process_eq_callback (gpointer user_data);static guint32 ik_move_matches = 0;static guint32 ik_move_misses = 0;static gboolean process_eq_running = FALSE;/* We use the lock from inotify-helper.c * * There are two places that we take this lock * * 1) In ik_read_callback * * 2) ik_process_eq_callback. * * * The rest of locking is taken care of in inotify-helper.c */G_LOCK_EXTERN (inotify_lock);typedef struct ik_event_internal { ik_event_t *event; gboolean seen; gboolean sent; GTimeVal hold_until; struct ik_event_internal *pair;} ik_event_internal_t;/* In order to perform non-sleeping inotify event chunking we need * a custom GSource */static gbooleanik_source_prepare (GSource *source, gint *timeout){ return FALSE;}static gbooleanik_source_timeout (gpointer data){ GSource *source = (GSource *)data; /* Re-active the PollFD */ g_source_add_poll (source, &ik_poll_fd); g_source_unref (source); ik_poll_fd_enabled = TRUE; return FALSE;}#define MAX_PENDING_COUNT 2#define PENDING_THRESHOLD(qsize) ((qsize) >> 1)#define PENDING_MARGINAL_COST(p) ((unsigned int)(1 << (p)))#define MAX_QUEUED_EVENTS 2048#define AVERAGE_EVENT_SIZE sizeof (struct inotify_event) + 16#define TIMEOUT_MILLISECONDS 10static gbooleanik_source_check (GSource *source){ static int prev_pending = 0, pending_count = 0; /* We already disabled the PollFD or * nothing to be read from inotify */ if (!ik_poll_fd_enabled || !(ik_poll_fd.revents & G_IO_IN)) return FALSE; if (pending_count < MAX_PENDING_COUNT) { unsigned int pending; if (ioctl (inotify_instance_fd, FIONREAD, &pending) == -1) goto do_read; pending /= AVERAGE_EVENT_SIZE; /* Don't wait if the number of pending events is too close * to the maximum queue size. */ if (pending > PENDING_THRESHOLD (MAX_QUEUED_EVENTS)) goto do_read; /* With each successive iteration, the minimum rate for * further sleep doubles. */ if (pending-prev_pending < PENDING_MARGINAL_COST (pending_count)) goto do_read; prev_pending = pending; pending_count++; /* We are going to wait to read the events: */ /* Remove the PollFD from the source */ g_source_remove_poll (source, &ik_poll_fd); /* To avoid threading issues we need to flag that we've done that */ ik_poll_fd_enabled = FALSE; /* Set a timeout to re-add the PollFD to the source */ g_source_ref (source); g_timeout_add (TIMEOUT_MILLISECONDS, ik_source_timeout, source); return FALSE; }do_read: /* We are ready to read events from inotify */ prev_pending = 0; pending_count = 0; return TRUE;}static gbooleanik_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data){ if (callback) return callback (user_data); return TRUE;}static GSourceFuncs ik_source_funcs ={ ik_source_prepare, ik_source_check, ik_source_dispatch, NULL};gboolean _ik_startup (void (*cb)(ik_event_t *event)){ static gboolean initialized = FALSE; GSource *source; user_cb = cb; /* Ignore multi-calls */ if (initialized) return inotify_instance_fd >= 0; initialized = TRUE; inotify_instance_fd = inotify_init (); if (inotify_instance_fd < 0) return FALSE; inotify_read_ioc = g_io_channel_unix_new (inotify_instance_fd); ik_poll_fd.fd = inotify_instance_fd; ik_poll_fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR; g_io_channel_set_encoding (inotify_read_ioc, NULL, NULL); g_io_channel_set_flags (inotify_read_ioc, G_IO_FLAG_NONBLOCK, NULL); source = g_source_new (&ik_source_funcs, sizeof (GSource)); g_source_add_poll (source, &ik_poll_fd); g_source_set_callback (source, ik_read_callback, NULL, NULL); g_source_attach (source, NULL); g_source_unref (source); cookie_hash = g_hash_table_new (g_direct_hash, g_direct_equal); event_queue = g_queue_new (); events_to_process = g_queue_new (); return TRUE;}static ik_event_internal_t *ik_event_internal_new (ik_event_t *event){ ik_event_internal_t *internal_event = g_new0 (ik_event_internal_t, 1); GTimeVal tv; g_assert (event); g_get_current_time (&tv); g_time_val_add (&tv, DEFAULT_HOLD_UNTIL_TIME); internal_event->event = event; internal_event->hold_until = tv; return internal_event;}static ik_event_t *ik_event_new (char *buffer){ struct inotify_event *kevent = (struct inotify_event *)buffer; ik_event_t *event = g_new0 (ik_event_t, 1); g_assert (buffer); event->wd = kevent->wd; event->mask = kevent->mask; event->cookie = kevent->cookie; event->len = kevent->len; if (event->len) event->name = g_strdup (kevent->name); else event->name = g_strdup (""); return event;}ik_event_t *_ik_event_new_dummy (const char *name, gint32 wd, guint32 mask){ ik_event_t *event = g_new0 (ik_event_t, 1); event->wd = wd; event->mask = mask; event->cookie = 0; if (name) event->name = g_strdup (name); else event->name = g_strdup(""); event->len = strlen (event->name); return event;}void_ik_event_free (ik_event_t *event){ if (event->pair) _ik_event_free (event->pair); g_free (event->name); g_free (event);}gint32_ik_watch (const char *path, guint32 mask, int *err){ gint32 wd = -1; g_assert (path != NULL); g_assert (inotify_instance_fd >= 0); wd = inotify_add_watch (inotify_instance_fd, path, mask); if (wd < 0) { int e = errno; /* FIXME: debug msg failed to add watch */ if (err) *err = e; return wd; } g_assert (wd >= 0); return wd;}int_ik_ignore (const char *path, gint32 wd){ g_assert (wd >= 0); g_assert (inotify_instance_fd >= 0); if (inotify_rm_watch (inotify_instance_fd, wd) < 0) { /* int e = errno; */ /* failed to rm watch */ return -1; } return 0;}void_ik_move_stats (guint32 *matches, guint32 *misses){ if (matches) *matches = ik_move_matches; if (misses) *misses = ik_move_misses;}const char *_ik_mask_to_string (guint32 mask){ gboolean is_dir = mask & IN_ISDIR; mask &= ~IN_ISDIR;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -