Skip to content

Latest commit

 

History

History
358 lines (211 loc) · 24.4 KB

精通Linux(第2版).md

File metadata and controls

358 lines (211 loc) · 24.4 KB

精通Linux(第2版)

Brian Ward

第1章 概述

理解操作系统工作原理最好的方法是抽象思维,换句话说,你可以暂时忽略大部分细节。

1.1 Linux 操作系统中的抽象级别和层次

硬件系统之上是内核,它是操作系统的核心。内核是运行在内存中的软件,它向中央处理器发送指令。内核管理硬件系统,是硬件系统和应用程序之间进行通信的接口。

内核和用户进程之间最主要的区别是:内核在内核模式(kernel mode)中运行,而用户进程则在用户模式(user mode)中运行。在内核模式中运行的代码可以不受限地访问中央处理器和内存,这种模式功能强大,但也非常危险,因为内核进程可以轻而易举地使整个系统崩溃。那些只有内核可以访问的空间我们称为内核空间(kernel space)。

1.4 用户空间

内核分配给用户进程的内存我们称之为用户空间。因为一个进程简单说就是内存中的一个状态。用户空间也可以指所有用户进程占用的所有内存。(用户空间还有一个不太正式的名称,叫userland。)

3.1 设备文件

套接字设备 套接字设备是跨进程通信经常用到的特殊接口。它们经常会存放于/dev目录之外。套接字文件代表Unix域套接字,

6.4 systemd

systemd配置文件分散在系统的很多目录中,不止一处。但主要是分布在两个地方:系统单元目录(全局配置,一般是/usr/lib/systemd/system)和系统配置目录(局部配置,一般是/etc/systemd/system)。 简单来说,记住这个原则即可:不要更改系统单元目录,因为它由系统来维护。可以在系统配置目录中保存你的自定义设置。在选择更改/usr还是更改/etc时,永远选择/etc。

systemctl -p UnitPath show

该设置信息来自pkg-config。你可以使用以下命令来查看系统单元和配置目录: $ pkg-config systemd --variable=systemdsystemunitdir $ pkg-config systemd --variable=systemdsystemconfdir

8.3 跟踪程序执行和系统调用

strace能够显示进程涉及的所有系统调用。可以通过下面的命令查看:  $ strace cat /dev/null

9.5 基本ICMP和DNS工具

因为6和7之间的有个很大的延时,所以那部分可能是一个长途的连接。

9.6 物理层与以太网

❑ 以太网上的设备以帧的形式发送信息,帧里除了实际的数据,还有发送者和接收者的MAC地址。

9.7 理解内核网络接口

Linux内核自有一套用于沟通这两层的方法,叫作(内核)网络接口。所谓配置网络接口,就是把网际层的IP地址跟物理层的硬件标识对应起来。

9.8 配置网络接口

这里interface是网络接口的名字,例如eth0。当网络接口能用了的时候,就可以添加路由了(其实就是设置默认网关): 

route add default gw gw-address

参数gw-address是你默认网关的IP地址,它必须处于你其中一个网络接口的地址和掩码所定义的子网之中。

9.9 开机启动的网络配置

就像Ubuntu的ifupdown套装是用/etc/network的配置文件,但Fedora则用/etc/sysconfig/network-scripts。

9.12 解析主机名

9.12.2 resolv.conf文件 传统的做法是在/etc/resolv.conf文件中指定DNS服务器。简单的话,会像以下这样,这里ISP的DNS服务器为10.32.45.23和10.3.2.3:

9.13 Localhost

lo接口是一个虚拟的网络接口,它叫作环回(loopback),因为它指向的是自己。连接127.0.0.1,其实就是连接本机。当发送数据到内核网络接口lo,内核只会将其重新包装,并通过lo回复。

9.14 传输层:TCP、UDP和Service

作为一种传输层协议,TCP之所以流行是因为它对应用没什么要求。应用的进程只需要知道如何打开(或监听)、读取、写入和关闭连接即可。对应用来说,这一过程就像收发数据流一样,而进程的工作就跟处理文件一样简单。

9.20 路由器与Linux

这类路由器一面世,就吸引了很多人去搞硬件。有个制造商,Linksys,被要求开放其某个软件的源代码,开放之后,便出现了一个Linux发行版,叫作OpenWRT。(WRT是Linksys的某个产品型号。)

9.23 无线以太网

Linux主要依靠一个叫wpa_supplicant守护进程来管理无线网络接口的认证和加密,以保证无线网络安全。这个守护进程可以处理WPA(WiFi Protected Access, WiFi网络安全接入)和WPA2的认证机制,以及几乎所有的无线网络加密技术。首次启动时,它会读取配置文件(默认是/etc/wpa_supplicant.conf),然后尝试在所指定的网络中向接入点提供自己的信息,并与其建立连接。该系统的帮助文档很详尽,尤其是wpa_supplicant(1)和wpa_supplicant.conf(5)帮助手册中都有详细描述。

10.1 服务的基本概念

TCP服务是最好理解的概念之一,因为它建立在简单、无中断的双路数据流之上。或许最佳的解释方式,是让你直接通过TCP与某个Web服务器的80端口沟通,去看看数据是如何在该连接上移动的。

10.2 网络服务器

大多数网络服务器有一个共同的特性,即它们通常是多进程的。其中至少有一个进程在监听网络端口,而当它接收到一个新的连接时,就会使用fork()来创建一个子进程,负责那个新的连接。该子进程,也叫辅助进程,会随着连接的终止而终止。同时,监听进程会继续接收连接。这样,一个服务器就能轻松地处理多个连接,一般不会有什么问题。

10.3 SSH

现在我们来详细看一下独立的Secure shell(以下简称SSH)服务器。SSH是最常见的网络服务应用之一,它是一种远程连接Unix机器的标准。配置好之后,我们就能通SSH进行安全的shell登录、执行远程程序、共享简单的文件等。此外,SSH还凭借公钥认证和简单的会话加密,取代了旧的、不安全的远程登录系统telnet和rlogin。

OpenSSH的客户端是ssh,服务器是sshd。SSH协议有两个主要版本:1和2。OpenSSH对两者都支持,但1是很少用的。 SSH的功能和特性使它能做到以下事情。 ❑ 对密码和会话内容加密,保护你不受窃听困扰。

但SSH也有缺点。其中一个就是,若想建立SSH连接,你必须先知道远程主机的公钥,它是不需要通过什么保密的渠道就能获得的(当然你也可以手动检查它的真假)

10.3.1 SSHD服务器 运行sshd需要一个配置文件以及主机密钥。大多数发行版都将配置文件放在/etc/ssh配置目录中,并在你安装它们的sshd包时,尝试将一切都配置好。(这里的配置文件名sshd_config跟客户端的文件名ssh_config很容易混淆,请注意区分。)

通常这表示远程主机的管理员更改了密钥(经常是在更换硬件时发生)。为了确认状况,不妨跟管理员联系一下。总之,以上的信息告诉你,错误的密钥是在用户的.ssh/known_hosts文件的第12行,如上例中➊处所示。 如果你确定没问题,那就移除错误的那行,或换一个正确的公钥。

OpenSSH包含了文件传输程序:scp和sftp。这两个命令用以取代旧的、不安全的命令rcp和ftp。

sftp程序就好比是命令行的ftp客户端,它有get和put命令。远程主机必须装好sftp-server程序(如果远端装了OpenSSH,那通常都会带上这个)。

10.5 诊断工具

10.5.2 tcpdump 如果你想明确地知道你的网络上有什么在流通,你可用tcpdump将网络接口置于混杂模式,并向你报告每一个通过的数据包

试试netcat(或nc)。netcat可以与TCP和UDP端口通信,指定本地端口,监听端口,扫描端口,对网络输入输出重定向到标准输入输出等等。用netcat连接TCP端口,如下所示:

10.6 远程程序调用

节提到的rpcbind服务是什么意思呢?RPC意指远程程序调用(Remote Procedure Call),是应用层中较低层的一个系统。它是为了方便程序员访问网络应用而设计的,能使本地程序调用远程程序(按程序号来标识),让远程程序返回结果码或信息。

RPC是一种难以消亡的协议。网络文件系统(Network File System,以下简称NFS)和网络信息服务(Network Information Service,以下简称NIS)就用得到它,但单机环境下是不需要这些服务的

10.7 网络安全

❑ 拒绝服务(Denial-of-service,简称DoS)攻击:这使得机器无法继续提供服务,或无需通过登录就用某些方法造成机器故障。这种攻击更难防范,但很容易应对。

10.8 前瞻

如果你对一些复杂的网络服务器感兴趣,这里有两个常见的:Apache网页服务器和Postfix邮件服务器。

10.9 套接字:进程与网络的通信方式

看看进程是怎样从网络读数据,以及怎样向网络写数据的。向建立好的连接读写数据是很容易的,只需要做一些系统调用(参考recv(2)和send(2)帮助手册)工作。

在Unix上,进程是用套接字来标识与网络通信的时机与方式的。套接字是进程通过内核访问网络的接口,它代表用户空间与内核空间的边界。它也常被用于进程间通信(Interprocess Communication,以下简称IPC)。

因为进程需要以不同的方式访问网络,所以套接字也分不同种类。例如,TCP连接会用流式套接字(程序员称之为SOCK_STREAM),而UDP连接则用数据报套接字(SOCK_DGRAM)。

10.10 Unix域套接字

进程间的通信可以使用本地主机(127.0.0.1)来做常规的网络通信,但我们一般使用另一种特别的套接字,这种套接字在第三章中简单提到过,叫Unix域套接字。进程与Unix域套接字的连接几乎与网络套接字连接一样:进程可以监听和接收套接字上的连接,还可以选择不同类型的套接字来实现TCP式或UDP式的工作方式。

进程可以创建非命名Unix域套接字,并与其他进程分享它的地址。

开发者们喜欢Unix域套接字的以下两点。第一,开发者可以通过管理套接字文件的访问权限来管理Unix域套接字的访问权限。也就是说,不能访问某个套接字文件的进程,也就不能使用该套接字

第二,因为Linux内核与Unix域套接字通信并不需要经历网络层次,所以性能会比网络套接字好。

所以有些网络服务器会同时提供网络套接字和Unix域套接字的连接。例如,MySQL的数据库服务器mysqld能接受远端的连接,同时也以/var/run/mysqld/mysqld.sock提供Unix域套接字。

10.10.2 列出Unix域套接字 你可以使用lsof -U来查看系统正在使用中的Unix域套接字: 

lsof -U

11.1 shell脚本基础

其中的#!叫作shebang

shell脚本的主要优点是,它能使任务简单化、自动化,而不用你在命令行提示符下一条条地敲命令(这对批量处理文件很方便)。

如果你要分解字符串、做繁复的数学计算、做复杂的数据库交互,或者你想写函数以及复杂的控制结构,你最好还是用Python、Perl之类的脚本语言,或者awk,甚至C这样的编译型语言。

最后,注意你shell脚本的大小,尽量写得短小一些。Bourne shell脚本不应该写太大(虽然有人还是会写很大)。

11.2 引号与字面量

因为shell看到了$1,它是一个shell变量(这很快会讲到)。于是你可能会想给它加上双引号,让shell不管$1。但这样也不行:

操作脚本或命令行时,先想想shell是如何执行一条命令的。 (1) 在执行之前,shell会查找其中的变量、通配符以及其他代词,如果有的话,就将它们进行替代。 (2) 将替换后的结果返回给命令。

大多数时候这样是行得通的,但有时却神秘地失效了。为什么呢?问题可能在于你当前的目录。如果当前目录包含名字如r.input和r.output的文件,那么shell就会将r.*t扩展为r.input和r.output,命令就会变成:  $ grep r.input r.output /etc/passwd

避免这种问题的关键是,找出可能被扩展的字符,然后用正确的引号来保护它。

当你想使用字面量时,请优先考虑单引号,它保证shell不会做任何替换,因此语法上也十分整洁。然而,可能有时你的需求会有点复杂,那么再考虑双引号。

稀奇的是,双引号里可以用单引号,就像下面的例子(效果跟上面例子一样):

11.3 特殊变量

shell的内置命令shift能删除第一个参数$1,并用后面的补上。说具体一点,就是$2变成$1,$3变成$2,

其中提到的2>&1能让标准错误重定向到标准输出

11.3.5 进程号:$$ $$变量持有shell的进程号。 11.3.6 退出码:$? $?变量持有shell执行的最后一条命令的退出码。

11.4 退出码

要注意的是,有些程序,如diff和grep,正常退出时也会使用非零的退出码。例如,grep会在匹配时返回0,不匹配时返回1。对于这些程序来说,退出码1不代表出错;grep和diff使用2来代表出错。

11.5 条件判断

以上脚本的if、then、else和fi是shell关键字,其余都是命令。这样区分是很重要的,如[ $1="hi" ]就是一条命令,其中[字符是Unix系统中的一个实际存在的程序,而不是shell的语法。(其实这种说法不完全准确,很快你就会看到。但暂时先把它当作命令。)所有Unix系统都有一个命令叫[,它能进行条件测试。这个程序还有另外一个名字,叫test。对比[和test,会发现它们指向同一个inode,或者其中一个是另一个的符号链接。

(1) shell执行if关键字之后的命令,获取了其退出码; (2) 如果退出码是0, shell就会接着执行then关键字后的命令,直至遇到else或fi关键字; (3) 如果退出码为非0,并且有else,就会执行else后的命令;

11.5.1 防范空参数 上例的条件判断还有个小问题,即$1可能会是空的,因为用户可能没有输入参数。没有参数的话,该脚本就会变成[=hi ],令[出错并中止。你可用以下其中一种方法(两种都很常见)来修复:  if [ "$1" = hi ]; then if [ x"$1" = x"hi" ]; then

除了[,还是有很多其他命令可用于测试的。以下就是一个用grep来测试的例子:  #! /bin/sh if grep -q daemon /etc/passwd; then echo The daemon user is in the passwd file.

你可在参数前加运算符!来测试相反的情况。例如,[ ! -f file ]会在file不是普通文件的情况下返回true。此外,-a和-o对应“and”和“or”运算符(例如[ -f file1 -a file2 ])。

文件测试 大多数文件运算符,像-f,被称为一元运算符,因为他们只要求一个参数:被测试的文件。例如下面这两个重要的运算符: ❑ -e:文件存在时返回true; ❑ -s:文件非空时返回true。

如果file1的修改时间比file2要新,命令就返回true。而-ot(意思是“旧于”)则相反。想检测一个文件是否是另一文件的硬链接,可用-ef。

❑ -z:参数为空时返回true([ -z "" ]返回0); ❑ -n:参数为非空时返回true([ -n " " ]返回1)。

你需要认识到,等号(=)是用于检验字符串相等性的,而不是数字。因此,[ 1=1 ]返回0(true),但[ 01=1 ]返回false。当你想要检验两个数字是否相等时,请用-eq,而非等号:[ 01-eq 1 ]会返回true。表11-3展示了所有比较数字的运算符。

案例可以是单个字符串(如上例的bye)或者是用|分隔的多个字符串(如果$1是hi或hello,则hi|hello返回true),你也可以使用或?模式(如what)。若想定义一个特定案例以外的默认案例,可用*,如上面代码的最后一个案例。

11.7 命令替换

命令替换的传统做法是使用反引号(``)包围命令,你会在很多shell脚本中看到。$()语法是一种较新的形式,但它符合POSIX标准,所以更容易阅读和编写。

11.8 管理临时文件

以下展示了如何使用mktemp命令来创建临时文件名。

给予mktemp的参数是一个模板。mktemp会将XXXXXX转换成一个唯一的字符串,并以该名字创建出一个空文件。注意,该脚本将文件名保存在变量中,所以改文件名的话就只需要改一行而已。

mktemp不一定要有参数。无参数的时候,模板会以/tmp/tmp.前缀开头。

11.10 重要的shell脚本工具

11.10.1 basename 想要去掉文件的扩展名,或者去掉路径全名中的目录部分,可以使用basename。

以上两条命令都会返回example。其中第一条会将example.html中的后缀.html去掉

awk的功能并不单一,实际上它是一种很强大的编程语言。不幸的是,它就像一门失落的艺术,正被更大型的语言(如Python)所取代。

当你把海量的文件当作一个命令的参数时,该命令或者shell可能会告诉你缓冲不足以容纳这些参数。解决这个问题,可用xargs,它能对自身输入流的每个文件名逐个地执行命令。

上例,xargs会执行file命令。不过,这样执行的话可能会出错,或造成安全问题,因为文件名可能包含空格或换行符。所以,请改用以下形式。这样,find的输出和xargs的参数的分界符就会是空字符,而不是换行符了。  $ find . -name '*.gif' -print0 | xargs -0 file

如果需要在shell脚本中进行算术操作,可使用expr(它甚至能进行字符串操作)。例如,命令expr 1 + 2会输出3。(执行expr --help,查看所有的运算符。) 其实expr做算术操作效率很低。如果你经常要做算术操作,你可以改用Python,而不用shell脚本。

11.11 子shell

将命令置于括号中,即可运行子shell。例如下面这行命令,它在uglydir目录里运行了uglyprogram,而并不影响源shell:  $ (cd uglydir; uglyprogram)

11.14 什么时候(不)应该使用shell脚本

然而,在某些情况下(尤其是你准备使用read时),你应该反问一下自己,你是否在使用正确的工具来完成这项工作。请记住shell脚本的强项:操控简单的文件和命令。如前面提到的,当你发现你的脚本写得有点繁琐,特别是涉及复杂的字符串或数学处理时,或许你就该试试Python、Per或awk之类的脚本语言了

12.2 rsync

如果你想移动整个目录,可用scp -r。如果想提高速度,那就用tar和管道:  $ tar cBvf - directory| ssh remote_host tar xBvpf -

rsync是Linux平台上标准的同步工具,它功能多样,性能强大。接下来我们会介绍一些实用的rsync操作模式和它的一些奇特之处。 12.2.1 rsync基础 要使rsync在两台主机之间运作,必须让它在源机器和目标机器上面都安装。

想要完整地、递归地传输整个目录——包括符号链接、权限、模式、设备——那就用-a选项。还有,如果你想复制到其他地方,而不是你的home目录,你可以在host后面加上具体地址,就像这样:

复制目录是需要技巧的,如果你不清楚传输过程中会发生什么,你可以使用-nv选项组合。-n选项会让rsync进入“空跑”模式,即模拟复制而非真的复制。-v选项则是冗长模式,它会将涉及的文件和传输过程的细节显示出来:  $ rsync -nva dir host:destination_dir 输出大概会是这样:

12.2.4 排除文件与目录 rsync有个很重要的功能,就是它可以排除某些文件与目录的传输。比如说,你想将一个本地目录src传输到host,但想排除其中叫.git的任何东西。那你可以这样做:  $ rsync -a --exclude=.git src host:

很多人喜欢在用-a的时候加上-z,以在传输前先进行压缩:

12.5 NFS客户端

Unix系统中标准的文件共享系统是NFS。不同版本的NFS适用于不同的情境。你可以通过TCP和UDP来提供NFS服务,并加上一大堆验证和加密技术。正因为选择繁多,所以NFS是个很大的话题,所以我们只讲讲NFS客户端的一些基本知识。

13.3 shell启动文件的元素

shell启动文件中最重要的就是命令路径。该路径应包含用户要用到的所有应用程序所在的目录。至少,按照顺序应包含这些:  /usr/local/bin /usr/bin /bin 这个顺序可确保你能使用/usr/local中的特定变体来覆盖默认程序。

如果你对系统工具感兴趣(例如traceroute、ping和lsmod),可以为路径加上sbin目录:  /usr/local/sbin /usr/sbin /sbin

14.5 D-Bus

D-Bus(即桌面总线)是Linux桌面系统的最重要的产物之一,它是一个消息传递系统。D-Bus之所以重要,是因为它作为一种进程间通信的机制,使得各种桌面应用能够相互沟通。同时,大多数的Linux系统都是用它来把系统事件(例如插入USB设备)通知给进程的。

D-Bus本身包含有支持任何两个进程相互沟通的库,其中定义了规范进程间通信的协议。该库其实只是一种进程间通信方式,就像Unix域套接字。它的重点是一个叫dbus-daemon的“中央槽”。需要对某些事件做出反应的进程,可以到dbus-deamon上注册,然后就能收到想要的事件通知了

17.1 Web服务器与应用

Web服务器本身做的事情不多——就是提供文件服务。Apache等大多数Web服务器的目标都是为Web应用提供底层平台。例如,Wikipedia是来自MediaWiki包的,它能让你建立自己的wiki。而Wordpress和Drupal之类的内容管理系统则可以让你建立自己的博客和媒体网站。

17.2 数据库

数据库是存储和查阅数据的专业工具,而Linux上可用的数据库有很多种。数据库受人喜爱的地方有两点:它管理数据的方式简单统一,而且操作高效。

有了数据库,查询和修改数据变得更方便,尤其是相对于文本解析和修改来说。例如,在网络上使用/etc/passwd和/etc/shadow是很麻烦的。解决方法是,你可以建立一个提供用户信息的LDAP(Lightweight Directory Access Protocol,轻量目录访问协议)数据库来支持Linux的认证系统。客户端的配置是很简单的,你只需编辑一下/etc/nsswitch.conf文件并加一些额外的配置即可。

数据库之所以能高效地读取数据,是因为它采用索引来记录数据的位置。比如你有一堆数据,里面是姓、名、电话号码的对应关系。

第8章谈到的硬盘和内存性能问题对于大部分的数据库实现来说都是非常重要的,因为你需要权衡RAM(RAM的存取更快速)和硬盘各要存储多少数据。很多大型数据库系统还涉及网络问题,因为它可能分布在多个服务器上。最常见的网络装置就是复制(replication),其做法就是将一个数据库复制到多个服务器上,使该系统能够接受更多的客户端连接。

17.3 虚拟化

而虚拟机技术则可以支持在同一套硬件上安装多于一个操作系统(被称为来宾),并能让你随心所欲地启动和关闭它们。你甚至还可以把一个虚拟机从一台机器复制到另一台机器上。

管理虚拟机的软件叫作hypervisor。hypervisor能操控本书提及的Linux底层的各个部分,令虚拟机的使用与物理机无异。

17.4 分布式计算与实时计算

其中的基础设施即服务(以下简称IaaS)就是一种能让你指定和使用远端CPU、内存、存储和网络等基础计算资源的系统。OpenStack项目就是这样一个包含了IaaS的接口平台。

17.5 嵌入式系统

诸如音乐播放器、视频播放器、恒温器等提供特定用途的系统都是嵌入式系统。你可拿它跟桌面或服务器系统(能做各种事情但并不精于某一项的系统)对比一下

你可以认为嵌入式系统是与分布式系统相反的,它不但没有打算扩展系统的规模,反而总是希望缩减它,以放进一个小的设备中。当今最流行的嵌入式Linux应该是Android了。

例如,路由器与桌面机器相比需要更多的网络端口,而完全不需要视频或音频硬件。有了定制的硬件之后,还要有定制的软件,例如定制操作系统和用户界面。第9章提到的OpenWRT就是一个这样的Linux定制发行版。