我们在编写应用程序的过程中,通常会遇到这样的情况,因为某些原因,程序被系统多次或者反复调用,导致程序耗尽系统内存或者出现不可预知的错误,最终影响到程序原本的设计需求,这显然不是我们所希望看到的结果,但是如果在程序内部加上必要的判断,不管系统是否多次调用,都能防止同一时刻多个同样的程序同时运行。
网络上防止程序二次启动的方法大同小异,最常见的便是程序启动时先检测指定文件是否存在,如果不存在则创建一个文件,退出时删除该文件,否则认为该程序已启动,直接退出。但是这样做有一个潜在的问题,万一程序在退出前崩溃了,指定文件还未来得及删除,则程序将无法再次启动。另外一个问题是,有些特定文件系统是只读属性的,压根就无法创建文件,所以这一方案也不可行。
上面那种办法虽然存在一定限制以及潜在的缺陷,但至少是支持大多数平台的,对于跨平台的程序而言比较有优势。如果程序只运行在特定平台,我们也可以使用各平台特有的办法实现。
Linux平台下,我们可以调用popen函数执行ps命令,结合awk、grep跟wc命令统计当前进程中与当前程序名字相同的进程数量,即执行“ps x | awk ‘{print $5}’ | grep “当前程序名” | wc -l”,看得到的结果是否大于1,倘若大于1则说明在当前程序启动以前已经启动过同样的程序了,当前程序作退出处理。这种方法的存在的隐患也很大,必须确保没有同名或名字相似的其他程序存在,也不允许该程序有多种命名方式,否则判断结果将出现误差。
#include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> int main(int argc, char *argv[]) { int i = 0; FILE *stream; char recvBuf[256] = {0}; char cmdBuf[256] = {0}; sprintf(cmdBuf, "ps x | awk '{print $5}' | grep %s | wc -l", argv[0]); stream = popen(cmdBuf, "r"); fread(recvBuf, sizeof(char), sizeof(recvBuf)-1, stream); pclose(stream); if (atoi(recvBuf) > 1) { printf("The number of programs: %s\n", recvBuf); printf("The program %s already exist, so exit!\n", argv[0]); exit(1); } //sleep for test while(1) sleep(10); return 0; }
Windows平台下,大多程序都用CreateMutex方式来限制多开。使用API函数CreateMutex来创建命名互斥对象来实现程序互斥是一个比较通用的方法。对于界面程序,我们可以在IninInstance()函数加入互斥对象,对于后台或者控制台程序,我们可以在main()开始的部分加入互斥对象。
#include <Windows.h> int main(int argc, char* argv[]) { HANDLE hMutex = ::CreateMutex(NULL, FALSE, “MYCOMPANY MYAPP.EXE”); if( (GetLastError() == ERROR_ALREADY_EXISTS) || (GetLastError() == ERROR_ACCESS_DENIED) ) { CloseHanle( hMutex ); printf(“程序已经运行!系统不允许重复运行!\n”); return FALSE; } // main code here return 0; }
Android平台下,因为其采用Linux内核,跟Linux一样的方法便能实现。
以上是网上广为传播的两类最普遍的解决程序多重启动的方法,都在不同程度上存在一定的局限跟隐患。本人想到了一种几乎兼容所有平台且方便使用的方法,那就是在程序启动以后,开始真正的任务之前,先尝试绑定一个系统端口,如果绑定成功,则继续执行其他主要任务,否则端口被其他程序绑定,说明已经有一个同样的程序正在执行,当前程序作退出处理。这样做的坏处是浪费了一个端口,不过这对于大多数业务来说是值得的。需要注意的是,端口默认无法复用,不能人为地设置端口复用,而且该端口应该尽可能取得生僻,以确保不会恰巧被其他程序所占用。
以Linux平台下C语言实现为例,其他平台代码大同小异:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <errno.h> int main(void) { int fd; struct sockaddr_in addr; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return -1; } memset(&addr, 0, sizeof(addr)); addr.sin_family = PF_INET; addr.sin_port = htons(1987); addr.sin_addr.s_addr = INADDR_ANY; if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) < 0) { perror("bind"); printf("The program already exist, so exit!\n"); close(fd); exit(1); } else { while (1) sleep(10); } return 0; }
除非注明,文章均为CppLive 编程在线原创,转载请注明出处,谢谢。