淺析gowatch監(jiān)聽文件變動實現(xiàn)原理

根據(jù)上圖可知,監(jiān)聽文件的變化主要依賴于linux內(nèi)核的INotify接口機制。Go的標(biāo)準(zhǔn)庫中對其做了實現(xiàn)。而fsnotify package的主要作用就是將進(jìn)一步封裝成watcher結(jié)構(gòu)體和事件類型結(jié)構(gòu)體的封裝,從而實現(xiàn)事件的判斷以及目錄的監(jiān)聽。下面看下 fsnotify package中對watcher的封裝。
type Watcher struct {mu sync.Mutex // Map accessfd int // File descriptor (as returned by the inotify_init() syscall)watches map[string]*watch // Map of inotify watches (key: path)fsnFlags map[string]uint32 // Map of watched files to flags used for filterfsnmut sync.Mutex // Protects access to fsnFlags.paths map[int]string // Map of watched paths (key: watch descriptor)Error chan error // Errors are sent on this channelinternalEvent chan *FileEvent // Events are queued on this channelEvent chan *FileEvent // Events are returned on this channeldone chan bool // Channel for sending a "quit message" to the reader goroutineisClosed bool // Set to true when Close() is first called}
linux內(nèi)核Inotify接口簡介
inotify中主要涉及3個接口。分別是inotify_init, inotify_add_watch,read。具體如下:
| 接口名 | 作用 |
| int fd = inotify_init() | 創(chuàng)建inotify實例,返回對應(yīng)的文件描述符 |
| inotify_add_watch (fd, path, mask) | 注冊被監(jiān)視目錄或文件的事件 |
| read (fd, buf, BUF_LEN) | 讀取監(jiān)聽到的文件事件? |
Inotify可以監(jiān)聽的文件系統(tǒng)事件列表:
| 事件名稱 | 事件說明 |
IN_ACCESS | 文件被訪問 |
IN_MODIFY | 文件被 write |
IN_CLOSE_WRITE | 可寫文件被 close |
IN_OPEN | 文件被 open |
IN_MOVED_TO | 文件被移來,如 mv、cp |
IN_CREATE | 創(chuàng)建新文件 |
IN_DELETE | 文件被刪除,如 rm |
IN_DELETE_SELF | 自刪除,即一個可執(zhí)行文件在執(zhí)行時刪除自己 |
| IN_MOVE_SELF | 自移動,即一個可執(zhí)行文件在執(zhí)行時移動自己 |
| IN_ATTRIB | 文件屬性被修改,如 chmod、chown、touch 等 |
| IN_CLOSE_NOWRITE | 不可寫文件被 close |
| IN_MOVED_FROM | 文件被移走,如 mv |
| IN_UNMOUNT | 宿主文件系統(tǒng)被 umount |
| IN_CLOSE | 文件被關(guān)閉,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) |
| IN_MOVE | 文件被移動,等同于(IN_MOVED_FROM | IN_MOVED_TO) |
示例應(yīng)用
接下來是一個簡易的示例應(yīng)用,具體的應(yīng)用實例可參考github.com/silenceper/gowatch包源代碼?。
主要邏輯如下:
初始化watcher對象
將文件或目錄加入到watcher監(jiān)控對象的隊列
啟動監(jiān)聽協(xié)程,實時獲取文件對象事件
package mainimport ("fmt""github.com/howeyc/fsnotify""runtime")var exit chan boolfunc main() {????//1、初始化監(jiān)控對象watcherwatcher, err := fsnotify.NewWatcher()if err != nil {fmt.Printf("Fail to create new Watcher[ %s ]\n", err)}????//3、啟動監(jiān)聽文件對象事件協(xié)程go func() {fmt.Println("開始監(jiān)聽文件變化")for {select {case e := <-watcher.Event:// 這里添加根據(jù)文件變化的業(yè)務(wù)邏輯fmt.Printf("監(jiān)聽到文件 - %s變化\n", e.Name)if e.IsCreate() {fmt.Println("監(jiān)聽到文件創(chuàng)建事件")}if e.IsDelete() {fmt.Println("監(jiān)聽到文件刪除事件")}if e.IsModify() {fmt.Println("監(jiān)聽到文件修改事件")}if e.IsRename() {fmt.Println("監(jiān)聽到文件重命名事件")}if e.IsAttrib() {fmt.Println("監(jiān)聽到文件屬性修改事件")}fmt.Println("根據(jù)文件變化開始執(zhí)行業(yè)務(wù)邏輯")case err := <-watcher.Error:fmt.Printf(" %s\n", err.Error())}}}()????//?2、將需要監(jiān)聽的文件加入到watcher的監(jiān)聽隊列中paths := []string{"config.yml"}for _, path := range paths {err = watcher.Watch(path) //將文件加入監(jiān)聽if err != nil {fmt.Sprintf("Fail to watch directory[ %s ]\n", err)}}<-exitruntime.Goexit()}
推薦閱讀
評論
圖片
表情
