Linux 如何在 C 或 C++ 中创建单实例应用程序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5339200/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to create a single instance application in C or C++
提问by whoi
What would be your suggestion in order to create a single instance application, so that only one process is allowed to run at a time? File lock, mutex or what?
为了创建单实例应用程序,以便一次只允许运行一个进程,您有什么建议?文件锁,互斥锁还是什么?
采纳答案by Maxim Egorushkin
A good way is:
一个好方法是:
#include <sys/file.h>
#include <errno.h>
int pid_file = open("/var/run/whatever.pid", O_CREAT | O_RDWR, 0666);
int rc = flock(pid_file, LOCK_EX | LOCK_NB);
if(rc) {
if(EWOULDBLOCK == errno)
; // another instance is running
}
else {
// this is the first instance
}
Note that locking allows you to ignore stale pid files (i.e. you don't have to delete them). When the application terminates for any reason the OS releases the file lock for you.
请注意,锁定允许您忽略陈旧的 pid 文件(即您不必删除它们)。当应用程序因任何原因终止时,操作系统会为您释放文件锁。
Pid files are not terribly useful because they can be stale (the file exists but the process does not). Hence, the application executable itself can be locked instead of creating and locking a pid file.
Pid 文件并不是非常有用,因为它们可能是陈旧的(文件存在但进程不存在)。因此,可以锁定应用程序可执行文件本身而不是创建和锁定 pid 文件。
A more advanced method is to create and bind a unix domain socket using a predefined socket name. Bind succeeds for the first instance of your application. Again, the OS unbinds the socket when the application terminates for any reason. When bind()
fails another instance of the application can connect()
and use this socket to pass its command line arguments to the first instance.
更高级的方法是使用预定义的套接字名称创建和绑定 unix 域套接字。应用程序的第一个实例绑定成功。同样,当应用程序因任何原因终止时,操作系统会解除套接字的绑定。当bind()
失败时,应用程序的另一个实例可以connect()
并使用此套接字将其命令行参数传递给第一个实例。
回答by Erik
For windows, a named kernel object (e.g. CreateEvent, CreateMutex). For unix, a pid-file - create a file and write your process ID to it.
对于 Windows,一个命名的内核对象(例如 CreateEvent、CreateMutex)。对于 unix,pid 文件 - 创建一个文件并将您的进程 ID 写入其中。
回答by AProgrammer
It will depend on which problem you want to avoid by forcing your application to have only one instance and the scope on which you consider instances.
这取决于您希望通过强制应用程序只有一个实例来避免哪个问题以及您考虑实例的范围。
For a daemon — the usual way is to have a /var/run/app.pid
file.
对于守护进程——通常的方法是拥有一个/var/run/app.pid
文件。
For user application, I've had more problems with applications which prevented me to run them twice than with being able to run twice an application which shouldn't have been run so. So the answer on "why and on which scope" is very important and will probably bring answer specific on the why and the intended scope.
对于用户应用程序,我遇到的应用程序问题更多,这些问题阻止我运行它们两次,而不是能够运行两次不应该运行的应用程序。因此,关于“为什么以及在哪个范围内”的答案非常重要,并且可能会带来关于原因和预期范围的具体答案。
回答by MarkR
You can create an "anonymous namespace" AF_UNIX socket. This is completely Linux-specific, but has the advantage that no filesystem actually has to exist.
您可以创建一个“匿名命名空间”AF_UNIX 套接字。这完全是 Linux 特有的,但优点是实际上不必存在文件系统。
Read the man page for unix(7) for more info.
阅读 unix(7) 的手册页了解更多信息。
回答by Maciej Piechotka
It's seems to not be mentioned - it is possible to create a mutex in shared memory but it needs to be marked as shared by attributes (not tested):
似乎没有提到 - 可以在共享内存中创建互斥锁,但需要将其标记为共享属性(未测试):
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_t *mutex = shmat(SHARED_MEMORY_ID, NULL, 0);
pthread_mutex_init(mutex, &attr);
There is also shared memory semaphores (but I failed to find out how to lock one):
还有共享内存信号量(但我没有找到如何锁定信号量):
int sem_id = semget(SHARED_MEMORY_KEY, 1, 0);
回答by stefanct
Based on the hints in maxim's answerhere is my POSIX solution of a dual-role daemon (i.e. a single application that can act as daemon and as a client communicating with that daemon). This scheme has the advantage of providing an elegant solution of the problem when the instance started first should be the daemon and all following executions should just load off the work at that daemon. It is a complete example but lacks a lot of stuff a real daemon should do (e.g. using syslog
for logging and fork
to put itself into background correctly, dropping privileges etc.), but it is already quite long and is fully working as is. I have only tested this on Linux so far but IIRC it should be all POSIX-compatible.
基于这里的格言回答中的提示,我的双角色守护程序(即可以充当守护程序并作为与该守护程序通信的客户端的单个应用程序)的 POSIX 解决方案。这个方案的优点是当实例首先启动时应该是守护程序并且所有后续执行应该只加载该守护程序的工作时,可以提供一个优雅的问题解决方案。这是一个完整的示例,但缺少真正的守护进程应该做的很多事情(例如,syslog
用于日志记录并fork
正确地将自己置于后台,删除权限等),但它已经很长并且完全按原样工作。到目前为止,我只在 Linux 上测试过这个,但 IIRC 它应该是所有 POSIX 兼容的。
In the example the clients can send integers passed to them as first command line argument and parsed by atoi
via the socket to the daemon which prints it to stdout
. With this kind of sockets it is also possible to transfer arrays, structs and even file descriptors (see man 7 unix
).
在示例中,客户端可以将作为第一个命令行参数传递给它们的整数发送给它们,并atoi
通过套接字解析到守护进程,守护进程将其打印到stdout
. 使用这种套接字还可以传输数组、结构甚至文件描述符(请参阅 参考资料man 7 unix
)。
#include <stdio.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/un.h>
#define SOCKET_NAME "/tmp/exampled"
static int socket_fd = -1;
static bool isdaemon = false;
static bool run = true;
/* returns
* -1 on errors
* 0 on successful server bindings
* 1 on successful client connects
*/
int singleton_connect(const char *name) {
int len, tmpd;
struct sockaddr_un addr = {0};
if ((tmpd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
printf("Could not create socket: '%s'.\n", strerror(errno));
return -1;
}
/* fill in socket address structure */
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, name);
len = offsetof(struct sockaddr_un, sun_path) + strlen(name);
int ret;
unsigned int retries = 1;
do {
/* bind the name to the descriptor */
ret = bind(tmpd, (struct sockaddr *)&addr, len);
/* if this succeeds there was no daemon before */
if (ret == 0) {
socket_fd = tmpd;
isdaemon = true;
return 0;
} else {
if (errno == EADDRINUSE) {
ret = connect(tmpd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
if (ret != 0) {
if (errno == ECONNREFUSED) {
printf("Could not connect to socket - assuming daemon died.\n");
unlink(name);
continue;
}
printf("Could not connect to socket: '%s'.\n", strerror(errno));
continue;
}
printf("Daemon is already running.\n");
socket_fd = tmpd;
return 1;
}
printf("Could not bind to socket: '%s'.\n", strerror(errno));
continue;
}
} while (retries-- > 0);
printf("Could neither connect to an existing daemon nor become one.\n");
close(tmpd);
return -1;
}
static void cleanup(void) {
if (socket_fd >= 0) {
if (isdaemon) {
if (unlink(SOCKET_NAME) < 0)
printf("Could not remove FIFO.\n");
} else
close(socket_fd);
}
}
static void handler(int sig) {
run = false;
}
int main(int argc, char **argv) {
switch (singleton_connect(SOCKET_NAME)) {
case 0: { /* Daemon */
struct sigaction sa;
sa.sa_handler = &handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGINT, &sa, NULL) != 0 || sigaction(SIGQUIT, &sa, NULL) != 0 || sigaction(SIGTERM, &sa, NULL) != 0) {
printf("Could not set up signal handlers!\n");
cleanup();
return EXIT_FAILURE;
}
struct msghdr msg = {0};
struct iovec iovec;
int client_arg;
iovec.iov_base = &client_arg;
iovec.iov_len = sizeof(client_arg);
msg.msg_iov = &iovec;
msg.msg_iovlen = 1;
while (run) {
int ret = recvmsg(socket_fd, &msg, MSG_DONTWAIT);
if (ret != sizeof(client_arg)) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
printf("Error while accessing socket: %s\n", strerror(errno));
exit(1);
}
printf("No further client_args in socket.\n");
} else {
printf("received client_arg=%d\n", client_arg);
}
/* do daemon stuff */
sleep(1);
}
printf("Dropped out of daemon loop. Shutting down.\n");
cleanup();
return EXIT_FAILURE;
}
case 1: { /* Client */
if (argc < 2) {
printf("Usage: %s <int>\n", argv[0]);
return EXIT_FAILURE;
}
struct iovec iovec;
struct msghdr msg = {0};
int client_arg = atoi(argv[1]);
iovec.iov_base = &client_arg;
iovec.iov_len = sizeof(client_arg);
msg.msg_iov = &iovec;
msg.msg_iovlen = 1;
int ret = sendmsg(socket_fd, &msg, 0);
if (ret != sizeof(client_arg)) {
if (ret < 0)
printf("Could not send device address to daemon: '%s'!\n", strerror(errno));
else
printf("Could not send device address to daemon completely!\n");
cleanup();
return EXIT_FAILURE;
}
printf("Sent client_arg (%d) to daemon.\n", client_arg);
break;
}
default:
cleanup();
return EXIT_FAILURE;
}
cleanup();
return EXIT_SUCCESS;
}
回答by CubicleSoft
No one has mentioned it, but sem_open()
creates a real named semaphore under modern POSIX-compliant OSes. If you give a semaphore an initial value of 1, it becomes a mutex (as long as it is strictly released only if a lock was successfully obtained).
没有人提到它,而是sem_open()
在现代 POSIX 兼容操作系统下创建了一个真正的命名信号量。如果你给一个信号量一个初始值1,它就变成了一个互斥体(只要它是在成功获得锁的情况下才被严格释放的)。
With several sem_open()
-based objects, you can create all of the common equivalent Windows named objects - named mutexes, named semaphores, and named events. Named events with "manual" set to true is a bit more difficult to emulate (it requires four semaphore objects to properly emulate CreateEvent()
, SetEvent()
, and ResetEvent()
). Anyway, I digress.
使用多个sem_open()
基于对象的对象,您可以创建所有常见的等效 Windows 命名对象 - 命名互斥体、命名信号量和命名事件。命名为“手动”设置为true,更难以模仿了一下事件(它需要四个信号灯对象来正确地模拟CreateEvent()
,SetEvent()
和ResetEvent()
)。无论如何,我离题了。
Alternatively, there is named shared memory. You can initialize a pthread mutex with the "shared process" attribute in named shared memory and then all processes can safely access that mutex object after opening a handle to the shared memory with shm_open()
/mmap()
. sem_open()
is easier if it is available for your platform (if it isn't, it should be for sanity's sake).
或者,有命名共享内存。您可以使用命名共享内存中的“共享进程”属性初始化 pthread 互斥锁,然后所有进程都可以在使用shm_open()
/打开共享内存的句柄后安全地访问该互斥锁对象mmap()
。 sem_open()
如果它可用于您的平台(如果不是,则应该是出于理智的考虑)会更容易。
Regardless of the method you use, to test for a single instance of your application, use the trylock()
variant of the wait function (e.g. sem_trywait()
). If the process is the only one running, it will successfully lock the mutex. If it isn't, it will fail immediately.
无论您使用哪种方法,要测试应用程序的单个实例,请使用trylock()
等待函数的变体(例如sem_trywait()
)。如果该进程是唯一运行的进程,它将成功锁定互斥锁。如果不是,它将立即失败。
Don't forget to unlock and close the mutex on application exit.
不要忘记在应用程序退出时解锁和关闭互斥锁。
回答by Akagi201
I have just written one, and tested.
我刚刚写了一个,并进行了测试。
#define PID_FILE "/tmp/pidfile"
static void create_pidfile(void) {
int fd = open(PID_FILE, O_RDWR | O_CREAT | O_EXCL, 0);
close(fd);
}
int main(void) {
int fd = open(PID_FILE, O_RDONLY);
if (fd > 0) {
close(fd);
return 0;
}
// make sure only one instance is running
create_pidfile();
}
回答by Binoy
Avoid file-based locking
避免基于文件的锁定
It is always good to avoid a file based locking mechanism to implement the singleton instance of an application. The user can always rename the lock file to a different name and run the application again as follows:
避免使用基于文件的锁定机制来实现应用程序的单例实例总是好的。用户始终可以将锁定文件重命名为不同的名称并再次运行应用程序,如下所示:
mv lockfile.pid lockfile1.pid
Where lockfile.pid
is the lock file based on which is checked for existence before running the application.
在lockfile.pid
运行应用程序之前检查是否存在的锁定文件在哪里。
So, it is always preferable to use a locking scheme on object directly visible to only the kernel. So, anything which has to do with a file system is not reliable.
因此,最好在只有内核直接可见的对象上使用锁定方案。因此,与文件系统有关的任何事情都是不可靠的。
So the best option would be to bind to a inet socket. Note that unix domain sockets reside in the filesystem and are not reliable.
所以最好的选择是绑定到 inet 套接字。请注意,unix 域套接字驻留在文件系统中并且不可靠。
Alternatively, you can also do it using DBUS.
或者,您也可以使用 DBUS 来完成。
回答by Mark Lakata
Here is a solution in C++. It uses the socket recommendation of Maxim. I like this solution better than the file based locking solution, because the file based one fails if the process crashes and does not delete the lock file. Another user will not be able to delete the file and lock it. The sockets are automatically deleted when the process exits.
这是 C++ 中的解决方案。它使用Maxim的socket推荐。我比基于文件的锁定解决方案更喜欢这个解决方案,因为如果进程崩溃并且不删除锁定文件,基于文件的解决方案就会失败。其他用户将无法删除该文件并将其锁定。当进程退出时,套接字会自动删除。
Usage:
用法:
int main()
{
SingletonProcess singleton(5555); // pick a port number to use that is specific to this app
if (!singleton())
{
cerr << "process running already. See " << singleton.GetLockFileName() << endl;
return 1;
}
... rest of the app
}
Code:
代码:
#include <netinet/in.h>
class SingletonProcess
{
public:
SingletonProcess(uint16_t port0)
: socket_fd(-1)
, rc(1)
, port(port0)
{
}
~SingletonProcess()
{
if (socket_fd != -1)
{
close(socket_fd);
}
}
bool operator()()
{
if (socket_fd == -1 || rc)
{
socket_fd = -1;
rc = 1;
if ((socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
throw std::runtime_error(std::string("Could not create socket: ") + strerror(errno));
}
else
{
struct sockaddr_in name;
name.sin_family = AF_INET;
name.sin_port = htons (port);
name.sin_addr.s_addr = htonl (INADDR_ANY);
rc = bind (socket_fd, (struct sockaddr *) &name, sizeof (name));
}
}
return (socket_fd != -1 && rc == 0);
}
std::string GetLockFileName()
{
return "port " + std::to_string(port);
}
private:
int socket_fd = -1;
int rc;
uint16_t port;
};