|
摘自:http://blog.chinaunix.net/space.php?uid=20196318;http://blog.csdn.net/magictong/article/details/3603015 對(duì)于很多服務(wù)來說,在同一個(gè)服務(wù)器上只能運(yùn)行一個(gè)實(shí)例,那么通過什么方法來保證程序同一時(shí)刻只有一個(gè)實(shí)例運(yùn)行呢?通過編寫shell腳本來管理程序的啟動(dòng)、停止是個(gè)不錯(cuò)的方法。在啟動(dòng)時(shí),shell腳本會(huì)創(chuàng)建進(jìn)程標(biāo)識(shí)文件(存儲(chǔ)正在運(yùn)行實(shí)例的pid)以表明已經(jīng)有實(shí)例在運(yùn)行,如果文件已存在,則說明已有實(shí)例在運(yùn)行,不需要做任何事;在退出時(shí),shell腳本會(huì)刪除進(jìn)程標(biāo)識(shí)文件,表明沒有實(shí)例運(yùn)行。 shell腳本管理方法在應(yīng)用程序之上再包了一層,那么能不能直接在程序開始運(yùn)行時(shí)自己判斷是否有實(shí)例在運(yùn)行呢,答案是肯定的。原理其實(shí)差不多,還是要借助公用資源---文件,當(dāng)然不僅僅是文件而已,還需要文件鎖的支持。大致思路是這樣的:程序在開始運(yùn)行時(shí)對(duì)特定文件進(jìn)行加鎖(不存在則創(chuàng)建),如果加鎖成功,則實(shí)例開始運(yùn)行;如鎖已經(jīng)被占有,則說明已經(jīng)有實(shí)例在運(yùn)行,則程序直接退出;另外在實(shí)例運(yùn)行完畢后對(duì)文件的鎖也隨著丟掉了。這樣就能保證每次只有一個(gè)程序?qū)嵗谶\(yùn)行。 具體步驟如下: 1. 打開特定文件(如/var/run/mydaemon.pid),如不存在則創(chuàng)建之; 2. 使用fcntl對(duì)文件整個(gè)區(qū)域加勸告鎖。 3. 如果加鎖成功,則繼續(xù)執(zhí)行后續(xù)代碼,并將pid寫入文件;如加鎖不成功,說明已經(jīng)有實(shí)例在運(yùn)行,直接退出。 實(shí)現(xiàn)示例: #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <printf.h> #include <string.h> #include <errno.h> #include <sys/stat.h> #define LOCKFILE "/var/run/mydaemon.pid" #define LOCKMODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) /* set advisory lock on file */ int lockfile(int fd) { struct flock fl;
fl.l_type = F_WRLCK; /* write lock */ fl.l_start = 0; fl.l_whence = SEEK_SET; fl.l_len = 0; //lock the whole file
return(fcntl(fd, F_SETLK, &fl)); } int already_running(const char *filename) { int fd; char buf[16];
fd = open(filename, O_RDWR | O_CREAT, LOCKMODE); if (fd < 0) { printf(LOG_ERR, "can't open %s: %m\n", filename); exit(1); }
/* 先獲取文件鎖 */ if (lockfile(fd) == -1) { if (errno == EACCES || errno == EAGAIN) { printf(LOG_ERR, "file: %s already locked", filename); close(fd); return 1; } printf(LOG_ERR, "can't lock %s: %m\n", filename); exit(1); } /* 寫入運(yùn)行實(shí)例的pid */ ftruncate(fd, 0); sprintf(buf, "%ld", (long)getpid()); write(fd, buf, strlen(buf) + 1); return 0; } int main(int argc, char *argv[]) { if (already_running(LOCKFILE)) return 0; /* 在這里添加工作代碼 */ printf("start main...\n"); sleep(100); printf("main done!\n"); exit(0); } 綜述:讓一個(gè)程序只運(yùn)行一個(gè)實(shí)例的方法有多種,但是原理都類似,也就是在程序創(chuàng)建前,有窗口的程序在窗口創(chuàng)建前,檢查系統(tǒng)中是否已經(jīng)設(shè)置了某些特定標(biāo)志了,如果有說明已經(jīng)有一個(gè)實(shí)例在運(yùn)行了,則當(dāng)前程序通知用戶怎樣怎樣,然后程序退出,當(dāng)然方法有這么多,各自也就有自己的優(yōu)缺點(diǎn)了。<注意下面的程序都是分塊拷貝的> 方法一: return nRet; // 在創(chuàng)建窗口前調(diào)用下面代碼 break; case -1:// 無法創(chuàng)建,退出 方法二: 方法三: 這種方法相比上面兩種方法,避免上面兩種方法的缺點(diǎn),通過SetProp()為程序主窗口設(shè)置一個(gè)特殊的Property,然后在啟動(dòng)時(shí)遍歷所有的窗口,找出包含著個(gè)Property的窗口局柄 ?!具@個(gè)附加的窗口屬性在窗口銷毀時(shí)也應(yīng)該銷毀】這個(gè)方法的缺點(diǎn)就是代碼比較多一點(diǎn),如下: // 聲明全局的 屬性 名和 屬性值 // 定義枚舉窗口回調(diào)函數(shù) // 主窗口創(chuàng)建前判斷 ::ShowWindow(oldHWnd, SW_NORMAL); // 顯示 // 主窗口創(chuàng)建后設(shè)置,為窗口附加一個(gè)屬性 // 主窗口退出時(shí)移除該附加屬性 方法四: 上面的方法二和方法三都有一個(gè)弊病,不知道大家發(fā)現(xiàn)沒,那就是依賴于窗口的存在,沒有窗口的程序怎么辦了,用方法一是可以的,不過方法一不太適合即時(shí)修改狀態(tài),譬如我想提供選項(xiàng)給用戶,可以即時(shí)修改是否允許多實(shí)例,像KMP就提供了即時(shí)修改是否允許多實(shí)例,使用全局變量是一個(gè)比較好的解決方案,使用全局共享變量的方法則主要是在VC框架程序中通過編譯器來實(shí)現(xiàn)的。通過#pragma data_seg預(yù)編譯指令創(chuàng)建一個(gè)新節(jié),在此節(jié)中可用volatile關(guān)鍵字定義一個(gè)變量,而且必須對(duì)其進(jìn)行初始化。Volatile關(guān)鍵字指定了變量可以為外部進(jìn)程訪問。最后,為了使該變量能夠在進(jìn)程互斥過程中發(fā)揮作用,還要將其設(shè)置為共享變量,同時(shí)允許具有讀、寫訪問權(quán)限。這可以通過#pragma comment預(yù)編譯指令來通知編譯器。下面給出使用了全局變量的進(jìn)程互斥代碼清單: #pragma data_seg("Shared") if (0 == g_lAppInstance) 【注意,代碼應(yīng)該放在程序的入口處】 其實(shí)上面的方法可以兩種進(jìn)行組合來實(shí)現(xiàn)一些比較特殊的需求,具體怎樣就自己去想了。 |
|
|