折腾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 |
|
按回车之后,程序开始运行,但此时我们发现,shell 中会输出 frps 程序的日志。
同时,此时我们的 shell 无法进行其他操作,无法输入下一条指令,只是等待这 frps 程序输出。
如果此时我们想要执行别的命令,要么就终止 frps 程序,要么就重开一个 shell 程序。
这就和我们上面说的,主线程会等待子线程执行完毕,但是 frps 程序简单来说,是一直在一个循环里等待连接,所以除非启动失败或者手动关闭,否则这个 shell 就只会一直输出 frps 程序执行的日志。
此时我们键盘输入 ctrl + c,将程序终止。
然后输入:
1 |
|
点击回车,运行。
此时我们发现,我们的 shell 竟然还可以继续输入命令并且运行。
这正好证实了我们的说法,程序运行在子进程中,同时 shell 的主进程并没有等待子进程程序运行完毕。
这样不就实现了让 frps 程序在后台运行的效果了嘛。
上图的终端里,下面开启了我们的程序(作为示范),上面使用
1 |
|
指令,可以看到后台(也就是子进程)是在运行着 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技术文章,还设置需要钱才可以看。其中对比不免让人感到可笑而无奈。
参考:
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!