Skip to content

【llbc】App运行时能力扩充 - 只允许启动一个实例 #224#378

Open
faith-free1st wants to merge 26 commits into
lailongwei:masterfrom
faith-free1st:master
Open

【llbc】App运行时能力扩充 - 只允许启动一个实例 #224#378
faith-free1st wants to merge 26 commits into
lailongwei:masterfrom
faith-free1st:master

Conversation

@faith-free1st
Copy link
Copy Markdown
Collaborator

可选方案

1、文件锁
优点:
标准化实现,可拓展
tip:目录+pid保证原子性,检查/proc//exe来保证进程指向验证
缺点:
crash之后需要额外处理(llbc不做处理,在进程启动的时候会检查并清理文件内容)
2、POSIX named semaphores
优点:
实现简单,原子性保证
缺点:
具有内核生命周期,系统重启前都无法被清理
3、bind a port
优点:
进程关闭后内核会自动关闭文件描述符,简单
如果进程本身就已经绑定了一个port,那完全无需改动
缺点:
对于不需要绑定port的进程也需要绑定port
port是系统资源,对于运行多个程序的系统,依赖单一的port不太可控
文件描述符会传递给子进程
依赖网络功能
4、mutex in shared memory
优点:
基于共享内存,原子性保证
缺点:
crash之后需要额外处理

方案选取

方案2,4在crash之后都会有数据残留且难以清除的问题。
接下来是在方案1和3里面选,两个方案其实都是操作系统提供的支持,一个是文件模块,一个是网络模块。
考虑到网络模块是对外模块,采用使用文件模块的方案一更稳定一些,最终决定使用方案一

功能实现

在Mac和Linux下:
1、判断.pid文件是否存在,存在就读取pid判断是否同名进程运行中。不存在则创建文件并直接进入下一步
2、设置.pid文件锁,写入当前进程pid到.pid文件中,释放文件锁
在Win平台下,步骤2中的设置文件锁,可以通过设置CreateFileA接口参数中的dwShareMode参数为共享读FILE_SHARE_READ只来实现。

corvoliu added 14 commits January 22, 2025 10:28
Copy link
Copy Markdown
Owner

@lailongwei lailongwei left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

win32平台实现还有一些细节问题,可以再修改一波,另:建议整体review一下哈 @faith-free1st

Comment thread llbc/src/core/os/OS_Process.cpp Outdated
static char runningExeFilePath[MAX_PATH + 1] = {0};

// Get current execute file path.
LLBC_String selfExeFilePath = LLBC_Directory::ModuleFilePath();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

建议加const修饰

Comment thread llbc/src/core/os/OS_Process.cpp Outdated
NULL);
LLBC_SetErrAndReturnIf(exclusiveInfoFile == INVALID_HANDLE_VALUE, LLBC_ERROR_OSAPI, LLBC_FAILED);
LLBC_Defer(
LLBC_SetErrAndReturnIf(!CloseHandle(exclusiveInfoFile), LLBC_ERROR_OSAPI,)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

此处是defer语句,无需再set error and return if, 直接CloseHandle()即可

Comment thread llbc/src/core/os/OS_Process.cpp Outdated
: exclusiveInfoFilePath;

// Create then read-lock exclusive info file.
HANDLE exclusiveInfoFile = CreateFileA(nmlExclusiveInfoFilePath.c_str(),
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

windows操作系统api是大驼峰风格,容易跟框架混淆,建议加'::'

Comment thread llbc/src/core/os/OS_Process.cpp Outdated

// Read pid from exclusive info file.
DWORD fileSize = GetFileSize(exclusiveInfoFile, NULL);
LLBC_SetErrAndReturnIf(fileSize == -1 || fileSize >= 32, LLBC_ERROR_OSAPI, LLBC_FAILED);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

未细致考虑:几处问题:

  1. 返回 -1 是失败的判断,不符合api规范,建议用INVALID_FILE_SIZE来判断
  2. fileSize >= 32是一个“业务测认为的错误”,在这种情况下,win32 api ::GetLastError()返回的值是错误或不确定的,因为OS API并未失败,这里粗心了,未区分这个case就统一以LLBC_ERROR_OSAPI方式的错误来做
  3. GetFileSize()时,如果大小大于32位大小,会将高位设置到第二个参数,我们使用时还很复杂,建议使用GetFileSizeEx()代替,详细api说明(下方也有截图):https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfilesize

image

Comment thread llbc/src/core/file/Directory_Com.cpp
Comment thread llbc/src/core/os/OS_Process.cpp Outdated
exclusiveInfoBuf[fileSize] = '\0';

// Deal with exclusive info.
long exclusiveInfoPid = LLBC_Str2Long(exclusiveInfoBuf);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

long在windows msvc的64位模式下编译是32位大小,有歧义(int/long是可以给编译器做决定的,而msvc遵循32位规范来,就是这样奇葩),建议使用int64

Comment thread llbc/src/core/os/OS_Process.cpp Outdated

// Deal with exclusive info.
long exclusiveInfoPid = LLBC_Str2Long(exclusiveInfoBuf);
LLBC_ReturnIf(exclusiveInfoPid == getpid(), LLBC_FAILED);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里直接返回错误,未设置任何errorno,不符合框架规范:框架api要求统一化设置错误码

// Open executable process by pid.
HANDLE processHandle = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, exclusiveInfoPid);
if (processHandle != NULL)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OpenProcess()应该会返回很多很多种可能性的错误,是否需要针对性处理,而不是统一处理(==null时)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里没有对processHandle为NULL的情况做处理主要两点原因:
1、processHandle在一些情况下为NULL是合理的,比如之前运行的进程已经结束了。
2、这里是NULL不会影响后续逻辑,打印一条日志容易产生误会

Copy link
Copy Markdown
Owner

@lailongwei lailongwei left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

要求修正

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants