Linux 软件开发人员指南:2 与进程合作
sinye56 2024-12-25 15:19 16 浏览 0 评论
作为开发者,您已经直观地熟悉了进程。它们是您劳动的成果:编写和调试代码之后,您的程序最终执行,转变成美丽的操作系统进程!
在 Linux 上,进程可以是长期运行的应用程序,一个快速的 shell 命令如 ls,或者任何内核生成来完成系统上一些工作的实体。如果 Linux 中有事情在进行,那么一个进程正在做它。您的网页浏览器、文本编辑器、漏洞扫描器,甚至像读取文件和您迄今为止学到的命令都会生成一个进程。
理解 Linux 的进程模型很重要,因为它提供的抽象——Linux 进程——是您将用来管理进程的所有命令和工具所依赖的。您习惯从开发者的角度看到的详细信息已经消失了:变量、函数和线程都被封装为“一个进程”。您剩下的是一组不同的、外部的旋钮来操作和检查:进程 ID、状态、资源使用情况,以及我们将在本章中介绍的所有其他进程属性。
首先,我们将仔细看看进程抽象本身,然后深入到您可以用 Linux 进程做的有用、实际的事情。在介绍实用方面的同时,我们将暂停添加一些常见的问题源的细节,比如权限,并为您提供一些解决进程问题的启发式方法。
在本章中,您将学习以下主题:
Linux 进程是什么,以及如何在您的系统上查看当前正在运行的进程 进程的属性,以便您知道在故障排除时可以收集哪些信息 查看和查找进程的常见命令 对于实际编写作为 Linux 进程执行的程序的开发者来说可能有用的更高级的主题:信号和进程间通信,/proc 虚拟文件系统,使用 lsof 命令查看打开的文件句柄,以及 Linux 中进程是如何创建的 您还将通过一个实际的故障排除会话,回顾您在本章中涵盖的理论和命令中学到的所有内容。现在,让我们深入探讨 Linux 进程到底是什么。
进程基础
当我们在 Linux 中提到“进程”时,我们指的是操作系统对正在运行的程序的内部模型。Linux 需要一个适用于所有程序的通用抽象,它可以封装操作系统关心的事情。进程就是这种抽象,它使操作系统能够跟踪一些重要的上下文,围绕正在执行的程序;即:
内存使用情况 使用的处理器时间 其他系统资源使用情况(磁盘访问、网络使用) 进程之间的通信 程序启动的相关进程,例如,启动一个 shell 命令 您可以通过运行带有 aux 标志的 ps 程序,获取所有系统进程的列表(至少是您的用户被允许看到的进程):
图 2.1:系统进程列表
我们将在本章中介绍对您作为开发者最相关的属性。
Linux 进程由什么构成? 从操作系统的角度来看,“进程”仅仅是一个数据结构,它便于访问如下信息:
进程 ID(在上述 ps 输出中的 PID)。PID 1 是 init 系统——所有其他进程的原始父进程,它引导系统启动。内核在开始执行后做的第一件事之一就是启动它。当创建一个进程时,它会获得下一个可用的进程 ID,按顺序排列。由于它对操作系统的正常运行至关重要,init 无法被杀死,即使是 root 用户也不行。不同的 Unix 操作系统使用不同的 init 系统——例如,大多数 Linux 发行版使用 systemd,而 macOS 使用 launchd,许多其他 Unix 使用 SysV。不管具体实现如何,我们将通过它所扮演的角色的名称来引用此进程:“init”。
注意 在容器中,进程是被命名空间隔离的——在“真实”环境中,所有容器进程可能是 PID 3210,而那个单一的 PID 映射到许多进程(1..n,其中 n 是容器中运行的进程数量)。您可以从容器外部看到这个,但无法从容器内部看到。
父进程 PID(PPID)。每个进程都是由父进程生成的。如果父进程在子进程存活时死亡,子进程就会变成“孤儿”。孤儿进程会被重新分配给 init(PID 1)。
状态(在上述 ps 输出中的 STAT)。man ps 将向您展示一个概览:
- D – 不可中断的睡眠(通常是 IO)
- I – 空闲的内核线程
- R – 运行或可运行(在运行队列上)
- S – 可中断的睡眠(等待事件完成)
- T – 被作业控制信号停止
- t – 在跟踪期间被调试器停止
- X – 死亡(永远不应该被看到)
- Z – 终止的(“僵尸”)进程,已终止但未被其父进程回收
优先级状态(“niceness”——此进程是否允许其他进程优先于它?)。
进程所有者(在上述 ps 输出中的 USER);有效的用户 ID。
有效组 ID(EGID),这是使用的。
进程的内存空间地址映射。
资源使用情况——进程正在使用的打开文件、网络端口和其他资源(在上述 ps 输出中的 VSZ 和 RSS 用于内存使用情况)。
(引用:来自《Unix 和 Linux 系统管理手册》,第 5 版,第 91 页。)
让我们更仔细地看看一些对开发者和偶尔的故障排除者来说最重要的进程属性。
进程 ID (PID) 每个进程都可以通过其进程 ID 唯一标识,这只是一个在进程启动时分配给进程的唯一整数。就像关系数据库中用 ID 唯一标识每行数据一样,Linux 操作系统通过其 PID 来跟踪每个进程。
PID 到目前为止是您与进程交互时使用的最有用的标签。
有效用户 ID (EUID) 和有效组 ID (EGID) 这些决定了您的进程是以哪个系统用户和组运行的。用户和组权限共同决定了进程在系统上被允许执行的操作。
正如您将在第 5 章“介绍文件”中看到的,文件上设置了用户和组所有者,这决定了它们的权限适用于谁。如果文件的所有者和权限本质上是一个锁,那么具有正确的用户/组权限的进程就像一把打开锁并允许访问文件的钥匙。我们将在稍后更深入地讨论权限时再深入探讨这一点。
环境变量 您可能已经在应用程序中使用过环境变量——它们是操作系统环境启动您的进程时传递给进程所需的数据的一种方式。这通常包括像配置指令(LOG_DEBUG=1)和密钥(AWS_SECRET_KEY)等,每种编程语言都有一种方法可以从程序的上下文中读取它们。
例如,这个 Python 脚本从 HOME 环境变量中获取用户的主目录,然后打印出来:
import os
home_dir = os.environ['HOME']
print("The home directory for this user is", home_dir)
以我为例,在 Linux 机器上的 python3 REPL 中运行这个程序会产生以下输出:
The home directory for this user is /home/dcohen
工作目录 一个进程有一个“当前工作目录”,就像您的 shell(它本身不过是一个进程)。在您的 shell 中输入 pwd 会打印其当前工作目录,每个进程都有一个工作目录。进程的工作目录可以更改,所以不要过于依赖它。
这结束了我们对您应该了解的进程属性的概述。在下一节中,我们将从理论中走出来,看看您可以立即开始使用的一些命令来处理进程。
用于处理 Linux 进程的实用命令 以下是您最常使用的命令:
- ps – 显示系统上的进程;您在本章早些时候看到了这个命令的一个例子。标志修改显示为列的进程属性。这个命令通常与过滤器一起使用,以控制您获得的输出量,例如,(ps aux | head –n 10) 将输出减少到只有前 10 行。一些更有用的技巧:
- ps –eLf 显示进程的线程信息
- ps -ejH 用于以可视化的方式查看父子进程之间的关系(子进程缩进在它们的父进程下)
- pgrep – 按名称查找进程 ID。可以使用正则表达式。
- top – 一个交互式程序,轮询所有进程(默认每秒一次),并输出资源使用的排序列表(您可以配置它的排序方式)。还显示了总的系统资源使用情况。按 Q 或使用 Ctrl + C 退出。您将在本章稍后看到这个命令输出的一个例子。
- iotop – 类似于 top,但是针对磁盘 IO。对于查找 IO 密集型进程非常有用。不是所有系统默认都安装了它,但大多数包管理器都有提供。
- nethogs – 类似于 top,但是针对网络 IO。按进程分组网络使用情况,这非常便捷。大多数包管理器都提供。
kill – 允许用户向进程发送信号,通常用于停止它们或使它们重新读取配置文件。我们将在本章后面解释信号和 kill 命令的使用。
高级进程概念和工具 这标志着本章“高级”部分的开始。虽然您不需要掌握本节中的所有概念就能有效地使用 Linux 进程,但它们可能非常有用。如果您有几分钟额外时间,我们建议至少熟悉每一个概念。
信号 systemctl 如何告诉您的 Web 服务器重新读取其配置文件?您如何礼貌地要求进程干净地关闭?您如何立即杀死一个出现故障的进程,因为它使您的生产应用程序陷入困境?
在 Unix 和 Linux 中,所有这些都是通过信号完成的。信号是可以在程序之间发送的数字消息。它们是进程与彼此以及操作系统通信的一种方式,允许进程发送和接收特定消息。
这些消息可以用来向进程传达各种事情,例如,表明某个特定事件已经发生,或者需要采取特定的行动或响应。
信号的实际用途 让我们看一些信号机制实现的实际价值的例子。信号可以用来实现进程间通信;例如,一个进程可以向另一个进程发送信号,表示它已经完成了特定任务,现在另一个进程可以开始工作。这允许进程协调它们的行动并以一种平稳有效的方式一起工作,就像编程语言中的执行线程一样(但不共享相关联的内存)。
进程信号的另一个常见应用是处理程序错误。例如,可以设计一个进程来捕获 SIGSEGV 信号,该信号表示段错误。当进程收到此信号时,它可以捕获该信号,然后采取行动记录错误、转储核心以供调试目的,或在优雅关闭之前清理使用的任何资源。
进程信号也可以用来实现优雅关闭。例如,当系统关闭时,可以向所有进程发送信号,给它们一个机会保存它们的状态并清理它们使用的任何资源,通过“捕获”信号。
捕获 许多信号可以被接收它们的进程“捕获”:这基本上与在编程语言中捕获和处理错误相同。
如果接收进程有正在发送的信号的处理程序函数,那么将运行该处理程序函数。这就是程序在接收到关闭信号后重新读取它们的配置而无需重启,以及在接收到关闭信号后完成它们的数据库写入并关闭它们的文件句柄的方式。
kill 命令 然而,不仅仅是进程通过信号进行通信:名称令人生畏的(以及技术上错误命名的)kill 是一个程序,允许用户也向进程发送信号。
通过 kill 命令发送用户进程最常见的用途之一是中断不再响应的进程。例如,如果一个进程卡在无限循环中,可以发送“kill”信号以强制它停止。
kill 命令允许您通过指定其 PID 向进程发送信号。如果您想要终止的进程的 PID 是 2600,您将运行:
kill 2600
这个命令将向进程发送信号 15(SIGTERM,或“终止”),然后进程将有机会捕获该信号并干净地关闭。
注意 正如您从包含的标准信号数字表中看到的,kill 默认发送的信号是“终止”(信号 15),而不是“kill”(SIGKILL 是 9)。kill 程序不仅仅是为了杀死进程,也是为了发送任何类型的信号。它的命名确实令人困惑,对此我感到抱歉——这只是一种 Unix 和 Linux 的特殊习惯,您会习惯的。
如果您不想发送默认的信号 15,您可以用短划线指定您想发送的信号;要发送 SIGHUP 给同一个进程,您将运行:
kill -1 2600
运行 man signal 将给出您可以发送的信号列表:
图 2.6:man signal 命令的输出示例
对一些信号熟悉可以带来好处——有时在工程面试中甚至是字面上的回报——熟悉以下几个信号:
- SIGHUP (1) – “挂断”: 许多应用程序(例如 nginx)将其解释为“因为我已经更改了它,所以请重新读取您的配置。”
- SIGINT (2) – “中断”: 通常与 SIGTERM 相同,意味着“请干净利落地关闭。”
- SIGTERM (15) – “终止”: 礼貌地请求进程关闭。
- SIGUSR1 (30) 和 SIGUSR2 (31) 有时用于应用程序定义的消息。例如,SIGUSR1 请求 nginx 重新打开它正在写入的日志文件,这在您刚刚旋转日志文件时非常有用。
- SIGKILL (9) – SIGKILL 不能被进程捕获和处理。如果这个信号被发送到一个程序,操作系统将立即杀死该程序。任何清理代码,如刷新写入或安全关闭,都不会执行,所以这通常是最后的手段,因为它可能导致数据损坏。
如果您想更深入地探索 Linux,欢迎探索 /proc 目录。这绝对超出了基础,但它是一个目录,包含每个进程的文件系统子树,您可以在读取这些文件时查找有关进程的实时信息。
/proc 在实践中,当您在故障排除时识别出一个表现异常(或神秘的)进程并想知道它实时在做什么时,这些知识可能会派上用场。
您可以通过在其 /proc 子目录中摸索并随意搜索来了解有关进程的许多信息。
本章中我们向您展示的许多工具实际上使用 /proc 来收集进程信息,并且只向您显示其中的一部分。如果您想看到一切并自己进行过滤,/proc 是您要查找的地方。
- lsof – 显示进程打开的文件句柄 lsof 命令显示进程打开用于读写的所有文件。这很有用,因为一个小程序出现一个小错误就可能导致文件句柄泄漏(它请求访问的文件的内部引用)。这可能导致资源使用问题、文件损坏和一长串奇怪的行为。
- 幸运的是,获取进程打开的文件列表很容易。只需运行 lsof 并传递 –p 标志和 PID(您通常需要以 root 身份运行此命令)。这将返回进程(在本例中,PID 为 1589)打开的文件列表:
- ~ lsof -p 1589
- 上述输出是 nginx Web 服务器进程的输出。第一行向您显示了进程的当前工作目录:在这种情况下,是根目录(/)。您还可以看到它打开了它自己的二进制文件(/usr/sbin/nginx)和 /usr/lib/ 中的各种库的文件句柄。
- 再往下看,您可能会注意到一些更有趣的文件路径:
这个列表包括了 nginx 正在写入的日志文件,以及它正在读写的套接字文件(Unix、IPv4 和 IPv6)。在 Unix 和 Linux 中,网络套接字只是一种特殊类型的文件,这使得相同的核心工具集可以跨多种用例使用——在几乎所有事物都表示为文件的环境中,与文件一起工作的工具非常强大。
继承 除了最初的进程 init(PID 1),所有进程都是由父进程创建的,父进程基本上会复制自身,然后“派生”(分裂)那个副本。当一个进程被派生时,它通常会继承其父进程的权限、环境变量和其他属性。
虽然这种默认行为可以被阻止和更改,但它有点安全风险:您手动运行的软件接收到您当前用户的权限(或者甚至是 root 权限,如果您使用了 sudo)。那个过程中可能创建的所有子进程——例如,在安装、编译等过程中——继承了这些权限。
想象一个以 root 权限启动的 Web 服务器进程(这样它就可以绑定到网络端口),并且环境变量包含云认证密钥(这样它就可以从云端获取数据)。当这个主进程派生出一个既不需要 root 权限也不需要敏感环境变量的子进程时,将这些传递给子进程是不必要的安全风险。因此,在生成子进程的服务中,降低权限和清除环境变量是一个常见模式。
从安全的角度来看,重要的是要记住这一点,以防止像密码或访问敏感文件这样的信息被泄露的情况。虽然本书的范围不包括如何避免这些的详细说明,但如果您正在编写将要在 Linux 系统上运行的软件,意识到这一点非常重要。
回顾 - 示例故障排除会话 让我们来看一个示例故障排除会话。我们所知道的是,一个特定的 Linux 服务器运行得非常缓慢。
首先,我们想要看看系统上发生了什么。您刚刚了解到,通过运行交互式 top 命令,您可以看到系统上正在运行的进程的实时视图。现在让我们尝试这样做。
图 2.9: top 命令的输出示例
默认情况下,top 命令按 CPU 使用率对进程进行排序,所以我们只需查看第一个列出的进程就能找到罪魁祸首。实际上,排在最上面的进程正在使用一个 CPU 的 94% 的可用处理时间。
运行 top 后,我们得到了一些有用的信息:
问题在于 CPU 使用率,而不是其他类型的资源竞争。 罪魁祸首的进程是 PID 1763,正在运行的命令(列在 COMMAND 栏中)是 bzip2,这是一个压缩程序。 我们确定这里不需要运行这个 bzip2 进程,我们决定停止它。使用 kill 命令,我们请求进程终止:
kill 1763
等待几秒钟后,我们检查是否还有这个(或任何其他)bzip2 进程在运行:
pgrep bzip2
不幸的是,我们看到相同的 PID 仍然在运行。是时候认真对待了:
kill -9 1763
这命令操作系统立即杀死进程,不允许进程捕获(并可能忽略)信号。SIGKILL(信号 #9)直接杀死进程。
现在您已经杀死了罪魁祸首的进程,服务器再次运行顺畅,您可以开始追踪那个认为在这台机器上压缩大源代码目录是个好主意的开发者。
在这个例子中,我们遵循了存在的最常见的系统故障排除模式:
我们查看了资源使用情况(在这个例子中是通过 top)。这可以是我们讨论的任何其他工具,取决于正在耗尽的资源是哪一个。 我们找到一个 PID 进行调查。 我们对那个进程采取了行动。在这个例子中,没有必要进一步调查,我们发送了一个信号,请求它关闭(15, SIGTERM)。
结论 在本章中,我们仔细研究了 Linux 对正在执行的程序所包装的进程抽象。您已经看到了所有进程共有的常见组件,并学习了查找和检查运行中进程所需的基本命令。有了这些工具,您将能够确定何时一个进程表现异常,更重要的是,哪个进程表现异常。
相关推荐
- CTO偷偷传我的系统性能优化十大绝招(万字干货)
-
上篇引言:取与舍软件设计开发某种意义上是“取”与“舍”的艺术。关于性能方面,就像建筑设计成抗震9度需要额外的成本一样,高性能软件系统也意味着更高的实现成本,有时候与其他质量属性甚至会冲突,比如安全性、...
- 提升效率!VMware虚拟机性能优化十大实用技巧
-
我40岁,干跨境婚恋中介的。为服务各国用户,常得弄英语、日语、俄语系统环境,VMware虚拟机帮了不少忙。用久了发现优化下性能,效率能更高。今儿就来聊聊优化技巧和同类软件。一、VMware虚拟...
- 低延迟场景下的性能优化实践
-
本文摘录自「全球C++及系统软件技术大会」ScottMeyers曾说到过,如果你不在乎性能,为什么要在C++这里,而不去隔壁的Pythonroom呢?今天我们就从“低延迟的概述”、“低延迟系...
- Linux性能调优之内存负载调优的一些笔记
-
写在前面整理一些Linux内存调优的笔记,分享给小伙伴博文没有涉及的Demo,理论方法偏多,可以用作内存调优入门博文内容涉及:Linux内存管理的基本理论寻找内存泄露的进程内存交换空间调优不同方式的...
- 优化性能套路:带你战胜这只后段程序员的拦路虎
-
来源|极客时间《卖桃者说》作者|池建强编辑|成敏你好,这里是卖桃者说。今天给大家推荐一篇文章,来自倪朋飞老师的专栏《Linux性能优化实战》,文章主要讲的是优化性能的套路,这几乎是每个后端程序员...
- SK海力士CXL优化解决方案已成功搭载于Linux:带宽提升30%,性能提升12%以上
-
SK海力士宣布,已将用于优化CXL(ComputeExpressLink)存储器运行的自研软件异构存储器软件开发套件(HMSDK)中主要功能成功搭载于全球最大的开源操作系统Linux上,不但提升了...
- Linux内核优化:提升系统性能的秘诀
-
Linux内核优化:提升系统性能的艺术在深入Linux内核优化的世界之前,让我们先来理解一下内核优化的重要性。Linux内核是操作系统的核心,负责管理系统资源和控制硬件。一个经过精心优化的内核可以显著...
- Linux系统性能优化:七个实战经验
-
Linux系统的性能是指操作系统完成任务的有效性、稳定性和响应速度。Linux系统管理员可能经常会遇到系统不稳定、响应速度慢等问题,例如在Linux上搭建了一个web服务,经常出现网页无法打开、打开速...
- 腾讯面试:linux内存性能优化总结
-
【1】内存映射Linux内核给每个进程都提供了一个独立且连续的虚拟地址空间,以便进程可以方便地访问虚拟内存;虚拟地址空间的内部又被分为内核空间和用户空间两部分,不同字长的处理器,地址空间的范围也不同...
- Linux文件系统性能调优《参数优化详解》
-
由于各种的I/O负载情形各异,Linux系统中文件系统的缺省配置一般来说都比较中庸,强调普遍适用性。然而在特定应用下,这种配置往往在I/O性能方面不能达到最优。因此,如果应用对I/O性能要求较高,除...
- Nginx 性能优化(吐血总结)
-
一、性能优化考虑点当我需要进行性能优化时,说明我们服务器无法满足日益增长的业务。性能优化是一个比较大的课题,需要从以下几个方面进行探讨当前系统结构瓶颈了解业务模式性能与安全1、当前系统结构瓶颈首先需要...
- Linux问题分析与性能优化
-
排查顺序整体情况:top/htop/atop命令查看进程/线程、CPU、内存使用情况,CPU使用情况;dstat2查看CPU、磁盘IO、网络IO、换页、中断、切换,系统I/O状态;vmstat2查...
- 大神级产品:手机装 Linux 运行 Docker 如此简单
-
本内容来源于@什么值得买APP,观点仅代表作者本人|作者:灵昱Termux作为一个强大的Android终端模拟器,能够运行多种Linux环境。然而,直接在Termux上运行Docker并不可行,需要...
- 新手必须掌握的Linux命令
-
Shell就是终端程序的统称,它充当了人与内核(硬件)之间的翻译官,用户把一些命令“告诉”终端程序,它就会调用相应的程序服务去完成某些工作。现在包括红帽系统在内的许多主流Linux系统默认使用的终端是...
- Linux 系统常用的 30 个系统环境变量全解析
-
在Linux系统中,环境变量起着至关重要的作用,它们犹如隐藏在系统背后的“魔法指令”,掌控着诸多程序的运行路径、配置信息等关键要素。尤其在shell脚本编写时,巧妙运用环境变量,能让脚本如虎...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle忘记用户名密码 (59)
- oracle11gr2安装教程 (55)
- mybatis调用oracle存储过程 (67)
- oracle spool的用法 (57)
- oracle asm 磁盘管理 (67)
- 前端 设计模式 (64)
- 前端面试vue (56)
- linux格式化 (55)
- linux图形界面 (62)
- linux文件压缩 (75)
- Linux设置权限 (53)
- linux服务器配置 (62)
- mysql安装linux (71)
- linux启动命令 (59)
- 查看linux磁盘 (72)
- linux用户组 (74)
- linux多线程 (70)
- linux设备驱动 (53)
- linux自启动 (59)
- linux网络命令 (55)
- linux传文件 (60)
- linux打包文件 (58)
- linux查看数据库 (61)
- linux获取ip (64)
- linux进程通信 (63)