我一直在尝试寻找关于GNU screen如何运作的高级信息,但不想阅读源代码,但我无法找到相关内容。
screen是如何做到即使在终端会话关闭后仍然存在的呢?它是否像守护进程一样运行,每个调用screen的人只是连接到它,然后它找到要附加的伪终端会话,还是它完全执行不同的操作?
我一直在尝试寻找关于GNU screen如何运作的高级信息,但不想阅读源代码,但我无法找到相关内容。
screen是如何做到即使在终端会话关闭后仍然存在的呢?它是否像守护进程一样运行,每个调用screen的人只是连接到它,然后它找到要附加的伪终端会话,还是它完全执行不同的操作?
这个问题有很多潜在的问题,所以我只会集中在一个问题上:
screen是如何做到即使终端会话关闭也能继续运行的?
Screen捕捉HUP信号,因此当其控制终端关闭时,它不会自动退出。相反,当它收到HUP信号后,它进入后台模式(因为它没有实际终端连接),并等待。当您使用各种-d/-D/-r/-R/-RR选项启动screen时,它会寻找已经运行的screen进程(可能在接收到HUP后分离,并/或通过发送HUP直接分离它),并接管该screen进程的子终端会话(一种合作过程,旧的screen进程将所有主PTY发送给新进程进行管理,然后退出)。
我没有深入研究过Screen本身,但我写了一个受它启发的程序,可以描述一下我的程序如何工作:
这个项目是我的终端模拟器,它有一个仿真核心、一个GUI前端、一个终端前端和两个类似于Screen的组件:attach.d和detachable.d。
https://github.com/adamdruppe/terminal-emulator
attach.d是前端。它通过Unix域套接字连接到特定的终端,并将活动屏幕的输出转发到实际终端。它还向后端进程发送消息,告诉它要从头开始重绘以及其他一些事情(还有更多的功能尚未完善)。
https://github.com/adamdruppe/terminal-emulator/blob/master/attach.d
我的terminal.d库提供了一个事件循环,用于转换终端输入和信号。其中之一是HUP信号,当控制终端关闭时发送。当它看到这个信号时,前端附加进程关闭,而后端进程仍然存在。https://github.com/adamdruppe/terminal-emulator/blob/master/attach.d#L709
当Attach启动并无法连接到现有进程时,它会进行分叉并创建可分离的后端。https://github.com/adamdruppe/terminal-emulator/blob/master/attach.d#L454
通过将前端和后端分离成独立的进程,关闭其中一个不会影响另一个。screen 也是这样做的:运行 ps aux | grep -i screen。全大写的 SCREEN 进程是后端,小写的 screen 进程是前端。me 3479 0.0 0.0 26564 1416 pts/14 S+ 19:01 0:00 screen
root 3480 0.0 0.0 26716 1528 ? Ss 19:01 0:00 SCREEN
我刚刚启动了screen
,你可以看到两个独立的进程。screen分叉并创建了SCREEN,它实际上保存了状态。分离会终止进程3479,但保留进程3480。
我的后端是一个完整的终端仿真器,维护所有内部状态:
https://github.com/adamdruppe/terminal-emulator/blob/master/detachable.d
这里:https://github.com/adamdruppe/terminal-emulator/blob/master/detachable.d#L140 读取与附加程序通信的套接字中的消息,并将其作为终端输入转发给应用程序。select
循环遍历每个打开的套接字,并仅绘制活动屏幕:https://github.com/adamdruppe/terminal-emulator/blob/master/attach.d#L300
我的程序为每个单独的终端屏幕运行一个独立的后端进程。GNU screen使用一个进程来处理整个会话,其中可能有多个屏幕。我猜想screen在后端做的工作比我多,但基本上是相同的原理:在每个pty上监视输入并更新内部屏幕缓冲区。当您运行screen -r
以连接它时,它通过命名管道连接 - 文件系统中的FIFO(在我的gnu screen配置中,它们存储在/tmp/screens
中),而不是我使用的unix套接字,但原理相同 - 它获取状态转储到屏幕,然后继续来回转发信息。