C 如何在linux中从进程中分离可执行文件以进行实时更新

C 如何在linux中从进程中分离可执行文件以进行实时更新,c,linux,process,C,Linux,Process,通常,从该文件启动进程时,无法覆盖可执行文件。在任何时候,进程都可以尝试重新加载丢失的代码段 是否有可能绕过/打破这把锁 我的进程没有mlockall(),因此所有代码页都已加载 目标是流程(一个长时间运行的任务)应该以尽可能少的停机时间进行自我更新 下载后,execl(argv[0],NULL)应该会激活更新的代码。对我来说,您在以下几点上是错误的: 首先,您可以覆盖Linux上运行的二进制文件(更具体地说,是一个文件存储在ext{2,3,4}文件系统上的二进制文件);原因很简单,只要文件上

通常,从该文件启动进程时,无法覆盖可执行文件。在任何时候,进程都可以尝试重新加载丢失的代码段

是否有可能绕过/打破这把锁

我的进程没有
mlockall()
,因此所有代码页都已加载

目标是流程(一个长时间运行的任务)应该以尽可能少的停机时间进行自我更新


下载后,
execl(argv[0],NULL)
应该会激活更新的代码。

对我来说,您在以下几点上是错误的:

  • 首先,您可以覆盖Linux上运行的二进制文件(更具体地说,是一个文件存储在ext{2,3,4}文件系统上的二进制文件);原因很简单,只要文件上仍有打开的文件描述符,与此文件相关联的索引节点将由驱动程序保持“分配”,直到最后一个文件描述符关闭,然后释放块。因此,不存在查找文件数据的风险
  • 代码被映射到内存中的进程启动,因此不会有丢失的代码(并且由于mmap使用文件描述符,即使在延迟映射的情况下,整个文件仍然是“可映射的”)
  • mlockall用于锁定内存中的页面,从而防止交换,这与锁定文件系统上的文件没有多大关系

最后,没有什么能阻止你做你要求的事。

对我来说,你在几点上是错的:

  • 首先,您可以覆盖Linux上运行的二进制文件(更具体地说,是一个文件存储在ext{2,3,4}文件系统上的二进制文件);原因很简单,只要文件上仍有打开的文件描述符,与此文件相关的索引节点将由驱动程序保持“分配”,直到最后一个文件描述符关闭,然后块被释放。因此,不存在查找文件数据的风险
  • 代码被映射到内存中的进程启动,因此不会有丢失的代码(并且由于mmap使用文件描述符,即使在延迟映射的情况下,整个文件仍然是“可映射的”)
  • mlockall用于锁定内存中的页面,从而防止交换,这与锁定文件系统上的文件没有多大关系
最后,没有什么能阻止你做你要求的事情

通常,从该文件启动进程时,无法覆盖可执行文件。是否有可能绕过/打破此锁定

是。不要覆盖可执行文件;替换它

也就是说,将新的可执行文件以临时名称保存在同一目录中(或同一文件系统中的任何位置——必须在同一装载上!),然后将临时文件或临时文件保存在可执行文件上

在shell脚本中,如果
newbinary
oldbinary
都位于同一文件系统中并装载,则可以使用
mv-f newbinary-oldbinary

#!/bin/bash
BINDIR=/usr/bin

# Autoremoved work directory
Work="$(mktemp -d)" || exit 1
trap "cd / ; rm -rf '$Work'" EXIT

# ... Check if new binaries available ...
#     Otherwise: exit 0

# ... Download new binaries under "$Work/" ...

# Copy 'executable' to $BINDIR, under a temporary name
tempbin="executable.$PID-$RANDOM$RANDOM$RANDOM"
if ! mv -f "$Work/executable" "$BINDIR/$tempbin" ; then
    # Failed
    exit 1
elif ! mv -f "$BINDIR/$tempbin" "$BINDIR/executable" ; then
    # Failed
    exit 1
fi

# Successfully replced.
exit 0
这适用于所有POSIXy系统,因为文件名和指定其内容、访问模式、所有权、时间戳等的文件名完全不同

实际上,只要有可执行文件在运行旧inode,或者任何进程打开它,内核就会保留旧inode。但是,文件名会立即指向新的inode,并带有新的可执行内容。因此,从本质上讲,重命名/链接只会更改文件名所指的inode。这也是临时文件名e必须驻留在同一个文件系统(相同的装载)上

目标是流程(一个长时间运行的任务)应该以尽可能少的停机时间进行自我更新

允许进程自行更改是一个常见的安全漏洞。通常情况下,POSIXy系统中甚至根本不允许这样做,除非进程以超级用户权限运行(即作为
root
或在Linux中使用
CAP\u FOWNER
功能)。您不希望这样做

(仅仅因为这样做很常见,例如PHP web stuff,并不意味着它是明智的或安全的。如果这样做了,那么我们就必须同意粪便的味道很好,因为有数以十亿计的苍蝇和粪甲虫这么认为。如果你看一看,你会发现这些web服务都有严重的安全问题,有些与安全直接相关。)这种更新机制。上述软件包的一些维护人员声称,更新过程中出现的问题,如中间人攻击,是用户的错,而不是他们的错。当然,他们错了。)

相反,您应该有一个单独的特权服务,定期检查更新,当发现更新时,使用上述替换方法检索新版本

如果您的用户真的希望您这样做,您可以创建一个最小的C守护进程,定期检查是否有新版本可用。您可以让它在特定的Unix域数据报地址上接收数据,这样您的可执行文件就可以向它发送一个字符(无论它作为哪个用户运行),以便更新守护进程随时进行检查(除非它最近检查得足够多)。本质上,它只是等待(比如说,使用)足够的时间,或者等待一个特定的检查请求。到了时间,它将运行一个shell脚本来检查是否有新的可执行文件可用(比如说,使用等;保存这些脚本的典型位置在
/usr/lib/yourservice/
)。如果脚本响应新版本可用,请运行另一个脚本下载并替换二进制文件。如果进程收到
SIGHUP
信号,请立即执行检查;如果它收到
SIGTERM
信号,请退出。这样,它可以作为服务运行,并且在运行时不会消耗太多资源

在长时间运行的可执行文件中,如果处于可以用较新版本替换自身的位置,请在
/proc/self/exe
argv[0]
上使用,以验证它们是否具有相同的版本<
    if (argv[0][0] == '/')
        execv(argv[0], argv);
    else
        execvp(argv[0], argv);
    execvp(exepath, argv);