命令行的本质(上)- 命令如何工作
内核与壳 🐢
内核(Kernel)是计算机操作系统用于与硬件打交道、进程调度、访问文件系统的一小段短小精悍的代码,他为我们屏蔽了硬件的差异,并且工作在一个非常特殊的状态,叫「内核态」。
壳(Shell)就是我们用于跟内核打交道的方式,Shell 是一类程序的统称,并不单单指某一个特定程序。
我们耳熟能详的 Shell 包括 bash、zsh、csh 等等,其实 Windows 提供的图形界面也是一个 Shell。
命令行的本质 🤔
在 Linux 系统启动后,内核会自行启动一个 init 进程,所谓的 init 进程是一个由内核启动的用户级进程。
init 进程始终会是 Linux 系统的第一个进程,其余必要的进程会由 init 进程 fork 出来,注意这个时候是没有 SHELL 的,只有一堆后台进程在运行,其中还包括一个叫做 「SSHD」的进程。
SSHD 的 「D」是 Daemon 的意思,在计算机领域常用于表示一个后台进程,类似的还有「firewalld」等。
SSHD 进程默认监听 22 端口,监听端口可以通过修改 /etc/ssh/sshd_config
配置文件指定,一旦有远程设备发起对 22 端口的请求,验证通过之后,SSHD 就会 fork 出来一个新的进程,该新进程名字就叫做 bash(或者其他 Shell)。
Shell 进程为用户提供一个可交互的窗口,它的工作就是读取用户输入的命令并执行,他的执行结果可能是 fork 一个新的进程,或者输出一些内容,取决于用户输入的具体命令,这其实就是命令行的本质。
以上的过程可以通过 pstree
命令验证:
图中可以看到,bash 是 SSHD fork 出来的子进程,而当我执行 pstree 命令之后,bash 又 fork 出来一个 pstree 进程。
如何指定 Shell 🏌️♂️
Linux 有一个著名的文件/etc/passwd
,该文件中保存了系统中所有用户的信息,每一行就是一个用户。
|
|
每一行的最后一个:
后面的内容,就是对应该用户登录之后,默认打开的 Shell 进程是哪个一个,如果是nologin
的话,则以为着该用户无法登录。
只要修改这个配置文件,就可以为每个用户指定默认的 Shell 进程。
Shell 的查找机制 💁♀️
当用户输入一个「内置」的命令,例如cd
,那么 Shell 就会立马作出反应,执行命令,切换至目标目录。
但如果用户输入的命令不是一个内置命令,那么当前的 Shell 就会根据查找机制去找到命令所在的路径再去执行,如果找不到就报错。
如上图所示,当我执行mvn
命令时,系统提示「找不到命令」错误,但我输入docker
的时候,就可以正确执行,这其实就是 Shell 根据查找机制没有找到 mvn 命令导致的。
当用户输入一个非内置命令时,当前的 Shell 就会开始尝试找到这个命令的位置,它会去哪里找呢?
默认情况下,它会去 $PATH
环境变量定义的路径中找:
|
|
当 Shell 没有在 $PATH
环境变量定义的路径中找到用户输入的命令对应的可执行程序时,就会报错,通常是非常熟悉的command not found
错误。
而一旦找到了,那么 Shell 就会立马去执行,而并不关心这到底是什么。
命令的执行 🎯
在大多数情况下,执行一个命令,就等于 fork 一个新的进程。
例如当用户在命令行窗口输入node
命令并敲下回车,bash 就会 fork 一个子进程 node,之后将所有参数以及控制权交给这个进程,这之后发生的事情就是程序自己的事情了,bash 并不会管,直到程序执行完成或者用户退出。
|
|
同理,也有另外一种常见的情况,如果用户创建一个脚本 script.sh,内容是 export 一个 AAA 环境变量,值为 123:
|
|
执行一下,却发现什么也没有发生:
|
|
原因是用户是用sh
命令,执行 script.sh 的话,script.sh 其实就相当于是 sh
命令的参数,当前的 Shell 会为 sh
命令 fork 一个新的子进程,然后读取参数 script.sh 并且在新的子进程中执行,执行完毕后自动退出,而 export 只会新的子进程环境中生效,所以当前进程中,是看不到 AAA 变量的。
如何解决呢?可以利用source
命令。
source
命令可以使指定的 Shell 程序文件在当前环境中执行并生效:
|
|