Xenstore的監視(watch)功能很實用,在xenstore監視目標檔案夾裡發生的任何修改,都會通知watch的註冊者。xen虛擬機器的後端驅動程式,就是通過watch來檢測前端裝置的改變。
需要注意的:
(1)註冊watch不需要開始一個transaction,只要用xs_open開啟串連就行了。核心可以直接調用register_xenbus_watch
(2)watch在註冊時,xenstored會馬上產生一個事件。它就是這麼設計的。如果不想處理這個時間,需要設法把它忽略掉。
watch的用法:
核心空間:(轉自http://wiki.xen.org/xenwiki/XenBus)
[cpp]
view plaincopyprint?
- static struct xenbus_watch xb_watch = {
- .node = "memory",
- .callback = watch_target;
- };
- ret = register_xenbus_watch(&xb_watch);
- if(IS_ERR(ret)) {
- IPRINTK("Failed to initialize balloon watcher\n");
- } else {
- IPRINTK("Balloon xenbus watcher initialized\n");
- }
使用者空間:使用者空間和核心稍有不同。使用者空間可以用xs_watch函數定義一個監視,但是不能指定回呼函數,而是需要調用xs_read_watch進行阻塞式的讀取,像這樣:
[cpp]
view plaincopyprint?
- xs_handle *xh=xs_open(0);
[cpp]
view plaincopyprint?
- assert(xs_watch(xh, path, token));
- char **result = 0;
- result = xs_read_watch(xh, &num); //會阻塞
- free(result);
- xs_unwatch(xh, path, token);
如果不想阻塞,就得自己開一個線程用來監聽。但是這裡有一個bug(4.1.3版本xen):xs_read_watch不是安全執行緒的。如果在xs_read_watch阻塞的時候,使用pthread_cancel來終止線程,xs_read_watch不會釋放所有的資源(鎖)。而主線程如果調用xs_close關閉與xenstore的串連,則會因為不能得到這些資源而死結。具體分析如下:
tools/xenstore/xs.c
[cpp]
view plaincopyprint?
- char **xs_read_watch(struct xs_handle *h, unsigned int *num)
- {
- struct xs_stored_msg *msg;
- char **ret, *strings, c = 0;
- unsigned int num_strings, i;
-
- mutex_lock(&h->watch_mutex); //------------------>加鎖
- //此處應該加一個pthread_cleanup_push(pthread_mutex_unlock, &h->watch_mutex)
- #ifdef USE_PTHREAD
- /* Wait on the condition variable for a watch to fire.
- * If the reader thread doesn't exist yet, then that's because
- * we haven't called xs_watch. Presumably the application
- * will do so later; in the meantime we just block.
- */
- while (list_empty(&h->watch_list) && h->fd != -1)
- condvar_wait(&h->watch_condvar, &h->watch_mutex);
- #else /* !defined(USE_PTHREAD) */
- /* Read from comms channel ourselves if there are no threads
- * and therefore no reader thread. */
-
- assert(!read_thread_exists(h)); /* not threadsafe but worth a check */
- if ((read_message(h) == -1)) //----------------------->沒有訊息時阻塞在這裡
- return NULL;
xs_read_watch一開始加鎖,緊接著調用read_message。read_message會阻塞調用read函數。如果這時主線程調用pthread_cancel來取消運行xs_read_watch的線程,h->watch_mutex就不會被釋放,會造成後面的死結。
很挫的解決辦法:在調用xs_unwatch和xs_close之前,檢查xs_handle內部鎖的情況,強行解鎖資源。
xs_handle這個結構體在標頭檔裡只有型別宣告,具體的定義在xs.c檔案裡,僅供xenstore內部使用。要調用xs_handle內部的對象,首先得把xs_handle的詳細聲明提取出來:
[cpp]
view plaincopyprint?
- //used to extract (>_<) xs_handle internal members.
- typedef struct list_head {
- struct list_head *next, *prev;
- }list_head_struct;
- typedef struct
- {
- int fd;
- pthread_t read_thr;
- int read_thr_exists;
- struct list_head watch_list;
- pthread_mutex_t watch_mutex;//監視訊號量,可能死結
- pthread_cond_t watch_condvar;
- int watch_pipe[2];
- struct list_head reply_list;
- pthread_mutex_t reply_mutex;//回複訊號量
- pthread_cond_t reply_condvar;
- pthread_mutex_t request_mutex;//請求訊號量
- }my_xs_handle;
-
- #if __GNUC__ > 3
- #define offsetof(a,b) __builtin_offsetof(a,b)//提取位移量的宏。
- #else
- #define offsetof(a,b) ((unsigned long)&(((a *)0)->b))
- #endif
然後像這樣提取xh內部成員:
pthread_mutex_t *pm_watch = (pthread_mutex_t *)(((void *)xh) + offsetof(my_xs_handle, watch_mutex));
解鎖的宏。其實寫成個函數也行
[cpp]
view plaincopyprint?
- #define check_xh_lock(x) do {\
- pthread_mutex_t *pm = (pthread_mutex_t *)(((void *)pthis->xh) + offsetof(my_xs_handle, x)); \
- if (pthread_mutex_trylock(pm) == EBUSY){ \ //pthread_mutex_trylock 如果沒上鎖,則加鎖;否則返回EBUSY
- cout << "thread_cleanup -> " #x " is already locked!" << endl; \
- if (0 != pthread_mutex_unlock(pm)) \
- cout << "thread_cleanup -> error unlocking!" << endl; \
- else cout << "thread_cleanup -> unlocking " #x << endl; \
- } else assert(pthread_mutex_unlock(pm)==0); \
- } while (0)
[cpp]
view plaincopyprint?
- check_xh_lock(watch_mutex);
- check_xh_lock(request_mutex);
- check_xh_lock(reply_mutex);
- cout << "----- unwatch -----" << endl;
- xs_unwatch(pthis->xh, pthis->path.c_str(), map_path("watch", pthis->path).c_str());
-
- check_xh_lock(watch_mutex);
- check_xh_lock(request_mutex);
- check_xh_lock(reply_mutex);
- cout << "----- close -----" << endl;
- xs_close(pthis->xh);
- pthis->xh=0;
測試如下:
[plain]
view plaincopyprint?
- viktor@buxiang-OptiPlex-330:~/proj/xc_map$ sudo ./domu domu.cpp
- watch -> add watch on path mmap/domu-cpp callback func 0x8049aad //主線程開啟監視線程
-
- thread_func -> begin watch thread path= mmap/domu-cpp //監視線程註冊watch
- thread_func -> watch event path= mmap/domu-cpp token= watch/mmap/domu-cpp //註冊時會發生一次事件,忽略之
- watch_callback -> entering
- //(在這裡等待,此時按下Ctrl+C)
- ^Cmain received signal Interrupt
- unwatch -> **stopping work thread. waiting here...work thread already stopped. //主線程用pthread_cancel關閉監視線程
- thread_cleanup -> path= mmap/domu-cpp thread=b6d6eb70 //監視線程退出,主線程開始清理
- ----- unwatch -----
- map_path -> watch/mmap/domu-cpp
- thread_cleanup -> watch_mutex is already locked! //此時watch_mutex上鎖了,說明xs_read_watch沒有釋放資源
- thread_cleanup -> unlocking watch_mutex //強行解鎖
- ----- close ----- //調用xs_unwatch和xs_close關閉串連。