0%

Shell到底是什么

The Kernel and the Utilities

kernel
Unix 系统在逻辑上可以分为两个部分:the kernel and the utilities。 或者,如果你愿意的话,内核和其他所有内容通常都通过 shell 访问。

内核是 Unix 系统的核心,从计算机启动到关闭期间都驻留在计算机的内存中。

磁盘上各种各样的 tools 构成了完整的 Unix 系统体验,只有当调用这些 utilities 时,才会被加载到内存并执行。事实上,你所知道的每一个 Unix 命令都是一个实用程序;因此,该程序驻留在磁盘上,并且仅根据你的请求才被调入内存。

比如当执行 date 命令时,Unix 系统会从磁盘上找到名为 date 的程序,并加载到内存中,然后开始执行加载到内存中命令的指令。

shell 同样也是一个 utility programe,当你执行登陆操作时会被加载到内存执行。事实上,非常值得学习一下在终端开启时启动第一个 shell 所发生的精确的事件顺序。

The Login Shell

在早期的时候,terminals 终端是一个物理设备,通过一条线直接连接到 Unix 硬件系统。如今 terminal programs 让你在自己的电脑上通过网络连接登陆远程系统。

通常我们打开一个程序(Terminal or xterm)然后使用诸如 sshtelnetrlogin 的程序连接远程系统。

对一个系统上的每一个物理终端都会有一个叫做 getty 的程序处于活跃状态。

The getty process.
getty

Unix 系统——更准确地说是一个名为 init 的程序——负责每当系统允许用户登录时,就会自动在每个终端端口上启动一个 getty 程序。

getty 本质上是一个设备驱动程序,让登录程序 login 在其指定的终端上显示消息 login: 并等待用户输入内容。

如果使用类似 ssh 的程序连接系统,就会被分配一个 pseudo-terminal 或者 pseudo-tty,即伪终端,这就是为什么使用 who 命令能看到 ptty3 或者 pty1 的原因。

有一个专门的程序(login)读取用户输入的账号和密码,并进行认证,如果认证通过了,会调用登陆程序。

login

login 开始执行时,它会在终端显示 Password: 字符串,并且等待输入,当输入密码并敲回车后,login 程序会使用 /etc/passwd 文件进行相关认证,这个文件包含每个用户的
相关信息,比如 login namehome directory、用户登陆后启动的程序等。文件每行最后一列(冒号分隔)就是 login shell,如果未指定,则默认使用 standard shell/bin/sh

如果使用终端程序登陆远程主机,那么会调用本地的 ssh 连接服务器上的 sshd,如果是在自己的电脑上打开终端,会自动帮你进行登陆操作,不需要再次输入密码了。

login 程序验证(/etc/shadow)完加密密码之后,它会检查要执行的登陆程序的名称。大多数情况下都是 /bin/sh/bin/bash。也允许自定义其他登陆程序,或者为了文件所属者管理不需要进行交互的账号的程序 /bin/nologin

这样做的目的就是允许任何登陆账号在任何时候登陆系统时运行任何程序,shell 通常是最通用的,但绝不是唯一的选择。

一旦用户验证成功,login 会退出,将对用户终端连接的控制权交给标准 shell(login shell),然后从内存中消失。

login shelllogin 程序不是一回事。

user_login.png

正如前面提到的,init 程序运行类似于 getty 的程序来进行网络连接。例如,sshdtelnetdrlogind 分别通过 sshtelnetrlogin 应答连接请求。这些程序不是直接连接到特定的物理终端或调制解调器线路,
而是将用户的 shell(on server) 连接到 pseudo-ttys(on client)。无论你是通过网络、X Windows 屏幕还是通过网络终端连接程序登陆系统,使用 who 查看,都可以看到 pts

Typing Commands to the Shell

当 shell 启动后,会终端显示一个 command prompt,通常是一个 $ 符号,并等待用户输入命令。每次输入命令并回车后,shell 都会分析输入的内容并且执行相关命令响应用户请求。

如果我们向 shell 请求调用一个特定的程序,shell 会根据 PATH 变量指定的路径搜索相关目录直到找到为止。当找到对应的程序后,shellclone 一个自己(subshell),并请求内核使用找到的程序替代 subshell,然后
login shell 会”挂起”直到对应的程序执行完毕。内核复制指定程序开始执行程序对应的指令。复制的程序被叫做 process。这样,保存在磁盘文件中的程序和内存中逐行执行的进程就产生了区别。

如果执行的程序向标准输出写入内容,如果没有重定向或者使用管道,那么这些内容就会出现在终端上。同样如果标准输入没有被重定向或使用管道,那么就会等待用户的输入。

当对应的程序执行完毕,它就会从内存中消失,并且控制权重新回到 login shell,并给出命令行提示符,等待用户输入。

重点:shell 仅仅是一个程序而已,他在系统上并没有什么特权,这就意味着任何有专业知识和热情的人都可以创建自己的 shell,登陆后执行自定义的 shell 或者手动切换。这就是为什么现在有那么多的 shell 的原因。

shell 的职责

shell职责

Program Execution

shell 负责执行你从终端请求的所有程序。

每次输入命令后,shell 都会分析命令并决定做哪些事,就 shell 而言,每一行都遵循相同的基本格式:program-name arguments

The line that is typed to the shell is known more formally as the command line.

shell 扫描 command line,决定要执行程序的名称和传入程序的参数是什么。

shell 使用特殊字符来确定程序的名称和各个参数从哪里开始到哪里结束,这个特殊字符集体叫做 whitespace characters 即空白字符,通常是 space characterhorizontal tab character
end-of-line character,通常也叫做 newline character,多次出现的空白字符通常会被忽略。

比如 echo Unix is cool. 最终会输出 Unix is cool.

正如之前所说,shell 会搜索相关的目录去找对应的程序,大多数时候是这样的,但是有些命令是 built in shell 的,比如 cdpwdecho。在搜索目录之前会先判断是否是 built-in command,如果是则直接执行。

type cd 返回 cd is a shell builtintype sleep 返回 sleep is /bin/sleep

shell 执行命令之前还要做一些其他工作。

Variable and Filename Substitution

变量($符号)和文件名替换。

shell 会在命令行上进行文件名的替换,事实上,shell 在决定执行文件名称和参数之前会扫描命令行并寻找文件名替换字符:*?[...]

比如:一个目录下有 4 个文件,那么 echo * 命令有几个参数呢?答案是四个,shell 会进行文件名替换。

I/O Redirection

shell 还负责处理输入和输出重定向。

一个很经典的例子:

1
2
wc -l users  # 从文件读
wc -l < users # 从标准输入读

上面两个命令的输出是不一样的,第一个命令实际上有两个参数 -lusers,第二个只有一个参数 -l,没有文件名,此时 wc 会从标准输入获取输入结果。

Hooking up a Pipeline

就像 shell 扫描命令行搜索重定向符号一样,他也会搜索管道符号,并把前一个命令的标准输出连接到后一个命令的标准输入上,然后开始执行两个程序。

who | wc -l shell 发现管道符分隔了 whowc 命令,连接前一个命令的标准输出到后一个命令的标准输入上,然后开始同时执行两个命令。当 who 开始执行后,
它把结果输出到标准输出上,并不会意识到最终结果不会写到终端而是另一个命令的标准输入上。

wc 开始执行时,它知道没有指定文件名,会从标准输入读取内容,同样也不会意识到输入来自 who 命令的输出而不是标准输入。

Enviromment Control

环境控制。

Interpreted Programming Language

shell 有它自己的编程语言,这个语言被 shell 解释执行。shell 有它自己语法和数据类型等。

tty

tty 命令可以知道当前使用的是哪个字符设备(终端)。

在终端或者命令行运行的命令会打开当前使用的终端作为其标准输入、标准输出和标准错误。

1
2
3
4
5
tty
cat
^z
jobs -l
ll /proc/pid/fd # 三个软连接都会指向当前终端