XenStore:使用,結構和原理(4. 監視: xs_watch)

來源:互聯網
上載者:User

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?
  1. static struct xenbus_watch xb_watch = {  
  2.     .node = "memory",  
  3.     .callback = watch_target;  
  4. };  
  5. ret = register_xenbus_watch(&xb_watch);  
  6.     if(IS_ERR(ret)) {  
  7.       IPRINTK("Failed to initialize balloon watcher\n");  
  8.     } else {  
  9.       IPRINTK("Balloon xenbus watcher initialized\n");  
  10.     }  

使用者空間:使用者空間和核心稍有不同。使用者空間可以用xs_watch函數定義一個監視,但是不能指定回呼函數,而是需要調用xs_read_watch進行阻塞式的讀取,像這樣:

[cpp]
view plaincopyprint?
  1. xs_handle *xh=xs_open(0);  
[cpp]
view plaincopyprint?
  1. assert(xs_watch(xh, path, token));  
  2. char **result = 0;  
  3. result = xs_read_watch(xh, &num);             //會阻塞  
  4. free(result);  
  5. 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?
  1. char **xs_read_watch(struct xs_handle *h, unsigned int *num)  
  2. {  
  3.         struct xs_stored_msg *msg;  
  4.         char **ret, *strings, c = 0;  
  5.         unsigned int num_strings, i;  
  6.   
  7.         mutex_lock(&h->watch_mutex);                 //------------------>加鎖  
  8.                                               //此處應該加一個pthread_cleanup_push(pthread_mutex_unlock, &h->watch_mutex)  
  9. #ifdef USE_PTHREAD  
  10.         /* Wait on the condition variable for a watch to fire. 
  11.          * If the reader thread doesn't exist yet, then that's because 
  12.          * we haven't called xs_watch.  Presumably the application 
  13.          * will do so later; in the meantime we just block. 
  14.          */  
  15.         while (list_empty(&h->watch_list) && h->fd != -1)  
  16.                 condvar_wait(&h->watch_condvar, &h->watch_mutex);  
  17. #else /* !defined(USE_PTHREAD) */  
  18.         /* Read from comms channel ourselves if there are no threads 
  19.          * and therefore no reader thread. */  
  20.   
  21.         assert(!read_thread_exists(h)); /* not threadsafe but worth a check */  
  22.         if ((read_message(h) == -1))                //----------------------->沒有訊息時阻塞在這裡  
  23.                 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?
  1. //used to extract (>_<) xs_handle internal members.  
  2. typedef struct list_head {  
  3.         struct list_head *next, *prev;  
  4. }list_head_struct;  
  5. typedef struct  
  6. {  
  7.     int fd;  
  8.     pthread_t read_thr;  
  9.     int read_thr_exists;  
  10.     struct list_head watch_list;  
  11.     pthread_mutex_t watch_mutex;//監視訊號量,可能死結  
  12.     pthread_cond_t watch_condvar;  
  13.     int watch_pipe[2];  
  14.     struct list_head reply_list;  
  15.     pthread_mutex_t reply_mutex;//回複訊號量  
  16.     pthread_cond_t reply_condvar;  
  17.     pthread_mutex_t request_mutex;//請求訊號量  
  18. }my_xs_handle;  
  19.   
  20. #if __GNUC__ > 3  
  21. #define offsetof(a,b) __builtin_offsetof(a,b)//提取位移量的宏。  
  22. #else  
  23. #define offsetof(a,b) ((unsigned long)&(((a *)0)->b))  
  24. #endif  

然後像這樣提取xh內部成員:

pthread_mutex_t *pm_watch = (pthread_mutex_t *)(((void *)xh) + offsetof(my_xs_handle, watch_mutex));

解鎖的宏。其實寫成個函數也行

[cpp]
view plaincopyprint?
  1. #define check_xh_lock(x) do {\  
  2.         pthread_mutex_t *pm = (pthread_mutex_t *)(((void *)pthis->xh) + offsetof(my_xs_handle, x));  \  
  3.         if (pthread_mutex_trylock(pm) == EBUSY){            \           //pthread_mutex_trylock 如果沒上鎖,則加鎖;否則返回EBUSY  
  4.             cout << "thread_cleanup -> " #x " is already locked!" << endl;   \  
  5.             if (0 != pthread_mutex_unlock(pm))              \         
  6.             cout << "thread_cleanup -> error unlocking!" << endl;            \  
  7.             else cout << "thread_cleanup -> unlocking " #x << endl;          \  
  8.         } else assert(pthread_mutex_unlock(pm)==0);             \  
  9.     } while (0)  
[cpp]
view plaincopyprint?
  1. check_xh_lock(watch_mutex);  
  2. check_xh_lock(request_mutex);  
  3. check_xh_lock(reply_mutex);  
  4. cout << "----- unwatch -----" << endl;  
  5. xs_unwatch(pthis->xh, pthis->path.c_str(), map_path("watch", pthis->path).c_str());  
  6.   
  7. check_xh_lock(watch_mutex);  
  8. check_xh_lock(request_mutex);  
  9. check_xh_lock(reply_mutex);  
  10. cout << "-----  close  -----" << endl;  
  11. xs_close(pthis->xh);  
  12. pthis->xh=0;  

測試如下:

[plain]
view plaincopyprint?
  1. viktor@buxiang-OptiPlex-330:~/proj/xc_map$ sudo ./domu domu.cpp  
  2. watch -> add watch on path mmap/domu-cpp callback func 0x8049aad                       //主線程開啟監視線程  
  3.   
  4. thread_func -> begin watch thread path= mmap/domu-cpp                                  //監視線程註冊watch  
  5. thread_func -> watch event path= mmap/domu-cpp token= watch/mmap/domu-cpp            //註冊時會發生一次事件,忽略之  
  6. watch_callback -> entering  
  7.                                          //(在這裡等待,此時按下Ctrl+C)  
  8. ^Cmain received signal Interrupt  
  9. unwatch -> **stopping work thread. waiting here...work thread already stopped.          //主線程用pthread_cancel關閉監視線程  
  10. thread_cleanup -> path= mmap/domu-cpp thread=b6d6eb70                                    //監視線程退出,主線程開始清理  
  11. ----- unwatch -----  
  12. map_path -> watch/mmap/domu-cpp  
  13. thread_cleanup -> watch_mutex is already locked!                                   //此時watch_mutex上鎖了,說明xs_read_watch沒有釋放資源  
  14. thread_cleanup -> unlocking watch_mutex                                          //強行解鎖  
  15. -----  close  -----                 //調用xs_unwatch和xs_close關閉串連。 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.