前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用 abstract unix socket 实现进程单实例运行

用 abstract unix socket 实现进程单实例运行

作者头像
byronhe
发布2021-06-25 11:04:02
1.1K0
发布2021-06-25 11:04:02
举报
文章被收录于专栏:Tech Explorer

一,问题背景

很多时候,我们需要确保进程只有一个实例运行

有几种方法:

http://stackoverflow.com/questions/2964391/preventing-multiple-process-instances-on-linux

http://stackoverflow.com/questions/5339200/how-to-create-a-single-instance-application-in-c-or-c

https://github.com/qtproject/qt-solutions/tree/master/qtsingleapplication/src

比较常规的做法,是对一个文件加文件锁 flock,比如对 pid 文件 flock( LOCK_EX|LOCK_NB )

但是这种方法有些弊端:

  1. 如果文件被 mv 或者 rm,是会被绕过的。
  2. 如果磁盘故障比如磁盘满,目录没有写权限,会失败。

二,abstract namespace unix socket

http://linux.die.net/man/7/unix

unix socket 有3种:

  1. 基于文件的
  2. socketpair 创建的,匿名的
  3. abstract namespace 的,Linux特有

Linux 下, AF_UNIX socket 支持一种特殊的 abstract namespace unix socket 。

相比 普通的基于文件系统的 unix socket,abstract namespace unix socket :

  1. 没有磁盘文件
  2. 进程挂了以后自动删除,无残留文件
  3. 无需担心与 文件系统上的文件冲突,不需要关心文件系统上的绝对路径是否存在的问题

在 lsof 的结果里面看起来,就是有一些 类似 @test_abstract_ns 这样的 文件项

代码中使用也很简单, abstract namespace unix socket 在 bind 之前,sockaddr_un.sun_path[0] 设成 0x0 即可。

三,代码

于是我用 abstract unix socket 实现了一个 SysSem 工具类( 一个 system 范围的 semaphore ), 用来:

  1. 让一个程序只启动一个实例。
  2. 让 x 进程等待 y 进程执行完 yyy 操作后,才能执行 xxx 操作。

特点:

  1. 多进程/线程 并发安全。
  2. 当持有的进程被 kill ,OS自动释放,无残留。
  3. 没有磁盘文件,没有文件意外被删的各种情况。
  4. 不占用 tcp/udp 端口。
  5. 简单,不到 60行代码。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103

#include <sys/socket.h> #include <sys/un.h> #include <unistd.h> #include <algorithm> #include <string> // // a semaphore with system scope. // // 1. no race conditions between Post() / GetValue() , better than flock(). // 2. when a running process be killed, automatically release all. // 3. no file on disk, no accidently delete . // 4. no tcp/udp socket, no confliction, no port consumption. // class SysSem { public: SysSem() : _fd(-1) { memset(&_addr, 0, sizeof(_addr)); } ~SysSem(); void Init(std::string id); bool Post(); bool GetValue(); const char* GetID() const; private: struct sockaddr_un _addr; int _fd; }; void SysSem::Init(std::string id) { _addr.sun_family = AF_UNIX; const size_t len = std::min(id.size(), sizeof(_addr.sun_path) - 2); // 2 = start null and end null byte // abstract namespace socket address , _addr.sun_path[0] is a null byte ('\0') memcpy(_addr.sun_path + 1, id.c_str(), len); // memcpy(_addr.sun_path + 0, id.c_str(), len); } const char* SysSem::GetID() const { return &_addr.sun_path[1]; } SysSem::~SysSem() { if (_fd >= 0) { ::close(_fd); _fd = -1; } } bool SysSem::Post() { _fd = ::socket(AF_UNIX, SOCK_STREAM, 0); if (_fd < 0) { return false; } if ((0 != ::bind(_fd, (struct sockaddr*)&_addr, sizeof(_addr))) || (0 != listen(_fd, 65536))) { return false; } return true; } bool SysSem::GetValue() { const int clientFD = ::socket(AF_UNIX, SOCK_STREAM, 0); if (clientFD < 0) { return false; } const bool ret = (0 == ::connect(clientFD, (struct sockaddr*)&_addr, sizeof(_addr))); ::close(clientFD); return ret; } #include <assert.h> #include <stdio.h> int main(int argc, char** argv) { if (argc != 3) { fprintf(stderr, "usage: %s abstract-path post/get\n", argv[0]); exit(1); } SysSem inst; inst.Init(argv[1]); if (0 == strcasecmp(argv[2], "post")) { assert(inst.Post()); SysSem check; check.Init(argv[1]); assert(check.GetValue()); printf("ok, i am the only one under %s. running ...\n", inst.GetID()); pause(); } else if (0 == strcasecmp(argv[2], "get")) { assert(inst.GetValue()); printf("a process is running under %s. \n", inst.GetID()); } else { printf("unknown cmd \n"); } return 0; }

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-03-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一,问题背景
  • 二,abstract namespace unix socket
  • 三,代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档