生活随笔
收集整理的這篇文章主要介紹了
linux下程序如何实现单实例运行
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
1、技術(shù)原理
無論是windows還是linux下,程序設(shè)計(jì)者都會(huì)遇到一個(gè)問題,那就是如何實(shí)現(xiàn)程序的單實(shí)例運(yùn)行。比如,Windows自帶的播放軟件Windows Medea Player只能啟動(dòng)一個(gè)實(shí)例。原因很簡(jiǎn)單,如果同時(shí)啟動(dòng)幾個(gè)實(shí)例,卻播放不同的文件,那么聲音和圖像就會(huì)引起混亂。所以,我們要做的就是,程序啟動(dòng)時(shí)檢測(cè)一下系統(tǒng)中是否已經(jīng)存在一個(gè)完全一樣的實(shí)例,如果已經(jīng)存在,則本次啟動(dòng)的程序自動(dòng)退出。那么,從編碼的角度來分析,該如何實(shí)現(xiàn)這個(gè)效果呢?在linux系統(tǒng)中,我們一般有以下幾種方法:
shell使用ps命令來判斷。最常見也是最簡(jiǎn)單的方案,直接寫一個(gè)特定的shell腳本來實(shí)現(xiàn)。不過存在可移植性差的問題,進(jìn)程名字一旦改變之后,腳本就失去作用無法監(jiān)控。同時(shí),還要額外附加這個(gè)腳本文件在系統(tǒng)中運(yùn)行,實(shí)在有點(diǎn)多余;信號(hào)量/共享內(nèi)存。使用共享內(nèi)存,通過shmget操作共享內(nèi)存,然后寫入pid,這樣就不用生成可見的文件。這個(gè)方案只存在一個(gè)很小的缺陷,需要配置共享內(nèi)存的key,并保證不與系統(tǒng)其他應(yīng)用沖突,一般來說,沖突概率非常小;端口搶占。應(yīng)用于大多數(shù)的Linux網(wǎng)絡(luò)應(yīng)用。思路是系統(tǒng)保證每個(gè)端口的TCP只能有一個(gè)進(jìn)程監(jiān)聽,那么如果程序啟動(dòng)時(shí),監(jiān)聽一個(gè)核心的端口,第二個(gè)運(yùn)行的實(shí)例就會(huì)監(jiān)聽失敗,無法啟動(dòng)。這個(gè)方案同樣很有效,省去了一個(gè)額外的配置文件,不足之處是一般只用于帶網(wǎng)絡(luò)的程序;創(chuàng)建文件,加鎖(建議性鎖)。這種做法最常見了,應(yīng)用于大多數(shù)的Linux程序,如apache httpd, mysql。思路是配置一個(gè)pid文件,當(dāng)程序啟動(dòng)時(shí),對(duì)pid文件加鎖,然后寫入本進(jìn)程的pid,如果鎖失敗,說明有實(shí)例已經(jīng)啟動(dòng)了。這個(gè)方案非??煽?#xff0c;唯一的不足是需要配置一個(gè)pid文件,并且保證文件目錄和文件可寫;進(jìn)程列表檢測(cè)。對(duì)于運(yùn)維常用的方法。由于運(yùn)維不一定能控制程序的修改,所以考慮從外部解決。crontab腳本,查詢運(yùn)行的進(jìn)程數(shù),一旦發(fā)現(xiàn)進(jìn)程數(shù)與預(yù)期不符,那么killall,重啟進(jìn)程。這個(gè)方案是旁路方案,比上面的方式更通用,還可以監(jiān)視進(jìn)程數(shù),避免某些子進(jìn)程core。這個(gè)方案沒有什么缺陷,如果硬要找一個(gè)的話,不同的系統(tǒng)ps命令輸出可能不一樣,腳本需要考慮移植。
2、源碼實(shí)現(xiàn)
經(jīng)過一番對(duì)比,優(yōu)選其中的方案4,也是最大眾化的一個(gè)方案。下面就是詳細(xì)的代碼實(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>
static int lockfile(
int fd)
{
struct flock fl;fl.l_type = F_WRLCK;fl.l_start =
0;fl.l_whence = SEEK_SET;fl.l_len =
0;
return(fcntl(fd, F_SETLK, &fl));
}
int proc_is_exist(
const char *procname)
{
int fd;
char buf[
16];
char filename[
100];
sprintf(filename,
"/var/run/%s.pid", procname);fd = open(filename, O_RDWR | O_CREAT, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
if (fd <
0) {
printf(
"open file \"%s\" failed!!!\n", filename);
return 1;}
if (lockfile(fd) == -
1) {
printf(
"file \"%s\" locked. proc already exit!!!\n", filename);close(fd);
return 1;}
else {ftruncate(fd,
0);
sprintf(buf,
"%ld", (
long)getpid());write(fd, buf,
strlen(buf) +
1);
return 0;}
}
#ifndef SINGLE_INSTANCE_H
#define SINGLE_INSTANCE_H#ifdef __cplusplus
extern "C"
{
#endifint proc_is_exist(
const char *procname);
#ifdef __cplusplus
}
#endif#endif
3、使用說明
調(diào)用者只需在程序啟動(dòng)時(shí)調(diào)用本函數(shù),根據(jù)返回值進(jìn)行判斷即可:
if (proc_is_exist(g_fk_process_name) == TRUE) { print_sys(
"an \"%s\" already running in system. exit now...\n", g_fk_process_name);
return 0;
}
else {print_sys(
"\"%s\" starting...\n", g_fk_process_name);
}
總結(jié)
以上是生活随笔為你收集整理的linux下程序如何实现单实例运行的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。