现在我们已经更加熟悉我们的环境,并且能够与我们的系统进行一些沟通,现在是时候更详细地研究我们可以启动的进程了。并非每个命令都会启动单个进程。有些命令会启动一系列进程,例如 mozilla;而另一些命令,例如 ls,则作为单个命令执行。
此外,Linux 基于 UNIX,在 UNIX 中,常见的策略是让多个用户在同一系统上同时运行多个命令。显然,必须采取措施让 CPU 管理所有这些进程,并且必须提供功能以便用户可以在进程之间切换。在某些情况下,即使启动进程的用户注销,进程也必须继续运行。用户需要一种重新激活中断进程的方法。
我们将在接下来的章节中解释 Linux 进程的结构。
交互式进程通过终端会话初始化和控制。换句话说,必须有人连接到系统才能启动这些进程;它们不会作为系统功能的一部分自动启动。这些进程可以在前台运行,占用启动程序的终端,并且只要此进程在前台运行,您就无法启动其他应用程序。或者,它们可以在后台运行,以便您启动程序的终端可以在程序运行时接受新命令。到目前为止,我们主要关注在前台运行的程序——运行它们所花费的时间太短以至于无法注意到——但是使用 less 命令查看文件是占用终端会话的一个很好的例子。在这种情况下,激活的程序正在等待您执行某些操作。该程序仍然连接到启动它的终端,并且该终端仅对输入此程序可以理解的命令有用。其他命令只会导致错误或系统无响应。
但是,当进程在后台运行时,用户不会被阻止在启动程序的终端中执行其他操作。
shell 提供了一个名为作业控制的功能,可以轻松处理多个进程。此机制在前台和后台之间切换进程。使用此系统,程序也可以立即在后台启动。
在后台运行进程仅对不需要用户输入(通过 shell)的程序有用。当预计作业执行需要很长时间时,通常会将作业置于后台。为了在输入命令后释放发出终端,添加一个尾随的 &。在示例中,在使用图形模式时,我们从现有终端窗口打开一个额外的终端窗口
billy:~> xterm & [1] 26558 billy:~> jobs [1]+ Running xterm & |
完整的作业控制功能在 bash Info 页面中详细解释,因此此处仅列出常用的作业控制应用程序
表 4-1. 控制进程
(部分)命令 | 含义 |
---|---|
regular_command | 在前台运行此命令。 |
command & | 在后台运行此命令(释放终端) |
jobs | 显示在后台运行的命令。 |
Ctrl+Z | 挂起(停止,但不退出)在前台运行的进程(挂起)。 |
Ctrl+C | 中断(终止并退出)在前台运行的进程。 |
%n | 每个在后台运行的进程都会被分配一个数字。通过使用 % 表达式,可以使用其编号来引用作业,例如 fg %2。 |
bg | 在后台重新激活挂起的程序。 |
fg | 将作业放回前台。 |
kill | 结束进程(另请参阅 bash 的 Info 页面中的 Shell 内置命令) |
更多实际示例可以在练习中找到。
大多数 UNIX 系统很可能能够运行 screen,当您实际上想要另一个 shell 来执行命令时,这非常有用。调用 screen 后,将创建一个新会话,其中包含指定的 shell 和/或命令,然后您可以将其移开。在这个新会话中,您可以执行您想做的任何事情。所有程序和操作都将独立于发出 shell 运行。然后,您可以分离此会话,而您在其中启动的程序将继续运行,即使您注销原始 shell,并且您可以随时重新启动您的 screen。
该程序起源于虚拟控制台尚未发明的时代,并且所有事情都需要使用一个文本终端完成。对于爱好者来说,即使我们已经拥有虚拟控制台将近十年了,它在 Linux 中仍然具有意义。
进程具有一系列特征,可以使用 ps 命令查看
进程 ID 或 PID:用于引用进程的唯一标识号。
父进程 ID 或 PPID:启动此进程的进程的编号 (PID)。
Nice 值:此进程对其他进程的友好程度(不要与进程优先级混淆,进程优先级是根据此 nice 值和进程最近的 CPU 使用率计算得出的)。
终端或 TTY:进程连接到的终端。
真实用户和有效用户的用户名(RUID 和 EUID):进程的所有者。真实所有者是发出命令的用户,有效用户是确定系统资源访问权限的用户。RUID 和 EUID 通常相同,并且进程具有与发出用户相同的访问权限。一个例子可以澄清这一点:浏览器 mozilla 在/usr/bin由用户 root 拥有
theo:~> ls -l /usr/bin/mozilla -rwxr-xr-x 1 root root 4996 Nov 20 18:28 /usr/bin/mozilla* theo:~> mozilla & [1] 26595 theo:~> ps -af UID PID PPID C STIME TTY TIME CMD theo 26601 26599 0 15:04 pts/5 00:00:00 /usr/lib/mozilla/mozilla-bin theo 26613 26569 0 15:04 pts/5 00:00:00 ps -af |
当用户 theo 启动此程序时,进程本身以及初始进程启动的所有进程都将由用户 theo 而不是系统管理员拥有。当 mozilla 需要访问某些文件时,该访问权限将由 theo 的权限而不是 root 的权限确定。
真实和有效组所有者(RGID 和 EGID):进程的真实组所有者是启动进程的用户的基本组。有效组所有者通常相同,除非 SGID 访问模式已应用于文件。
ps 命令是用于可视化进程的工具之一。此命令有多个选项可以组合使用以显示不同的进程属性。
在未指定任何选项的情况下,ps 仅提供有关当前 shell 和最终进程的信息
theo:~> ps PID TTY TIME CMD 4245 pts/7 00:00:00 bash 5314 pts/7 00:00:00 ps |
由于这没有提供足够的信息 - 通常,您的系统上至少运行着数百个进程 - 我们通常会从所有进程列表中选择特定的进程,在管道中使用 grep 命令,请参阅 第 5.1.2.1 节,如本行所示,它将选择并显示特定用户拥有的所有进程
ps-ef| grepusername
此示例显示了进程名称为 bash 的所有进程,bash 是 Linux 系统上最常见的登录 shell
theo:> ps auxw | grep bash brenda 31970 0.0 0.3 6080 1556 tty2 S Feb23 0:00 -bash root 32043 0.0 0.3 6112 1600 tty4 S Feb23 0:00 -bash theo 32581 0.0 0.3 6384 1864 pts/1 S Feb23 0:00 bash theo 32616 0.0 0.3 6396 1896 pts/2 S Feb23 0:00 bash theo 32629 0.0 0.3 6380 1856 pts/3 S Feb23 0:00 bash theo 2214 0.0 0.3 6412 1944 pts/5 S 16:18 0:02 bash theo 4245 0.0 0.3 6392 1888 pts/7 S 17:26 0:00 bash theo 5427 0.0 0.1 3720 548 pts/7 S 19:22 0:00 grep bash |
在这些情况下,在具有大量空闲时间的系统上,通常也会显示查找包含字符串 bash 的行的 grep 命令。如果您不希望发生这种情况,请使用 pgrep 命令。
Bash shell 是一个特例:此进程列表还显示了哪些是登录 shell(您必须在其中提供用户名和密码,例如当您以文本模式登录或进行远程登录时,与非登录 shell 相对,例如通过单击终端窗口图标启动)。此类登录 shell 以破折号 (-) 开头。
![]() | |? |
---|---|
我们将在下一章中解释 | 运算符,请参阅第 5 章。 |
更多信息可以通过常用方式找到:ps --help 或 man ps。GNU ps 支持不同样式的选项格式;以上示例不包含错误。
请注意,ps 仅给出活动进程的瞬时状态,它是一次性记录。top 程序通过每五秒更新一次 ps(带有一堆选项)给出的结果来显示更精确的视图,定期生成导致最重负载的进程的新列表,同时整合有关正在使用的交换空间和 CPU 状态的更多信息,从proc文件系统
12:40pm up 9 days, 6:00, 4 users, load average: 0.21, 0.11, 0.03 89 processes: 86 sleeping, 3 running, 0 zombie, 0 stopped CPU states: 2.5% user, 1.7% system, 0.0% nice, 95.6% idle Mem: 255120K av, 239412K used, 15708K free, 756K shrd, 22620K buff Swap: 1050176K av, 76428K used, 973748K free, 82756K cached PID USER PRI NI SIZE RSS SHARE STAT %CPU %MEM TIME COMMAND 5005 root 14 0 91572 15M 11580 R 1.9 6.0 7:53 X 19599 jeff 14 0 1024 1024 796 R 1.1 0.4 0:01 top 19100 jeff 9 0 5288 4948 3888 R 0.5 1.9 0:24 gnome-terminal 19328 jeff 9 0 37884 36M 14724 S 0.5 14.8 1:30 mozilla-bin 1 root 8 0 516 472 464 S 0.0 0.1 0:06 init 2 root 9 0 0 0 0 SW 0.0 0.0 0:02 keventd 3 root 9 0 0 0 0 SW 0.0 0.0 0:00 kapm-idled 4 root 19 19 0 0 0 SWN 0.0 0.0 0:00 ksoftirqd_CPU0 5 root 9 0 0 0 0 SW 0.0 0.0 0:33 kswapd 6 root 9 0 0 0 0 SW 0.0 0.0 0:00 kreclaimd 7 root 9 0 0 0 0 SW 0.0 0.0 0:00 bdflush 8 root 9 0 0 0 0 SW 0.0 0.0 0:05 kupdated 9 root -1-20 0 0 0 SW< 0.0 0.0 0:00 mdrecoveryd 13 root 9 0 0 0 0 SW 0.0 0.0 0:01 kjournald 89 root 9 0 0 0 0 SW 0.0 0.0 0:00 khubd 219 root 9 0 0 0 0 SW 0.0 0.0 0:00 kjournald 220 root 9 0 0 0 0 SW 0.0 0.0 0:00 kjournald |
top 的第一行包含与 uptime 命令显示的信息相同的信息
jeff:~> uptime 3:30pm, up 12 days, 23:29, 6 users, load average: 0.01, 0.02, 0.00 |
这些程序的数据存储在其他位置,包括/var/run/utmp(有关当前连接用户的信息)和虚拟文件系统中/proc,例如/proc/loadavg(平均负载信息)。有各种图形应用程序可以查看此数据,例如 Gnome System Monitor 和 lavaps。在 FreshMeat 和 SourceForge 上,您会找到数十个应用程序,它们将此信息与来自一台(Web)服务器上的多台服务器的其他服务器数据和日志集中在一起,从而可以从一个工作站监控整个 IT 基础设施。
进程之间的关系可以使用 pstree 命令可视化
sophie:~> pstree init-+-amd |-apmd |-2*[artsd] |-atd |-crond |-deskguide_apple |-eth0 |-gdm---gdm-+-X | `-gnome-session-+-Gnome | |-ssh-agent | `-true |-geyes_applet |-gkb_applet |-gnome-name-serv |-gnome-smproxy |-gnome-terminal-+-bash---vim | |-bash | |-bash---pstree | |-bash---ssh | |-bash---mozilla-bin---mozilla-bin---3*[mozilla-bin] | `-gnome-pty-helper |-gpm |-gweather |-kapm-idled |-3*[kdeinit] |-keventd |-khubd |-5*[kjournald] |-klogd |-lockd---rpciod |-lpd |-mdrecoveryd |-6*[mingetty] |-8*[nfsd] |-nscd---nscd---5*[nscd] |-ntpd |-3*[oafd] |-panel |-portmap |-rhnsd |-rpc.mountd |-rpc.rquotad |-rpc.statd |-sawfish |-screenshooter_a |-sendmail |-sshd---sshd---bash---su---bash |-syslogd |-tasklist_applet |-vmnet-bridge |-xfs `-xinetd-ipv6 |
The-uand-a选项提供附加信息。有关更多选项及其作用,请参阅 Info 页面。
在下一节中,我们将看到一个进程如何创建另一个进程。
新进程的创建是因为现有进程创建了自身的精确副本。此子进程与父进程具有相同的环境,只是进程 ID 号不同。此过程称为forking(派生)。
在派生过程之后,子进程的地址空间被新进程数据覆盖。这是通过对系统的 exec 调用完成的。
因此,fork-and-exec 机制将旧命令切换为新命令,而新程序在其中执行的环境保持不变,包括输入和输出设备的配置、环境变量和优先级。此机制用于创建所有 UNIX 进程,因此也适用于 Linux 操作系统。即使是第一个进程 init(进程 ID 为 1)也是在所谓的引导过程中在启动过程中派生的。
此方案说明了 fork-and-exec 机制。进程 ID 在派生过程后更改
在某些情况下,init 成为进程的父进程,而该进程不是由 init 启动的,正如我们在 pstree 示例中已经看到的那样。例如,许多程序守护进程化它们的子进程,以便它们可以在父进程停止或被停止时继续运行。窗口管理器是一个典型的例子;它启动一个 xterm 进程,该进程生成一个接受命令的 shell。然后,窗口管理器拒绝承担任何进一步的责任,并将子进程传递给 init。使用此机制,可以在不中断正在运行的应用程序的情况下更改窗口管理器。
偶尔,即使在良好的家庭中,事情也会出错。在特殊情况下,进程可能会在父进程不等待此进程完成的情况下完成。这种未埋葬的进程称为僵尸进程。
正如上一章中所承诺的,我们现在将更详细地讨论特殊模式 SUID 和 SGID。这些模式的存在是为了让普通用户能够执行他们通常无法执行的任务,因为基于 UNIX 的系统上使用了严格的文件权限方案。在理想情况下,特殊模式应尽可能少地使用,因为它们包含安全风险。Linux 开发人员通常会尽可能避免使用它们。例如,Linux ps 版本使用存储在/proc文件系统中的信息,每个人都可以访问该信息,从而避免将敏感系统数据和资源暴露给公众。在此之前,在较旧的 UNIX 系统上,ps 程序需要访问诸如/dev/memand/dev/kmem之类的文件,由于这些文件的权限和所有权,这存在缺点
rita:~> ls -l /dev/*mem crw-r----- 1 root kmem 1, 2 Aug 30 22:30 /dev/kmem crw-r----- 1 root kmem 1, 1 Aug 30 22:30 /dev/mem |
对于旧版本的 ps,除非对其应用特殊模式,否则普通用户无法启动该程序。
虽然我们通常尝试避免应用任何特殊模式,但在某些情况下,有必要使用 SUID。一个例子是更改密码的机制。当然,用户希望自己执行此操作,而不是让系统管理员设置他们的密码。我们知道,用户名和密码列在/etc/passwd文件中,该文件具有以下访问权限和所有者
bea:~> ls -l /etc/passwd -rw-r--r-- 1 root root 1267 Jan 16 14:43 /etc/passwd |
但是,用户需要能够在此文件中更改自己的信息。这是通过为 passwd 程序提供特殊权限来实现的
mia:~> which passwd passwd is /usr/bin/passwd mia:~> ls -l /usr/bin/passwd -r-s--x--x 1 root root 13476 Aug 7 06:03 /usr/bin/passwd* |
调用时,passwd 命令将使用 root 的访问权限运行,从而使普通用户能够编辑系统管理员拥有的密码文件。
文件上的 SGID 模式不如 SUID 频繁出现,因为 SGID 通常涉及创建额外的组。但是,在某些情况下,我们必须经历这种麻烦才能构建一个优雅的解决方案(不要对此过于担心 - 必要的组通常在安装时创建)。write 和 wall 程序就是这种情况,它们用于向其他用户的终端 (tty) 发送消息。write 命令向单个用户写入消息,而 wall 命令向所有连接的用户写入消息。
通常不允许将文本发送到另一个用户的终端或图形显示器。为了绕过这个问题,创建了一个组,该组拥有所有终端设备。当授予 write 和 wall 命令 SGID 权限时,这些命令将使用适用于此组的访问权限运行,在本例中为 tty。由于此组对目标终端具有写入权限,因此即使是没有权限以任何方式使用该终端的用户也可以向其发送消息。
在下面的示例中,用户 joe 首先使用 who 命令查明他的通信员连接到哪个终端。然后,他使用 write 命令向她发送消息。还说明了 write 程序和接收用户占用的终端的访问权限:很明显,除了组所有者之外,其他用户对该设备没有任何权限,组所有者可以写入该设备。
joe:~> which write write is /usr/bin/write joe:~> ls -l /usr/bin/write -rwxr-sr-x 1 root tty 8744 Dec 5 00:55 /usr/bin/write* joe:~> who jenny tty1 Jan 23 11:41 jenny pts/1 Jan 23 12:21 (:0) jenny pts/2 Jan 23 12:22 (:0) jenny pts/3 Jan 23 12:22 (:0) joe pts/0 Jan 20 10:13 (lo.callhost.org) joe:~> ls -l /dev/tty1 crw--w---- 1 jenny tty 4, 1 Jan 23 11:41 /dev/tty1 joe:~> write jenny tty1 hey Jenny, shall we have lunch together? ^C |
用户 jenny 在她的屏幕上得到这个
Message from joe@lo.callhost.org on ptys/1 at 12:36 ... hey Jenny, shall we have lunch together? EOF |
收到消息后,可以使用 Ctrl+L 组合键清除终端。为了不接收任何消息(系统管理员的消息除外),请使用 mesg 命令。要查看哪些连接的用户接受来自其他用户的消息,请使用 who -w。所有功能都在每个命令的 Info 页面中完整解释。
![]() | 组名称可能有所不同 |
---|---|
组方案特定于发行版。其他发行版可能使用其他名称或其他解决方案。 |