折腾Linux - 2.Shell的进程

承接上文

上一篇中,我们实现了内网穿透,这样我们就可以直接使用SSH连接到内网的服务器。

一旦可以用SSH连接进入设备,我们就可以掌控设备了(当然还需要root的密码)。

但是我们关闭了终端之后,连接似乎就断了。这是怎么回事呢?

本篇博客将深入到Linux,分析问题,最终解决这个问题。

为什么关闭终端,frp也会自动关闭?

首先我们要先了解一下shell

something easy about shell

shell是什么?

shell可以理解为是一个夹在用户与操作系统之间的程序,我们在shell中输入指定的指令,点击回车后,shell会对我们输入的指令进行解释(interpret)执行,并与操作系统进行交互。

所以shell的行为可以拆解为三大部分:

  • 读取输入命令
  • 解析命令
  • 执行命令,并将结果输出

同时shell中的指令分为了两大类:

  • shell自身的 builtin 指令,即shell自身支持的指令,例如:cd、ls等
  • shell自身无法解析的指令,这一类多数都是用户自己编写的程序

我们知道,在操作系统中,运行一个程序,都是需要单独开启一个进程(Process),来供程序运行。

在Unix上只有两种启动进程的方法。 第一个(几乎用不到)是被初始化。 当Linux计算机启动时,将加载其内核。 加载并初始化后,内核仅启动一个进程,称为Init。 此过程将在计算机打开的整个时间范围内运行,并负责加载计算机需要使用的其余过程。

也就是说,shell再神奇,也是在process上运行的。

由于大多数程序不是Init,因此仅留下一种实用的方法来启动进程:fork()系统调用。 调用此函数时,操作系统将复制该过程并启动它们的运行。 原始进程称为“父进程”,新进程称为“子进程”。 fork()向子进程返回0,并将其子进程的ID(PID)返回给父进程。 从本质上讲,这意味着新流程的唯一方法就是开始现有流程的复制。

对于shell自身的 builtin 指令,shell 都会在其自身的主线程中,进行执行的操作。

而对于非shell本身自带的指令,在linux中,其会使用系统提供的 fork 接口,来创建一个子进程,然后在子进程中运行该指令。

并且在子进程中运行指令时,主线程,也就是shell所在的进程,会等待子进程的执行结果。

解决方案

shell中的 “&” 指令

先做一个实验,在shell中,我们进入到之前frp的文件夹,输入:

1
./frps -c ./frps.ini

按回车之后,程序开始运行,但此时我们发现,shell 中会输出 frps 程序的日志。

同时,此时我们的 shell 无法进行其他操作,无法输入下一条指令,只是等待这 frps 程序输出。

如果此时我们想要执行别的命令,要么就终止 frps 程序,要么就重开一个 shell 程序。

这就和我们上面说的,主线程会等待子线程执行完毕,但是 frps 程序简单来说,是一直在一个循环里等待连接,所以除非启动失败或者手动关闭,否则这个 shell 就只会一直输出 frps 程序执行的日志。

此时我们键盘输入 ctrl + c,将程序终止。

然后输入:

1
./frps -c ./frps.ini &

点击回车,运行。

此时我们发现,我们的 shell 竟然还可以继续输入命令并且运行。

这正好证实了我们的说法,程序运行在子进程中,同时 shell 的主进程并没有等待子进程程序运行完毕。

这样不就实现了让 frps 程序在后台运行的效果了嘛。

上图的终端里,下面开启了我们的程序(作为示范),上面使用

1
ps -ef | grep frps

指令,可以看到后台(也就是子进程)是在运行着 frps 的,而且下面的终端主线程并没有等待该终端子线程程序运行结束。

但是。。。

但是 & 指令,只是将程序放在了shell的子进程中运行,那如果shell主进程关闭了,子进程会怎么样呢?

此时我们将下方的终端关闭。并在上面的终端里继续输入 ps -ef | grep frps 指令,会发现:

程序被关闭了,也就是说子进程也关闭了。

难道想要内网穿透,还得必须打开一个终端?

nohup

nohup(全称应该是 “no hangup”)这个指令,可以让运行的程序忽略 HUP 信号。

那么何为HUP信号呢?每当与当前程序运行所在的进程相关的shell关闭时,也就是shell的主进程关闭时,会向子进程发送该信号,使其正在运行的程序关闭。

如果使用了nohup指令,则会使当前的程序忽略掉 HUP 信号,也就是你的shell关闭后,其仍然可以继续运行,同时将输出信息,输出到当前目录下的 nohup.out。我们可以在nohup.out里查看程序运行的日志。

OK,那我们再来实验:

这次我们使用了nohup指令。然后我们关闭掉下方的shell,也就是启动 frps 程序的主线程。

可以看到,我们将下方的shell关闭后,frps 程序仍然在运行中,我们可以通过 ps 指令与万能的 grep 指令找到他。

这样我们也就不用把终端一直挂着,而是随用随关闭。内网穿透就像暗地里的守护者一样,不在我们的视野范围内活动,但是没有他,我们就无法从自己的内网访问我们的服务器了。

写在最后

本文从进程的角度,以frps为例,说明了linux中关于shell进程的问题。

当然作者也是linux入门没多久的新人,如果文章中有什么错误,请联系作者,一起讨论。

另外作者还想多一句嘴,查询这些资料时,国内的论坛指令参差不齐,只有一篇linux中国的文字像个样子,而且还是完全翻译linux.com上的文章,有能力的同学,还是用谷歌,并且用英文搜索,尤其是可以避开某个C字开头的论坛,文章质量太差了。

linux本身就是因为开源免费而吸引了那么多技术人员、hacker们来维护、开发的,而某些博主偷窃、转载别人的linux技术文章,还设置需要钱才可以看。其中对比不免让人感到可笑而无奈。

参考: