程序和进程的区别和联系
程序(Program):
- 程序是一组指令的集合,通常存储在磁盘或其他存储设备上,是一种静态的概念。
- 程序本身并没有运行,它只是一个可执行的文件或脚本,包含了一系列的指令和数据。
- 程序可以是编译后的可执行文件(如
.exe
文件)、脚本文件(如.sh
、.py
等)、动态链接库等形式。
进程(Process):
- 进程是程序在运行过程中的一个实例,是操作系统分配资源的基本单位。
- 每个进程都有独立的内存空间,包括代码段、数据段、堆栈等。
- 一个进程对应着一个执行中的程序
总而言之:
程序是静态的,存储在磁盘或其他存储设备中;而进程是动态的,是程序在运行时的实例。程序是概念上的代码和数据集合;进程是程序实际运行时的实体,具有独立的状态和资源。
那线程又是什么呢?
线程(Thread)是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程的一个实体。与进程不同的是,同属于一个进程内的多个线程共享相同的内存空间和系统资源,包括地址空间、文件描述符和其他进程的资源。一个进程中的线程可以被理解为程序的多线程,每个线程可以执行程序中的不同部分或者并行执行相同的代码段。所以,进程中的线程可以被称为程序的多线程,它们共同完成程序在计算机上的并发执行。多线程程序的主要优势在于可以提高程序的并发性和响应能力。
进程相关命令总览
ps 命令(列出来的信息是静态的)
ps -ef(列出当前系统中所有进程的详细信息)
在输出中,每一列的含义通常如下:
- UID:进程的用户ID,表示该进程是由哪个用户启动的。
- PID:进程ID,是唯一标识系统中每个进程的数字。
- PPID:父进程ID,指示启动(创建)该进程的父进程的ID。
- C:CPU使用的累计时间(单位为分钟)。如果进程使用的CPU时间超过999分钟,它会显示为
999:00
。 - STIME:进程启动的时间或日期。
- TTY:终端设备,如果没有分配终端,显示
?
。 - TIME:进程消耗的CPU时间。
- CMD:启动进程的命令行命令。
其中,1号进程通常指的是init进程或systemd
具体解释如下:
init进程:在传统的Unix系统中,init进程是所有进程的祖先,其进程ID通常为1。它是系统启动时由内核启动的第一个进程,并且负责启动和管理所有其他进程。init进程的职责包括系统的初始化、服务的启动和关闭等。
systemd:在许多现代Linux发行版中,init进程已经被systemd所取代。systemd是一个系统和服务管理器,它负责启动、管理和监控系统上运行的进程、服务和资源。类似于传统的init进程,systemd的进程ID也通常是1。
这些进程(init或systemd)在Linux系统中起着非常重要的作用,它们是系统的第一个用户空间进程,负责初始化系统环境并确保其他进程能够正常运行。
TTY的解释
TTY列显示了该进程的终端设备。如果一个进程没有被分配到任何终端,TTY列会显示为?。
通常来说守护进程,后台作业,系统服务有关的进程一般都不分配任何终端。一个进程没有分配到任何终端(TTY列显示为?
),意味着它不与当前用户的任何终端会话直接关联。这种情况下,它通常是一个独立运行的进程,可能是一个守护进程、后台作业或系统服务。这并不意味着它没有其他进程依附或依赖,只是它不接受来自用户终端的交互。
更通俗来讲意思是:当进程没有分配到任何终端(TTY列显示为?
),即使用户在任何终端上输入命令或者进行其他操作,也不会影响到这个进程的正常执行。这种进程通常是独立运行的后台进程或者服务,它们不依赖于用户的交互来运行或者完成任务。这种设计使得系统可以同时运行一些不需要用户干预的服务或者任务,例如网络服务、定时任务等。
这里看到的几个进程被分配了终端pts/0,那么在pts/0这个终端中可以对这些进程进行操作和管理,影响这些进程的执行。
我们可以在
pts/0
这个终端中对这些进程进行操作和管理,例如:
查看进程状态:您可以使用
ps
、top
或其他系统监控工具来查看进程的状态。发送信号:您可以使用
kill
命令向进程发送信号,如SIGINT
(中断信号,通常用于终止进程)或SIGTERM
(终止信号)。终止进程:如果进程没有响应其他信号,您可以使用
kill -9
发送SIGKILL
信号强制终止进程。重定向输入输出:您可以将进程的输入输出重定向到文件或其他设备,以便于记录或进一步处理。
交互式操作:对于交互式进程,您可以在终端中与它们进行交互,例如输入命令或数据。
脚本和自动化:您可以在终端中运行脚本或自动化任务,这些任务可能会启动、监控或管理其他进程。
下面我们会提到这里说的对于进程管理的一些操作
ps aux(列出系统中进程的详细资源使用情况)
在 ps aux
输出中,每一列的含义如下:
- USER:启动该进程的用户名。
- PID:进程的唯一标识符,即进程ID。
- %CPU:该进程占用的CPU时间百分比。
- %MEM:该进程占用的内存百分比。
- VSZ:进程使用的虚拟内存大小(单位:KB)。
- RSS:进程占用的实际物理内存大小(单位:KB)。
- TTY:该进程关联的终端设备(tty)。
- STAT:进程状态代码:
- S:睡眠状态(sleep)
- R:运行状态(running)
- Z:僵尸状态(zombie)
- D:不可中断的睡眠状态(uninterruptible sleep)
- T:停止状态(stopped)
- W:无法使用交换机(paging)
- <:高优先级进程
- N:低优先级进程
- L:有些页被锁在内存中
- +:前台进程组的成员
- START:进程启动时间或者启动日期。
- TIME:该进程累计占用CPU的时间。
- COMMAND:启动进程时使用的命令行。
ps ajx(也是列出进程相关信息,这里不细讲)
可以用man ps 查看更多内容
pstree命令
pstree
命令用于以树形结构显示进程的层次关系。它将当前系统中所有进程按照它们的父子关系组织起来,从而形成一棵树状结构,以清晰和直观的方式展示进程之间的层次关系。
-
显示进程层次结构:以树形图的形式展示当前系统中所有进程的层次关系。这样可以清晰地看出每个进程的父进程和子进程。
-
显示进程的终端关系:在树状结构中,
pstree
还会显示每个进程的关联终端(TTY),有助于了解哪些进程是由特定的终端启动的。
pstree命令的常见选项
-
-p
: 显示进程的PID(进程ID)。默认情况下,pstree
只显示进程的名字,使用-p
选项可以显示进程名字和PID。示例:pstree -p
-
-u
: 显示进程的所有者。显示每个进程的所有者用户名。示例:pstree -u
-
-l
: 使用长格式显示。显示更详细的信息,包括命令行参数。示例:pstree -l
-
-h
: 在输出中高亮当前用户启动的进程。有助于区分不同用户启动的进程。示例:pstree -h
-
-A
: 使用ASCII字符绘制树状结构。在文本终端中显示时更加清晰。示例:pstree -A
-
-T
: 显示每个进程的终端(TTY)。示例:pstree -T
-
-c
: 不压缩相同的子进程。在显示子进程时,不合并相同的进程。示例:pstree -c
top 命令(动态监视系统进程)
top
命令是一个用于动态监视系统进程活动的命令行工具,它能够实时显示系统中各个进程的资源占用情况。并且支持用户动态切换显示方式,例如按 CPU 使用率排序、按内存使用率排序等各种功能。
top命令可以类比于Windows下的任务管理器,它会以指定的时间间隔刷新并显示当前系统中运行的进程信息。
退出监测方法
想要退出去,按下按键q就可以。
测试top命令查看进程资源的一个示例
创建while死循环
为了测试查看top和后面对进程控制的一些操作,这里我们创建一个while死循环让它在电脑上面运行。
这条命令 gcc while.c -o while
是在编译一个名为 while.c
的 C 语言源代码文件,并将编译输出的可执行文件命名为 while
。
然后通过./while来执行这个可执行文件。或者通过/home/linux/while完整路径来执行,这里执行可执行文件的时候并不像cd/cat等命令一样,cd/cat当前路径下的文件夹可以不加./,这里执行当前路径下的可执行文件不加./可能会执行不了。
执行了文件之后由于这里是一个死循环,可以看到回车键不会出现新的让我们输入命令的地方,除非我们中断这个程序进程(可以使用ctrl+c,或者后面即将介绍的kill命令)
监测while进程资源占用
显然我们这里就是想要看一下在while程序进程执行中的进程资源信息,但是由于程序死循环执行,可以看到回车键不会出现新的让我们输入命令的地方,我们应该怎么做?
其实很简单,我们可以打开一个新的终端窗口,在这里我们可以输入其他命令。
新建新的终端窗口可以有如下方法:
1.
点击图片左上角的+号
2. Ctrl + Shift + T:这个组合键会在当前终端窗口中打开一个新的标签页(tab)。如果你需要在同一个终端窗口中打开多个终端实例,可以重复按下这个组合键来创建多个标签页。
这个达到的效果是和第一种方法效果一样。
3.Ctrl + Alt + T:这个组合键通常会在当前的桌面环境中打开一个新的终端窗口。每次按下这个组合键,都会打开一个新的终端窗口实例。相当于新开了一个小窗口,效果如下:
现在我们便可以在另一个终端窗口中输入top命令,来实时监测while进程占用资源情况
top命令的选项
-
-p pid[,pid...]: 仅显示指定进程 ID(PID)的进程信息,多个 PID 之间用逗号分隔。
-
-u username: 显示指定用户名(username)的进程信息,仅显示该用户的进程。
如果不知道某个进程的pid号呢?
那么我们可以通过ps -ef来查看
top显示翻页
由于屏幕限制,肯定是显示不完全所有进程的,可以通过>和<来进行翻页,还有通过上下箭头可以逐条增加向下或者向上翻
kill 命令(给指定的进程发送信号)
常见linux信号介绍
这些是常见的信号名称和对应的编号。每个信号都有特定的含义和用途,比如 SIGTERM
用于正常终止进程,SIGKILL
用于强制终止进程等。ctrl+c对应的就是2号信号SIGINT。当程序不采取自定义的信号处理器情况下,这里信号中除了个别的信号(如17-20等),其他绝大多数信号都能杀死对方进程。
自定义信号处理器
在 Linux 中,可以通过信号处理器(Signal Handler)来自定义程序对接收到信号的响应方式。信号处理器允许程序在接收到信号时执行特定的处理函数或逻辑,而不是简单地终止或忽略信号。它可以捕获并处理,从而改变默认行为。
信号处理器是嵌入在程序内部的。在 Unix/Linux 系统中,每个进程都有自己的信号处理机制
特别介绍一下9号信号
信号处理器无法捕捉或者阻塞它,所以也不能改变这个信号的默认行为
在 Unix/Linux 系统中,信号 9 是
SIGKILL
,而其他的 kill 信号如SIGTERM
(15号信号) 和SIGINT
(2号信号) 在作用和影响上有显著的不同:
SIGKILL (
kill -9
):
- 强制终止:SIGKILL 是一个强制终止信号,它会立即终止目标进程,无论进程的当前状态如何。即使进程正在执行某些关键操作或者阻塞,SIGKILL 也会将其强制终止。
- 不能被捕获或忽略:进程无法捕获或者忽略 SIGKILL 信号。这是因为 SIGKILL 不像其他信号一样可以被处理或者阻塞,它对目标进程来说是不可忽略的终止请求。
其他 kill 信号 (
SIGTERM
,SIGINT
等):
- 软件终止信号:这些信号是软件终止信号,通常用于请求目标进程优雅地终止。比如,
kill
命令默认发送的是SIGTERM
(15号信号),它请求进程进行清理操作并安全地退出。- 可以被捕获或忽略:进程可以选择捕获或者忽略这些信号。通过信号处理器,进程可以在收到
SIGTERM
或SIGINT
时执行特定的清理操作,然后退出。总结起来,主要区别在于
SIGKILL
是一个无法被阻止或者处理的强制终止信号,而其他 kill 信号则是可处理的软件终止请求信号,可以让进程在退出前进行必要的清理工作。
让程序在后台运行
.while/ &这个命令的效果是启动名为 while
的可执行程序,并使其在后台运行,让你可以继续在同一个终端或命令行窗口中执行其他命令,而不必等待 while
程序执行完毕。
程序后台执行的特点:
不阻塞终端或命令行:后台执行的程序不会占用当前终端或命令行窗口,这意味着你可以继续在同一终端执行其他命令,而不必等待该程序完成。
信号处理:后台程序仍然可以接收和处理信号。比如,它可以接收到
Ctrl+C
信号(SIGINT),并根据程序中的信号处理程序进行处理。这种灵活性允许程序在后台运行时,仍然可以对外部输入做出响应。输出处理:后台程序的标准输出(stdout)、标准错误输出(stderr)通常被重定向到特定的位置,如
/dev/null
或者其他文件。这样可以避免在终端上看到大量输出,尤其是对于长时间运行的程序或者守护进程来说特别有用。
这样看来,对于上面 监测while进程资源占用的示例我们可以不用打开新的终端窗口,只需要来让这个程序后台执行,也能top看到while进程信息。
后台执行的程序对于ctrl+c发送的2号信号和使用kill发送的二号信号接收不一样。
对于后台运行的程序,它的信号处理能力取决于多个因素:
Ctrl+C(SIGINT)信号:
- 当程序在后台运行时,默认情况下,终端是不会将
Ctrl+C
发送给后台进程的,而是发送给前台进程。- 如果你希望在后台运行的程序能够接收
Ctrl+C
信号,可以使用一些工具或技术,例如nohup
或者将程序以某种方式放置在可以接收信号的环境中。kill 命令发送的信号:
- 使用
kill
命令可以向后台运行的程序发送不同的信号,例如kill -TERM <pid>
发送 SIGTERM 信号(默认的终止信号)。- 无论程序是在前台还是后台运行,
kill
命令都可以向其发送信号,除非该信号被特定条件阻止。总结来说,后台运行的程序通常可以接收信号,但是
Ctrl+C
是一个特例,需要特殊处理才能使后台程序接收到该信号。默认情况下ctrl+c发送的信号后台运行程序进程接收不到,而kill
命令发送2号信号的功能并不受前后台状态的限制。下面我们将来学习如何使用kill给进程发送信号。
kill命令的使用
kill -l 查看所有信号名称
kill 1234 终止进程(会向进程号为 1234
的进程发送默认的终止信号(SIGTERM))
kill -9 5678 会向进程发送信号编号9对应的信号
kill -s SIGTERM 5678 (如果是指定信号名称而不是信号编号,需要加上-s,指定信号编号的话加不加-s都可以)
需要注意的是:填写的必须是要kill发送信号的进程标识号,不能是进程名字,进程号才是一个进程的唯一标识,运行相同程序对应的进程可能有好几个。不知道进程号,就用ps -ef查询
killall 进程名称(可以杀死这个一个名称对应的多个进程)了解一下,平时用的不多
nice命令(设置新进程优先级)
nice
命令用于置新的进程的优先级。在Linux系统中,每个进程都有一个优先级,这决定了它在系统资源分配中的优先程度。较高的优先级使得进程更可能更早地获得CPU时间片,从而更快地执行任务。
nice
命令是系统管理员和高级用户优化进程调度的一个有用工具,但对于普通用户来说,使用频率可能不高。
想要了解nice命令的作用效果,首先来看一下top命令显示的结果中的项
我这里仅仅截取了详细进程信息。
- 每一行都代表一个进程,包括以下列:
- PID:进程的唯一标识符。
- USER:启动该进程的用户名。
- PR:进程的优先级。
- NI:进程的nice值,表示其调度优先级的偏移量。
- VIRT:进程使用的虚拟内存量。
- RES:进程当前使用的物理内存量(Resident Set Size)。
- SHR:进程使用的共享内存量。
- S:进程的状态(例如,R表示运行,S表示睡眠,Z表示僵尸)。
- %CPU:进程使用的CPU时间占总CPU时间的百分比。
- %MEM:进程使用的物理内存占总内存的百分比。
- TIME+:自进程启动以来已使用的累计CPU时间。
我们在这里主要看 PR和NI,他们两个的值范围都是-20-+20,PR越大该进程优先级越高,反之越低。NI则是值越大该进程优先级越低,反之越高。我们使用nice命令调整的是NI的值,但是PR也会随之变化,比如调大了NI,PR就变小,调小了NI,PR就变大。
我们可以将NI看为这个进程的好人值,好人值越大,说明该进程是个好人,将资源都让给了其他进程,让其他进程优先执行。反之,说明该进程是个坏人,将资源都抢了过来,让自己优先执行。
nice -n 10 long_running_task & //
这会将执行long_running_task的进程的NI设置为 5
。
sudo nice -n -5 top_priority_task & //
这会将执行long_running_task的进程的NI设置为-5,优先执行。
这里优先级调低不需要sudo,优先级调高需要sudo,不然大家都抢资源不就乱套了吗?
renice命令(动态调整运行进程优先级)
renice 15 -p PID //
这会将进程号为 PID 的进程的NI设置为 15。
sudo renice 15 -p PID //
这会将进程号为 PID 的进程的NI设置为 -5。,优先执行。
nice和renice区别
nice
命令:
- 设置新进程的优先级:在启动新进程时,可以使用
nice
命令设置进程的优先级,即调整进程的 nice 值。- 避免竞争:可以通过
nice
命令降低新进程的优先级,以避免新进程占用过多的 CPU 资源,从而保持系统的响应性和稳定性。
renice
命令:
- 调整运行中进程的优先级:对于已经在运行的进程,使用
renice
命令可以调整其优先级。- 动态调整:通过
renice
可以在系统运行时根据需要调整进程的优先级,以响应实时的系统负载变化或者优化资源分配。