小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

運(yùn)行單個(gè)實(shí)例

 feiyacz 2012-11-13

摘自: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)了。<注意下面的程序都是分塊拷貝的>

 

方法一:
我用得做多的方法是創(chuàng)建互斥體Mutex,使用Mutex代碼比較簡潔,但是此時(shí)不能取得已經(jīng)啟動(dòng)的實(shí)例窗口局柄,因此無法激活已經(jīng)啟動(dòng)的實(shí)例窗口,代碼如下:
// -------------------------------------------------------------------------
// 函數(shù)  : CreateSendingWNDList
// 功能  : 創(chuàng)建互斥量,用于保證只啟動(dòng)一個(gè)進(jìn)程
// 返回值 : int 
//            成功   0
//     失敗   -1
//     存在進(jìn)程實(shí)例 1
// 附注  : 
// -------------------------------------------------------------------------
int CreateSendingWNDList(const TCHAR *pstrKSCoreAppName)
{
 //-------防止多次起動(dòng)----------  
 HANDLE hMutex = ::CreateMutex(0, true, pstrKSCoreAppName);
 int nRet = 0;
 if (hMutex)
 { 
  if(GetLastError() == ERROR_ALREADY_EXISTS) 
  {
   nRet = 1;
  }
  else
  {
   nRet = 0;
  }
 }
 else
 {
  nRet = -1;
 }

 return nRet;
}

// 在創(chuàng)建窗口前調(diào)用下面代碼
switch(CreateSendingWNDList(g_strKSCoreAppName))
{
case 0: 
 // 正常啟動(dòng)
 // TODO……

 break;  
case 1:  
 // 已存在進(jìn)程,退出
 {
  ::MessageBox(NULL, TEXT("已經(jīng)有一個(gè)實(shí)例在運(yùn)行了。"), TEXT("注意"), MB_OK);
 }

case -1:// 無法創(chuàng)建,退出
default: 
 return FALSE;
}

 

方法二:
一般來說,使程序只運(yùn)行一個(gè)實(shí)例的最簡單的方法當(dāng)然是使用FindWindow()查找主窗口,如果主窗口已經(jīng)存在了,當(dāng)然說明已經(jīng)有一個(gè)實(shí)例運(yùn)行了。代碼如下:
// 這種方法有缺陷,窗口名字改變之后就再也找不到了,F(xiàn)indWindow()的參數(shù)ClassName和Caption比較難取得。
HWND hWnd = FindWindow(NULL, TEXT("SingleInstanceFW"));
if(IsWindow(hWnd))
{
 ::MessageBox(NULL, TEXT("已經(jīng)有一個(gè)實(shí)例在運(yùn)行了。"), TEXT("注意"), MB_OK);
 ::ShowWindow(hWnd, SW_NORMAL);     // 顯示
 ::SetForegroundWindow(hWnd);       // 激活
 return FALSE;
}

 

方法三:

這種方法相比上面兩種方法,避免上面兩種方法的缺點(diǎn),通過SetProp()為程序主窗口設(shè)置一個(gè)特殊的Property,然后在啟動(dòng)時(shí)遍歷所有的窗口,找出包含著個(gè)Property的窗口局柄

?!具@個(gè)附加的窗口屬性在窗口銷毀時(shí)也應(yīng)該銷毀】這個(gè)方法的缺點(diǎn)就是代碼比較多一點(diǎn),如下:

// 聲明全局的 屬性 名和 屬性值
TCHAR g_strKSCoreAppName[] = _T("AFX_KSInstall_CPP__12036F8B_8301_46e2_ADC5_A14A44A85877__");
HANDLE g_hValue = (HANDLE)1022;

// 定義枚舉窗口回調(diào)函數(shù)
BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lParam)
{
 //TCHAR str[200] = {0};
 //::GetWindowText(hwnd, str, 200);
 HANDLE h = GetProp(hwnd, g_strKSCoreAppName);
 if(h == g_hValue)
 {
  *(HWND*)lParam = hwnd;
  return FALSE;
 }
 return TRUE;
}

// 主窗口創(chuàng)建前判斷
HWND oldHWnd = NULL;
::EnumWindows(EnumWndProc,(LPARAM)&oldHWnd);    //枚舉所有運(yùn)行的窗口
if (oldHWnd != NULL)
{
 ::MessageBox(NULL, TEXT("已經(jīng)有一個(gè)實(shí)例在運(yùn)行了。"), TEXT("注意"), MB_OK);

 ::ShowWindow(oldHWnd, SW_NORMAL);     // 顯示
 ::SetForegroundWindow(oldHWnd);       // 激活
 return FALSE;
}

// 主窗口創(chuàng)建后設(shè)置,為窗口附加一個(gè)屬性
::SetProp(m_hWnd, g_strKSCoreAppName, g_hValue);

// 主窗口退出時(shí)移除該附加屬性
::RemoveProp(m_hWnd, g_strKSCoreAppName);

 

方法四:

上面的方法二和方法三都有一個(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") 
int volatile g_lAppInstance = 0; 
#pragma data_seg() 
#pragma comment(linker,"/section:Shared,RWS")

if (0 == g_lAppInstance)
{
 g_lAppInstance = 1;
}
else if (1 == g_lAppInstance)
{
 ::MessageBox(NULL, TEXT("已經(jīng)有一個(gè)實(shí)例在運(yùn)行了。"), TEXT("注意"), MB_OK);
 return FALSE;
}
else
{
 // 直接啟動(dòng)
}

【注意,代碼應(yīng)該放在程序的入口處】

其實(shí)上面的方法可以兩種進(jìn)行組合來實(shí)現(xiàn)一些比較特殊的需求,具體怎樣就自己去想了。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多