Unix系统进程管理与Shell相关知识详解(上)
1. /proc文件系统
在许多Unix系统中,借鉴了贝尔实验室开发的一个概念——/proc文件系统。与通过大量需要不断更新的系统调用提供对内核数据的访问不同,内核数据通过一个特殊的设备驱动程序提供,该驱动程序在
/proc
目录中实现了标准的文件系统接口。每个正在运行的进程在该目录下都有一个以进程号命名的子目录,子目录内包含各种存储内核数据的小文件。该文件系统的详细内容可在大多数系统的
proc(4)
或GNU/Linux系统的
proc(5)
手册页中查看。
GNU/Linux在这方面的发展比其他大多数Unix系统更深入,其
ps
命令通过读取
/proc
目录下的文件来获取所需的所有进程信息。你可以使用
strace -e TRace=file ps aux
命令进行系统调用跟踪来验证这一点。
以下是一个文本编辑器会话的进程文件示例:
$ ls /proc/16521 # 列出进程16521的proc文件
cmdline environ fd mem root statm
cwd exe maps mounts stat status
$ ls -l /proc/16521 # 详细列出这些文件
total 0
-r--r--r-- 1 jones devel 0 Oct 28 11:38 cmdline
lrwxrwxrwx 1 jones devel 0 Oct 28 11:38 cwd -> /home/jones
-r-------- 1 jones devel 0 Oct 28 11:38 environ
lrwxrwxrwx 1 jones devel 0 Oct 28 11:38 exe -> /usr/bin/vi
dr-x------ 2 jones devel 0 Oct 28 11:38 fd
-r--r--r-- 1 jones devel 0 Oct 28 11:38 maps
-rw------- 1 jones devel 0 Oct 28 11:38 mem
-r--r--r-- 1 jones devel 0 Oct 28 11:38 mounts
lrwxrwxrwx 1 jones devel 0 Oct 28 11:38 root -> /
-r--r--r-- 1 jones devel 0 Oct 28 11:38 stat
-r--r--r-- 1 jones devel 0 Oct 28 11:38 statm
-r--r--r-- 1 jones devel 0 Oct 28 11:38 status
需要注意的是,这些文件看起来似乎都是空的,但实际上,当读取它们时,设备驱动程序会提供相应的数据,它们并不真正存在于存储设备上。它们的时间戳也有些特殊:在GNU/Linux和OSF/1系统中,时间戳反映的是当前时间;而在IRIX和Solaris系统中,显示的是每个进程启动的时间。
/proc
文件的大小为零会让一些实用工具(如
scp
和
tar
)产生混淆。你可能需要先使用
cp
命令将它们复制到其他地方,转换为普通文件。
让我们查看其中一个文件的内容:
$ cat -v /proc/16521/cmdline # 显示进程的命令行
vi^@+273^@ch13.xml^@
2. 进程管理总结
在进程管理方面,我们可以创建、列出、控制、调度和删除进程,还能向进程发送信号并跟踪它们的系统调用。由于进程在私有地址空间中运行,它们不会相互干扰,因此编写可以同时运行的程序时无需特别努力。
进程可以捕获除两个信号之外的几十个信号,并可以选择忽略这些信号或采取任何所需的操作进行响应。这两个无法捕获的信号是
KILL
和
STOP
,它们确保即使是行为恶劣的进程也能被终止或暂停。需要执行清理操作(如保存活动文件、重置终端模式或移除锁)的程序通常会捕获常见信号;否则,大多数未捕获的信号会导致进程终止。
trap
命令让在shell脚本中添加简单的信号处理变得容易。
此外,还有几种不同的机制可用于延迟或控制进程执行。其中,
sleep
命令在shell脚本编写中最为有用,不过其他机制也各有其用途。
3. Shell语言概述
POSIX定义的shell语言比原始的V7 Bourne shell大得多,但比ksh93和bash这两种最常用的Bourne shell扩展版本所实现的语言要小得多。如果你要进行大量利用shell语言扩展的脚本编写,很可能会使用这两种shell中的一种或两种。因此,熟悉它们的共同特性和差异是很有价值的。
随着时间的推移,bash已经获得了ksh93的许多扩展,但并非全部。因此,它们之间有相当多的功能重叠,但也存在许多差异。下面我们将详细探讨这些方面。
4. Shell使用中的注意事项
在使用shell时,有一些需要特别注意的地方:
-
保存shell状态
:在POSIX标准中,没有明确的方法来保存函数定义以供后续恢复。以下是保存bash和ksh93 shell状态(包括函数)的示例代码:
{
set +o # 选项设置
(shopt -p) 2>/dev/null # bash特定选项,子shell使ksh静音
set # 变量和值
export -p # 导出的变量
readonly -p # 只读变量
trap # 陷阱设置
typeset -f # 函数定义(非POSIX)
} > /tmp/shell.state
需要注意的是,bash和ksh93在定义函数时可能使用不同的语法,因此如果你想从一个shell中导出状态并在另一个shell中恢复,需要格外小心。
-
echo命令的可移植性问题
:
echo
命令仅可在最简单的用途中实现可移植性,各种选项和/或转义序列可能可用也可能不可用(尽管有POSIX标准)。在ksh93中,内置的
echo
命令会尝试模拟
$PATH
中找到的外部
echo
命令的行为,目的是为了兼容;而在bash中,内置的
echo
命令在不同的Unix系统上行为一致,目的是为了保持一致性。因此,为了实现完全的可移植性,应避免使用
echo
命令,
printf
仍然是最佳选择。
-
OPTIND作为局部变量
:在ksh93中,使用
function
关键字定义的函数会有一个局部的
OPTIND
副本。这样,函数可以像独立的脚本一样使用
getopts
处理其参数,而不会影响父进程的选项处理。
5. bash的shopt命令
bash shell除了使用带有长、短选项的
set
命令外,还有一个单独的
shopt
命令用于启用和禁用选项。以下是
shopt
命令的详细信息:
| 选项 | 说明 |
|---|---|
| -o |
将选项限制为可以使用
set -o
设置的选项
|
| -p | 以适合重新读取的形式打印输出 |
| -q | 安静模式。退出状态表示选项是否已设置。如果有多个选项,当所有选项都启用时,状态为零;否则为非零 |
| -s | 设置(启用)给定的选项 |
| -u | 取消设置(禁用)给定的选项 |
对于
-s
和
-u
选项,如果没有指定选项名称,则分别显示已设置或未设置的选项列表。
其使用语法为:
shopt [ -pqsu ] [ -o ] [ option-name ... ]
该命令的目的是集中控制bash中添加的shell选项,而不是增加
set
选项或shell变量的数量。
6. bash和ksh93的共同扩展 - select循环
bash和ksh都支持
select
循环,它可以轻松生成简单的菜单。其语法简洁,但功能强大,语法如下:
select name [in list]
do
statements that can use $name ...
done
这与常规的
for
循环语法相同,只是将关键字
for
替换为
select
。并且和
for
循环一样,你可以省略
in list
部分,此时它将默认使用
"$@"
,即带引号的命令行参数列表。
select
循环的工作流程如下:
1. 为列表中的每个项目生成一个菜单,每个选项用数字编号。
2. 打印
PS3
的值作为提示信息,并等待用户输入一个数字。
3. 将所选的选项存储在变量
name
中,将所选的数字存储在内置变量
REPLY
中。
4. 执行循环体中的语句。
5. 无限重复这个过程(后续会介绍如何退出)
以下是一个示例,假设在一个分时系统中,需要根据不同的视频显示终端正确设置
TERM
变量。用户通过终端服务器进行通信,终端服务器无法传递
TERM
环境变量,因此需要在用户登录时提示他们选择终端类型。可以将以下代码放在
/etc/profile
文件中(假设已知一组固定的终端类型):
PS3='terminal? '
select term in gl35a t2000 s531 vt99
do
if [ -n "$term" ]
then
TERM=$term
echo TERM is $TERM
fi
done
下面是
select
循环的mermaid流程图:
graph TD;
A[开始] --> B[生成菜单];
B --> C[打印提示,等待输入];
C --> D[存储选择到变量];
D --> E[执行循环体语句];
E --> C;
7. 下载bash和ksh93的源代码
如果需要获取bash和ksh93的源代码并进行编译,可以按照以下步骤操作:
-
bash
:bash可以从自由软件基金会GNU项目的FTP服务器下载。截至目前,当前版本是3.0。可以使用
wget
(如果已安装)来获取发行版的tar文件:
$ wget ftp://ftp.gnu.org/gnu/bash/bash-3.0.tar.gz
--17:49:21-- ftp://ftp.gnu.org/gnu/bash/bash-3.0.tar.gz
=> `bash-3.0.tar.gz'
或者使用传统的匿名FTP方式获取文件:
$ ftp ftp.gnu.org # 连接到服务器
Connected to ftp.gnu.org (199.232.41.7).
220 GNU FTP server ready.
Name (ftp.gnu.org:tolstoy): anonymous # 匿名登录
230 Login successful.
230-Due to U.S. Export Regulations, all cryptographic software on this
230-site is subject to the following legal notice:
...
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> cd /gnu/bash # 切换到bash目录
250 Directory successfully changed.
ftp> binary # 确保使用二进制模式
200 Switching to Binary mode.
ftp> hash # 打印#标记以显示进度
Hash mark printing on (1024 bytes/hash mark).
ftp> get bash-3.0.tar.gz # 下载文件
local: bash-3.0.tar.gz remote: bash-3.0.tar.gz
227 Entering Passive Mode (199,232,41,7,149,247)
150 Opening BINARY mode data connection for bash-3.0.tar.gz (2418293 bytes).
Unix系统进程管理与Shell相关知识详解(下)
8. 其他扩展Bourne风格的Shell
除了bash和ksh93,还有另外两种流行且值得关注的Shell:
-
公共领域Korn Shell(pdksh)
:许多开源类Unix系统(如GNU/Linux)都附带了公共领域Korn Shell,即pdksh。它以源代码形式提供,可从其主页http://web.cs.mun.ca/~michael/pdksh/ 开始获取。它附带了在各种Unix平台上进行编译和安装的说明。pdksh最初由Eric Gisin编写,基于Charles Forsyth的公共领域版本7 Bourne Shell克隆版。它大多与1988年的Korn Shell和POSIX兼容,并带有自身的一些扩展。
-
Z-Shell(zsh)
:zsh是一个强大的交互式Shell和脚本语言,具有许多在ksh、bash和tcsh中能找到的特性,以及一些独特的特性。zsh拥有ksh88的大部分特性,但ksh93的特性较少。它可以免费获取,并且几乎可以在任何现代Unix版本上进行编译和运行,也有针对其他操作系统的移植版本。zsh的主页是http://www.zsh.org/ 。
这两种Shell在《Learning the Korn Shell》(O’Reilly出版)中有更详细的描述。
9. 查看Shell版本
在使用不同的Shell时,有时需要知道它们的版本号,以下是查看几种常见Shell版本的方法:
| Shell名称 | 查看版本命令 | 示例输出 |
| ---- | ---- | ---- |
| bash |
bash --version
|
GNU bash, version 3.00.16(1)-release (i686-pc-linux-gnu)
|
| ksh(新版ksh93) |
ksh --version
|
version sh (AT&T Labs Research) 1993 - 12 - 28 p
|
| ksh(旧版) | 先输入
ksh
,再输入
^V
|
Version 11/16/88f
|
| pdksh |
echo 'echo $KSH_VERSION' | pdksh
|
@(#)PD KSH v5.2.14 99/07/13.2
|
| zsh |
echo 'echo $ZSH_VERSION' | zsh
|
4.1.1
|
似乎没有办法从
/bin/sh
获取版本号,这并不奇怪。大多数商业Unix系统上的真正Bourne Shell都源自System V Release 3(1987年)或Release 4(1989年)的Bourne Shell,从那时起几乎没有变化。希望提供符合POSIX标准Shell的商业供应商通常会通过改编某个版本的Korn Shell来实现这一目的。
10. Shell的初始化和终止
为了支持用户自定义,Shell在启动时会读取某些指定的文件,对于某些Shell,在终止时也会读取。每个Shell都有不同的约定,下面分别进行讨论。
如果编写供他人使用的Shell脚本,不能依赖于启动时的自定义设置。本文中开发的所有Shell脚本都会自行设置环境(例如
$PATH
的值),以便任何人都能运行它们。
Shell的行为取决于它是否是登录Shell。当坐在终端前,响应计算机的提示输入用户名和密码时,会得到一个登录Shell。同样,使用
ssh hostname
时也会得到一个登录Shell。但是,如果通过名称运行Shell,或者作为脚本初始
#!
行中指定的命令解释器隐式运行,或者创建一个新的工作站终端窗口,或者在远程Shell中运行命令(例如
ssh hostname command
),那么这个Shell就不是登录Shell。
Shell通过检查
$0
的值来确定它是否是登录Shell。如果该值以连字符开头,则该Shell是登录Shell;否则不是。可以通过以下简单实验来判断是否为登录Shell:
$ echo $0 # 显示Shell名称
-ksh # 是的,这是一个登录Shell
这个连字符并不意味着存在一个名为
/bin/-ksh
的文件,它只是表示父进程在运行
exec()
系统调用启动Shell时将第零个参数设置成了这样。
如果通常只使用单个Shell,那么以下各节中描述的初始化和终止文件不会有太大问题:一旦将它们进行了适当的自定义,可能多年都不需要再进行修改。但是,如果使用多个Shell,则需要更仔细地考虑如何设置自定义,以避免重复和维护麻烦。
.
(点)和
test
命令会很有帮助:在自定义脚本中使用它们来读取一小部分精心编写的、适合所有Bourne系列Shell以及所有可访问主机的文件。系统管理员还需要确保
/etc
中的系统范围自定义脚本适用于所有用户。
10.1 Bourne Shell(sh)启动
当Bourne Shell(sh)作为登录Shell时,它会执行以下操作:
test -r /etc/profile && . /etc/profile # 尝试读取 /etc/profile
test -r $HOME/.profile && . $HOME/.profile # 尝试读取 $HOME/.profile
也就是说,它可能会在当前Shell的上下文中读取两个启动文件,但不要求这两个文件都存在。注意,主目录下的文件是点文件,而
/etc
中的系统范围文件不是。
本地管理员创建的系统Shell启动文件可能如下所示:
$ cat /etc/profile # 显示系统Shell启动文件
PATH=/usr/local/bin:$PATH # 将 /usr/local/bin 添加到系统路径开头
export PATH # 使其对子进程可见
umask 022 # 移除组和其他用户的写权限
一个典型的
$HOME/.profile
文件可以使用如下命令修改本地系统的默认登录环境:
$ cat $HOME/.profile # 显示个人Shell启动文件
PATH=$PATH:$HOME/bin # 将个人bin目录添加到系统路径末尾
export PATH # 使其对子进程可见
11. 总结
POSIX标准为实现可移植的Shell脚本编写做出了很大努力。如果在其定义的范围内进行编写,就有机会编写出可移植的脚本。然而,现实世界仍然比较复杂。虽然bash和ksh93在POSIX的基础上提供了许多扩展,但这两个Shell之间并不总是100%兼容。即使在像
set
选项或保存Shell完整状态这样简单的领域,也有很多小细节需要注意。
shopt
命令可以控制bash的行为,特别推荐在交互式使用时启用
extglob
选项。
bash和ksh93有许多共同的扩展,这些扩展对Shell编程非常有用,如
select
循环、
[[...]]
扩展测试工具、扩展模式匹配、花括号扩展、进程替换和索引数组等。此外,还介绍了一些小而有用的杂项扩展,其中算术
for
循环和
((...))
算术命令可能最为显著。
可以从互联网上下载bash和ksh93的源代码,并展示了如何编译这两个Shell。还提到了另外两种流行的扩展Bourne风格的Shell,即pdksh和zsh。
展示了如何确定常用扩展Bourne风格Shell的版本,这在需要确切知道正在使用的程序时非常重要。
最后,不同的Bourne Shell语言实现有不同的启动和终止自定义功能及文件。供一般使用的Shell脚本不应依赖于每个用户设置的功能或变量,而应自行完成所有必要的初始化。
下面是一个总结整个流程的mermaid流程图:
graph LR;
A[进程管理] --> B[Shell语言];
B --> C[Shell使用注意事项];
C --> D[bash shopt命令];
D --> E[共同扩展select循环];
E --> F[下载源代码];
F --> G[其他扩展Shell];
G --> H[查看Shell版本];
H --> I[Shell初始化和终止];
I --> J[总结];
超级会员免费看
33万+

被折叠的 条评论
为什么被折叠?



