1. 程式人生 > 實用技巧 >基於window的共享日誌模組

基於window的共享日誌模組


1
/************************************************************************/ 2 /* SharedLogging.h 多程序共享日誌模組 */ 3 /************************************************************************/ 4 5 #ifndef __SHARED_LOGGING_H_20200811__ 6 #define
__SHARED_LOGGING_H_20200811__ 7 8 #pragma once 9 10 #ifdef SHAREDLOGGING_EXPORTS 11 #define SHAREDLOGGING_API __declspec(dllexport) 12 #else 13 #define SHAREDLOGGING_API __declspec(dllimport) 14 #endif 15 16 #include <Windows.h> 17 18 #ifdef __cplusplus 19 extern "C" { 20 #endif /* __cplusplus */ 21
22 23 typedef void (*incomingLogging)(void *user_data, wchar_t const *logging_texts, unsigned logging_length); 24 /**< 版本號 */ 25 SHAREDLOGGING_API unsigned SharedLoggingVersion(void); 26 /**< 建立連線 */ 27 SHAREDLOGGING_API HANDLE SharedLoggingConnect(const wchar_t* moudle_name); 28 /**< 斷開連線
*/ 29 SHAREDLOGGING_API int SharedLoggingDisconnect(HANDLE handle); 30 /**< 寫入日誌 */ 31 SHAREDLOGGING_API int SharedLoggingWrite(HANDLE handle, const wchar_t *logging_text, unsigned text_length); 32 /**< 訂閱日誌 */ 33 SHAREDLOGGING_API int SharedLoggingSubscribe(HANDLE handle, incomingLogging cb, void *user_data); 34 /**< 取消訂閱 */ 35 SHAREDLOGGING_API int SharedLoggingUnsubscribe(HANDLE handle); 36 /**< 讀出最後一條日誌 */ 37 SHAREDLOGGING_API int SharedLoggingRead(HANDLE handle, wchar_t *logging, unsigned length); 38 39 #ifdef __cplusplus 40 } 41 #endif /* __cplusplus */ 42 43 #endif //__SHARED_LOGGING_H_20200811__

原始碼,如下:

  1 /************************************************************************/
  2 /* SharedLogging.cpp 多程序共享日誌模組                                                                     */
  3 /************************************************************************/
  4 #include "stdafx.h"
  5 #include "SharedLogging.h"
  6 #include <stdlib.h>
  7 #include <stdio.h>
  8 #include <process.h>
  9 
 10 #define MAX(a,b) ((a>b)?a:b)
 11 #define MIN(a,b) ((a<b)?a:b)
 12 
 13 #define VERSION_MAIN    1
 14 #define VERSION_SUB        0
 15 #define VERSION_NUM        ((unsigned)(VERSION_MAIN << 8)|(VERSION_SUB))
 16 
 17 #define SHARED_LOGGING_NAME            L"SharedLogging"
 18 #define SHARED_LOGGING_MUTEX_NAME    L"SharedLoggingMutex" 
 19                            
 20 #define SHARED_LOGGING_GLOBAL_SIZE        80  
 21 #define SHARED_LOGGING_ITEM_NUM            256 
 22 #define SHARED_LOGGING_ITEM_SIZE        160 // 0x50
 23 #define SHARED_LOGGING_MEMORY_SIZE  ((SHARED_LOGGING_ITEM_NUM * SHARED_LOGGING_ITEM_SIZE) + SHARED_LOGGING_GLOBAL_SIZE)
 24 
 25 
 26 
 27 struct shared_logging_base
 28 {
 29    wchar_t name[16]; /**< write SHARED_LOGGING_NAME to this */
 30    unsigned offsetRead;
 31    HANDLE hMap;
 32    HANDLE hMutex;
 33    LPVOID pBuffer;
 34    unsigned shutdown;
 35    HANDLE hThread;
 36    HANDLE hEvent;
 37    CRITICAL_SECTION csLock;
 38    incomingLogging cb;
 39    void *user_data;
 40 };
 41 typedef struct shared_logging_base sl_base_st;
 42 
 43 struct shared_logging_global
 44 {
 45     unsigned version;
 46     unsigned capacity_items_num; 
 47     unsigned total_items_num;
 48     unsigned write_item_offset;    
 49 };            
 50 typedef struct shared_logging_global sl_global_st;
 51 
 52 #define ALLOC_ONE(type)    ((type *)malloc(sizeof(type))) 
 53 #define EXIT_ASSERT(val)  do{if (!val) exit(1);}while(0)
 54 #define SET_ZERO(ptr, type) (memset(ptr, 0, sizeof(type)))
 55 
 56 static unsigned __stdcall fnQueueHandler(void *arg);
 57 
 58 
 59 static sl_base_st *CreateUserDescription(const wchar_t *name)
 60 {
 61     sl_base_st *base = ALLOC_ONE(sl_base_st);
 62     EXIT_ASSERT(base);
 63     SET_ZERO(base, sl_base_st);    
 64     memcpy(base->name, name, sizeof(base->name)-2); 
 65     return base;
 66 }
 67 
 68 
 69 
 70 static int OpenMapFile(sl_base_st *base)
 71 {
 72     sl_global_st *global = NULL;
 73     LPVOID pBuffer = NULL;                                /**< 共享記憶體指標 */
 74 
 75      HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, 0, SHARED_LOGGING_NAME);
 76      if (!hMap)        /**< 開啟失敗,建立之 */
 77      {
 78         hMap = ::CreateFileMapping(INVALID_HANDLE_VALUE,    /**< 物理檔案控制代碼 */
 79                              NULL,                            /**< 預設安全級別 */
 80                              PAGE_READWRITE,                /**< 可讀可寫 */
 81                              0,                                /**< 高位檔案大小 */
 82                              SHARED_LOGGING_MEMORY_SIZE,    /**< 地位檔案大小 */
 83                              SHARED_LOGGING_NAME);            /**< 共享記憶體名稱 */
 84 
 85         EXIT_ASSERT(hMap);
 86 
 87      }
 88      if (hMap)
 89      {       
 90         /**< 對映物件的一個檢視,得到指向共享記憶體的指標,設定裡面的資料 */
 91         pBuffer = ::MapViewOfFile(hMap,                        /**< 共享記憶體的控制代碼 */
 92                                 FILE_MAP_ALL_ACCESS,        /**< 可讀寫許可 */
 93                                 0,                            /**< 表示檔案對映起始偏移的高32位 */
 94                                 0,                            /**< 表示檔案對映起始偏移的低32位.(64KB對齊不是必須的) */
 95                                 0);                            /**< 指定對映檔案的位元組數 */
 96         EXIT_ASSERT(pBuffer);
 97      }
 98      if (pBuffer)
 99      {
100          global = (sl_global_st *)pBuffer;
101          if (global->version != VERSION_NUM)
102          {
103              global->version = VERSION_NUM; 
104              global->capacity_items_num = SHARED_LOGGING_ITEM_NUM;
105              global->total_items_num = 0;
106              global->write_item_offset = 0;
107          }      
108         base->pBuffer = pBuffer;
109         base->hMap = hMap; 
110         return 0;
111      }    
112     return -1;
113      
114 }         
115 
116 static int OpenMutex(sl_base_st *base)  /**< 共享互斥鎖 */
117 {               
118     HANDLE hMutex;
119     hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE,SHARED_LOGGING_MUTEX_NAME);     
120     if (NULL == hMutex)
121     {                               
122         hMutex = CreateMutex(NULL, FALSE, SHARED_LOGGING_MUTEX_NAME);
123         EXIT_ASSERT(hMutex);   
124     }
125     if (hMutex)
126     {
127         base->hMutex = hMutex;
128         return 0;
129     }
130     else
131     {
132         return -1;
133     }
134 }
135 
136 static int LockMutex(sl_base_st *base)  /**< 共享互斥鎖 */
137 {                
138     DWORD ret = WaitForSingleObject(base->hMutex, INFINITE); 
139     if (ret == WAIT_OBJECT_0)
140     {               
141         return 0;
142     }
143     else if (ret == WAIT_ABANDONED)
144     {          
145         return -1;
146     }
147     else{
148         return -2;
149     }
150 }
151 static int UnlockMutex(sl_base_st *base)  /**< 共享互斥鎖 */
152 {     
153     return (ReleaseMutex(base->hMutex)==TRUE)?0:-1;
154 }
155 
156 static int ReleaseHandle(sl_base_st *base)
157 {
158     if (base)
159     {
160         /**< 解除檔案對映,關閉記憶體對映檔案物件控制代碼 */
161         if (base->pBuffer)
162             UnmapViewOfFile(base->pBuffer);
163 
164         if (base->hMutex)
165             CloseHandle(base->hMutex);
166 
167         if (base->hMap)
168             CloseHandle(base->hMap);
169 
170         free(base);
171         return 0;
172     } 
173     return -1;
174 }
175 
176 static size_t WriteMapFile(sl_base_st *base, void *buffer, size_t length)
177 {
178     sl_global_st *global = (sl_global_st *)base->pBuffer;        /**< 共享記憶體指標 */  
179     size_t wlen = MIN(length, SHARED_LOGGING_ITEM_SIZE);
180 
181     unsigned offset = (global->write_item_offset * SHARED_LOGGING_ITEM_SIZE) + SHARED_LOGGING_GLOBAL_SIZE;
182     memcpy(((unsigned char *)base->pBuffer) + offset, buffer, wlen);
183     if ((global->write_item_offset + 1) >= global->capacity_items_num)
184         global->write_item_offset = 0;
185     else
186         global->write_item_offset += 1;
187 
188     global->total_items_num += 1;
189     return wlen;
190 }
191 static size_t ReadMapFileForNextItem(sl_base_st *base, void *buffer, size_t length)
192 {
193     sl_global_st *global = (sl_global_st *)base->pBuffer;    /**< 共享記憶體指標 */ 
194     size_t wlen = MIN(length, SHARED_LOGGING_ITEM_SIZE);  /**< 最小的長度 */   
195     unsigned offset = ((base->offsetRead) * SHARED_LOGGING_ITEM_SIZE) + SHARED_LOGGING_GLOBAL_SIZE;    
196 
197     memcpy(buffer, ((unsigned char *)base->pBuffer) + offset, wlen);
198     if ((base->offsetRead + 1) >= global->capacity_items_num)
199         base->offsetRead = 0;
200     else
201         base->offsetRead += 1;    
202     return wlen;
203 
204 }
205 static size_t ReadMapFileForLastItem(sl_base_st *base, void *buffer, size_t length)
206 {
207     sl_global_st *global = (sl_global_st *)base->pBuffer;    /**< 共享記憶體指標 */ 
208     size_t wlen = MIN(length, SHARED_LOGGING_ITEM_SIZE);  /**< 最小的長度 */ 
209     
210     unsigned offset = 0;
211     if (global->write_item_offset > 0)     /**< 非首寫位置的話 */ 
212     {
213        offset = ((global->write_item_offset - 1) * SHARED_LOGGING_ITEM_SIZE) + SHARED_LOGGING_GLOBAL_SIZE;     
214     }
215     else if (global->total_items_num > 0)  /**< 若首寫位置的話,則要再加上非零記錄的判斷 */
216     {
217       offset = ((global->total_items_num - 1) * SHARED_LOGGING_ITEM_SIZE) + SHARED_LOGGING_GLOBAL_SIZE;
218     }
219     else
220         return 0;
221 
222     memcpy(buffer, ((unsigned char *)base->pBuffer) + offset, wlen); 
223     return wlen;
224 }
225 
226  
227 
228 static int inline CompareCurrentOffset(sl_base_st *base, unsigned offset)
229 {
230     sl_global_st *global = (sl_global_st *)base->pBuffer;    /**< 共享記憶體指標 */
231     
232     if (global->write_item_offset == offset)
233         return 0;
234     else   
235         return -1;
236 }
237 
238 /**< 版本號 */
239 SHAREDLOGGING_API unsigned SharedLoggingVersion(void)
240 {
241     return VERSION_NUM;
242 }
243 
244 /**< 建立連線 */
245 SHAREDLOGGING_API HANDLE SharedLoggingConnect(const wchar_t* moudle_name)
246 {
247     sl_base_st *base = CreateUserDescription(moudle_name);    
248     
249     if ((!OpenMutex(base)) &&(!OpenMapFile(base)))
250         return (HANDLE)base;
251     else
252     {
253         free(base);
254         return INVALID_HANDLE_VALUE;
255     }
256 }
257 
258 /**< 斷開連線 */
259 SHAREDLOGGING_API int SharedLoggingDisconnect(HANDLE handle)
260 {
261      return ReleaseHandle((sl_base_st *)handle);    
262 }
263 
264 /**< 寫入日誌 */
265 SHAREDLOGGING_API int SharedLoggingWrite(HANDLE handle, const wchar_t *logging, unsigned length)
266 {
267     sl_base_st *base = (sl_base_st *)handle;
268     LockMutex(base);
269     size_t rlen = WriteMapFile(base, (LPVOID)logging, sizeof(wchar_t) * length);
270     UnlockMutex(base);
271     return rlen;  
272 }
273 
274 /**< 讀出最後一條日誌 */
275 SHAREDLOGGING_API int SharedLoggingRead(HANDLE handle, wchar_t *logging, unsigned length)
276 {
277     sl_base_st *base = (sl_base_st *)handle;
278     LockMutex(base);
279     size_t rlen = ReadMapFileForLastItem(base, (LPVOID)logging, sizeof(wchar_t) * length);
280     UnlockMutex(base);
281     return rlen;  
282 }
283 
284 /**< 訂閱 */ 
285 SHAREDLOGGING_API int SharedLoggingSubscribe(HANDLE handle, incomingLogging cb, void *user_data)
286 {
287     sl_base_st *base = (sl_base_st *)handle;
288 
289     //初始化佇列的臨界區
290     InitializeCriticalSection(&base->csLock);
291 
292     //初始化事件訊號
293     HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
294     if (!hEvent)
295     {
296         DeleteCriticalSection(&base->csLock);
297         return -1;
298     }
299 
300     //建立事件佇列的處理執行緒 
301     HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, fnQueueHandler, handle, 0, NULL);
302     if (!hThread)
303     {
304         CloseHandle(hEvent);
305        DeleteCriticalSection(&base->csLock);
306        return -2;
307     }
308     
309     base->hThread = hThread;
310     base->hEvent = hEvent;
311     base->cb = cb;
312     base->user_data = user_data;
313     return 0;    
314 }
315 
316 
317 /**< 取消訂閱 */ 
318 SHAREDLOGGING_API int SharedLoggingUnsubscribe(HANDLE handle)
319 {
320     sl_base_st *base = (sl_base_st *)handle;
321 
322     EnterCriticalSection(&base->csLock);//進入臨界區 
323     base->shutdown = 1;    
324     LeaveCriticalSection(&base->csLock);//退出臨界區
325 
326     SetEvent(base->hEvent);
327     //等待子執行緒結束
328     if (base->hThread){
329         WaitForSingleObject(base->hThread, INFINITE);
330         //一定要記得關閉執行緒控制代碼 
331         CloseHandle(base->hThread);    
332         base->hThread = NULL;
333     }
334     if (base->hEvent){
335         CloseHandle(base->hEvent);
336         base->hEvent = NULL;
337     }
338 
339     DeleteCriticalSection(&base->csLock);  //刪除臨界區
340    return -1;
341 }
342  
343 static unsigned __stdcall fnQueueHandler(void *arg)
344 {
345     sl_base_st *base = (sl_base_st *)arg;
346     CRITICAL_SECTION *pLock = &base->csLock;
347     HANDLE hEvent = base->hEvent; 
348     HANDLE hThread = base->hThread;
349     char buffer[SHARED_LOGGING_ITEM_SIZE];
350     wchar_t *logging = (wchar_t *)buffer;
351 
352     while (true)
353     {
354         while ((base->shutdown == 0)&&(CompareCurrentOffset(base, base->offsetRead) == 0))
355         {
356             LeaveCriticalSection(pLock);//退出臨界區
357             WaitForSingleObject(hEvent, INFINITE);
358             EnterCriticalSection(pLock);//進入臨界區
359         }
360         if (base->shutdown == 1)
361         {
362             LeaveCriticalSection(pLock);//退出臨界區
363             break;
364         }   
365         memset(buffer, 0, SHARED_LOGGING_ITEM_SIZE);
366         LockMutex(base);
367         ReadMapFileForNextItem(base, buffer, SHARED_LOGGING_ITEM_SIZE);
368         UnlockMutex(base);
369         LeaveCriticalSection(pLock);//退出臨界區
370 
371         if (base->cb)     
372             (base->cb)(base->user_data, logging, wcslen(logging));
373 
374         EnterCriticalSection(pLock);//進入臨界區 
375     }     
376 
377     return 0;
378 }    

測試程式碼,如下:

 1 /************************************************************************/
 2 /* Tester.cpp                                                                     */
 3 /************************************************************************/
 4 #include "stdafx.h"
 5 #include "SharedLogging.h"
 6 
 7 #define SAY_HELLO L"Hello world!"
 8 #define SAY_GOOD L"Good world!"
 9 #define SAY_BYE L"Bye world!"
10 
11 void cbLogging(void *user_data, wchar_t const*logging_texts, unsigned logging_length)
12 {
13     wprintf(L"text : %s \n", logging_texts);
14 }
15 
16 int _tmain(int argc, _TCHAR* argv[])
17 {
18     wchar_t buffer[256];
19     int rlen = 0;
20 
21     printf("Version : 0x%x\n", SharedLoggingVersion());
22     
23     HANDLE handle = SharedLoggingConnect(L"Tester");  
24     
25     SharedLoggingSubscribe(handle, cbLogging, handle);
26 
27     for (size_t i=0; i<10; ++i)
28     {
29         SharedLoggingWrite(handle, SAY_HELLO, wcslen(SAY_HELLO)); 
30           SharedLoggingWrite(handle, SAY_GOOD, wcslen(SAY_GOOD)); 
31         SharedLoggingWrite(handle, SAY_BYE, wcslen(SAY_BYE)); 
32     }    
33 
34     getchar();
35 
36     return 0;
37 }

輸出效果,如下:

 1 Version : 0x100
 2 text : Hello world!
 3 text : Good world!
 4 text : Bye world!
 5 text : Hello world!
 6 text : Good world!
 7 text : Bye world!
 8 text : Hello world!
 9 text : Good world!
10 text : Bye world!
11 text : Hello world!
12 text : Good world!
13 text : Bye world!
14 text : Hello world!
15 text : Good world!
16 text : Bye world!
17 text : Hello world!
18 text : Good world!
19 text : Bye world!
20 text : Hello world!
21 text : Good world!
22 text : Bye world!
23 text : Hello world!
24 text : Good world!
25 text : Bye world!
26 text : Hello world!
27 text : Good world!
28 text : Bye world!
29 text : Hello world!
30 text : Good world!
31 text : Bye world!

關鍵要點:

1、互斥鎖與共享記憶體檔案的名稱,不能相同,否則後者會報告(錯誤6)。

2、臨界區與互斥鎖,需要深入理解,前者是保護該程序訂閱功能在多執行緒環境中也能使用;後者則是為了在多個程序中的應用互斥。

3、共享空間的首部結構,是為了儲存它的全域性變數,比如,最大容量、當前總數量,寫入偏移等,往後還可以追加其他。

4、每個日誌條目,都是固定長度,有點浪費空間。可以優化成動態長度,把全域性變數的“寫入偏移”這個變數,替換成“寫指標”即可。

5、超過最大容量的時候,會覆蓋最前面的內容。