我可以允许我的服务器进程在不终止现有连接的情况下重新启动吗?

2024-04-18 04:14:04 发布

您现在位置:Python中文网/ 问答频道 /正文

为了使我的基于终端的程序能够生存更长的时间,我被告知要研究将进程从系统中分离出来。我找不到太多指定PID的内容,我想从中派生一个新进程。你知道吗

这在Linux中可能吗?我主要是一个Windows的家伙。你知道吗

我的程序将处理套接字,如果我的应用程序崩溃,那么我将丢失大量信息。我的印象是,如果它是从系统中分叉的插座将保持活力?你知道吗

编辑:下面是我要做的。我有多台计算机要与之通信。因此,我正在构建一个程序,让我可以在套接字上侦听(简单)。然后我将从每个远程计算机连接到它(简单)。你知道吗

一旦我有了一个连接,我想打开一个新的终端,并使用我的程序开始与远程计算机交互(简单)。你知道吗

问题来自这一部分。。客户端shell将所有通信发送到主shell,然后主shell将其发送到远程计算机。当收到响应时,它将转到主shell并将其转发到客户端shell。你知道吗

问题是保持每个客户机shell处于循环中。我希望所有客户端shell都知道每个客户端shell上谁与谁连接。所以客户机shell 1应该告诉我是否有客户机shell 2、3、4、5等等,以及谁与之连接。这就涉及到不同进程之间的资源共享。所以我在考虑使用本地套接字在所有这些客户端shell之间发送数据。但后来我遇到了一个问题,如果主炮弹死了,一切都会失去。所以我想找到一种方法来保护它。你知道吗

如果这有道理的话。你知道吗


Tags: 程序终端客户端内容客户机远程进程linux
2条回答

从您自己的程序中分叉是一种方法,但是创建服务更简单、更容易。服务是围绕程序的一个小包装器,负责保持程序运行、在失败时重新启动程序以及提供启动和停止程序的方法。你知道吗

此链接显示如何编写服务。尽管它是专门针对web服务器应用程序的,但是相同的逻辑可以应用于任何东西。你知道吗

https://medium.com/@benmorel/creating-a-linux-service-with-systemd-611b5c8b91d6

然后要启动程序,您可以编写:

sudo systemctl start my_service_name

要阻止它:

sudo systemctl stop my_service_name

查看其输出:

sudo journalctl -u my_service_name

那么,您希望能够重新加载程序而不丢失打开的套接字连接吗?你知道吗

首先要了解的是,当进程退出时,所有打开的文件描述符都将关闭。这包括插座连接。作为守护进程运行不会改变这一点。进程通过独立于终端会话而成为守护进程,因此当终端会话结束时它将继续运行。但是,与任何其他进程一样,当守护进程由于任何原因(正常退出、崩溃、终止、机器重新启动等)终止时,与它的所有连接都将不存在。顺便说一句,这不是特定于unix,Windows是相同的。你知道吗

因此,您的问题的简短答案是不,没有办法告诉unix/linux在进程停止时不关闭套接字,它将关闭它们,仅此而已。你知道吗

长话短说的答案是,有几种方法可以重新设计一些东西来绕过这个问题:

1)当您向程序发送一个特殊的消息或信号(例如SIGHUP)时,它可以拥有自己的程序exec()。在unix中,exec(或它的几个变体)不结束或启动任何进程,它只是将代码加载到当前进程并开始执行。新代码在同一进程中取代旧代码。由于过程保持不变,因此任何打开的文件都保持打开状态。但是,您将丢失内存中的所有数据,因此套接字将打开,但您的程序将对它们一无所知。在启动时,您必须使用各种系统调用来发现进程中打开了哪些描述符,以及其中是否有到客户端的套接字连接。解决这个问题的一种方法是将关键信息作为命令行参数或环境变量传递,这些参数可以通过exec()调用传递,从而在新代码开始执行时保留以供使用。你知道吗

请记住,这仅在进程仍在运行时调用exec本身时有效。因此您无法从崩溃或进程结束的任何其他原因中恢复。。你的关系就会消失。但这种方法确实解决了您希望在不丢失连接的情况下加载新代码的问题。你知道吗

2)您可以通过将服务器(主服务器)划分为两个进程来绕过此问题。第一个(称之为“代理”)接受来自客户机的TCP连接并保持它们的打开状态。代理永远不能退出,所以应该保持简单,这样您就很少需要更改代码了。第二个进程运行“worker”,它是实现应用程序逻辑的代码。您可能希望经常更改的所有代码都应该放在worker中。现在,您只需建立从代理到工作进程的进程间通信,并确保如果工作进程退出,代理中就有足够的信息在工作进程再次启动时重新建立应用程序状态。在一个非常简单的、低容量的应用程序中,该机制可以像代理在每次需要执行某项操作时对worker执行fork()+exec()一样简单。实现这一点的一种更奇特的方法是unix域数据报(SOCK_DGRAM)套接字,我使用了这种方法并取得了很好的效果。代理接收来自客户机的消息,通过数据报套接字将它们转发给工作机,工作机执行工作,并将结果返回给代理,代理将其转发回客户机。这很好,因为只要代理正在运行并且打开了unix域套接字,工作进程就可以随意重新启动。共享内存还可以作为代理和工作进程之间通信的一种方式。你知道吗

3)可以使用unix域套接字、函数sendmesg()recvmsg()以及SCM_RIGHTS标志来传递客户机数据本身,而实际上是将打开的套接字文件描述符从旧实例发送到新实例。这是在不相关的进程之间传递打开的文件描述符的唯一方法。使用这个机制,你可以实现各种各样的策略。。例如,您可以启动主程序的一个新实例,并让它(通过unix域套接字)连接到旧实例,然后将所有套接字都传输过来。然后可以退出旧实例。或者,您可以使用代理/工作者模型,但不必通过代理传递消息,您只需让代理通过它们之间的unix域套接字将套接字描述符交给工作者,然后工作者就可以使用该描述符直接与客户机通信。或者,您可以让您的主机将其所有socket文件描述符发送到另一个“隐藏”进程,以防主机需要重新启动。有各种各样的架构。请记住,操作系统只是提供了将描述符发送到各个位置的能力,所有其他逻辑都必须自己编写。你知道吗

4)你可以接受,无论你多么小心,都不可避免地会失去联系。网络不可靠,程序有时崩溃,机器重新启动。因此,与其花大力气确保连接不会关闭,不如让系统在不可避免的情况下恢复。你知道吗

最简单的方法是:因为您的客户机知道他们希望连接到谁,所以您可以让您的客户机进程运行一个循环,在这个循环中,如果与主进程的连接由于任何原因丢失,他们会定期尝试重新连接(比如说每10-30秒一次),直到成功。因此,主程序所要做的就是打开集合(侦听)套接字并等待,然后从每个仍在运行的客户端重新建立连接。然后,客户机必须重新发送它所拥有的任何信息,这些信息对于在主服务器中重新建立正确的状态是必要的。你知道吗

已连接计算机的列表可以保存在主机的内存中,没有理由将其写入磁盘或其他任何位置,因为当主机退出(出于任何原因)时,这些连接就不再存在。然后,任何客户机都可以连接到您的服务器(主)进程,并要求它提供已连接的客户机列表。你知道吗

就我个人而言,我会采取最后一种方法。因为在您的系统中,连接本身似乎比主机的状态更有价值,所以在发生丢失时能够恢复它们将是第一要务。你知道吗

在任何情况下,由于主机的作用似乎只是在客户机之间来回传递数据,这将是使用select()poll()函数的“异步”套接字I/O的一个很好的应用,这允许您在一个进程中在多个套接字之间进行通信,而不会阻塞。下面是一个很好的基于poll()的服务器示例,它接受多个连接:

https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/rzab6/poll.htm

至于在“系统外”运行进程。。在Unix/Linux中,这被称为作为守护进程运行。在*ix中,这些进程是进程id 1的子进程,init进程。。这是系统启动时启动的第一个进程。您不能告诉您的进程成为init的子进程,当现有父进程退出时,这种情况会自动发生。所有“孤立”进程都被init采用。由于有许多编写unix守护进程的简单示例(此时,您需要编写的代码已经变得相当标准化),因此我不会在这里粘贴任何代码,但我发现了一个很好的示例:http://web.archive.org/web/20060603181849/http://www.linuxprofilm.com/articles/linux-daemon-howto.html#ss4.1

如果您的linux发行版使用systemd(在某些发行版中是init的最新替代品),那么您可以将其作为systemd服务来执行,这是systemd的守护进程思想,但它们会为您做一些工作(无论好坏)。。有很多关于系统的抱怨。。战争打得差不多。。。你知道吗

相关问题 更多 >