Linux进程与磁盘管理

Linux进程与磁盘管理

进程(process):进程是程序在一个数据集合上的一次执行过程,在早期的 UNIX、Linux 2.4 及更早的版本中,它是系统进行资源分配和调度的独立基本单位。

  • 动态性:进程的实质是一次程序执行的过程,有创建、撤销等状态的变化。而程序是一个静态的实体。
  • 并发性:进程可以做到在一个时间段内,有多个程序在运行中。程序只是静态的实体,所以不存在并发性。
  • 独立性:进程可以独立分配资源,独立接受调度,独立地运行。
  • 异步性:进程以不可预知的速度向前推进。
  • 结构性:进程拥有代码段、数据段、PCB(进程控制块,进程存在的唯一标志)。也正是因为有结构性,进程才可以做到独立地运行。

并发:在一个时间段内,宏观来看有多个程序都在活动,有条不紊的执行(每一瞬间只有一个在执行,只是在一段时间有多个程序都执行过)

并行:在每一个瞬间,都有多个程序都在同时执行,这个必须有多个 CPU 才行

线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。因为线程中几乎不包含系统资源,所以执行更快、更有效率。

进程分类

  • 用户进程:通过执行用户程序、应用程序或称之为内核之外的系统程序而产生的进程,此类进程可以在用户的控制下运行或关闭。
  • 系统进程:通过执行系统内核程序而产生的进程,比如可以执行内存资源分配和进程切换等相对底层的工作;而且该进程的运行不受用户的干预,即使是 root 用户也不能干预系统进程的运行
  • 交互进程:由一个 shell 终端启动的进程,在执行过程中,需要与用户进行交互操作,可以运行于前台,也可以运行在后台。
  • 批处理进程:该进程是一个进程集合,负责按顺序启动其他的进程。
  • 守护进程:守护进程是一直运行的一种进程,在 Linux 系统启动时启动,在系统关闭时终止。它们独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。例如 httpd 进程,一直处于运行状态,等待用户的访问。还有经常用的 cron(在 centOS 系列为 crond)进程,这个进程为 crontab 的守护进程,可以周期性的执行用户设定的某些任务。

我们启动了终端,就是启动了一个 bash 进程,我们可以在 bash 中再输入 bash 则会再启动一个 bash 的进程,此时第二个 bash 进程就是由第一个 bash 进程创建出来的,他们之间关系是父进程与子进程

image-20210915105254288

进程查看

我们可以通过 top 实时的查看进程的状态,以及系统的一些信息(如 CPU、内存信息等),我们还可以通过 ps 来静态查看当前的进程信息,同时我们还可以使用 pstree 来查看当前活跃进程的树形结构。

top

查看系统中使用CPU、使用内存最多的进程;

1
top

对于进程,平时我们最常想知道的就是哪些进程占用CPU最多,占用内存最多。以下两个命令就可以满足要求:

1
2
3
P:根据CPU使用百分比大小进行排序。
M:根据驻留内存大小进行排序。
i:使top不显示任何闲置或者僵死进程。

可以说 top就是linux下的任务管理器

image-20210915110542055

关于进程情况

列名解释
PID进程 id
USER该进程的所属用户
PR该进程执行的优先级 priority 值 越小越高
NI该进程的 nice 值
VIRT该进程任务所使用的虚拟内存的总数
RES该进程所使用的物理内存数,也称之为驻留内存数
SHR该进程共享内存的大小
S该进程进程的状态: S=sleep R=running Z=zombie
%CPU该进程 CPU 的利用率
%MEM该进程内存的利用率
TIME+该进程活跃的总时间
COMMAND该进程运行的名字

NICE 值叫做静态优先级,是用户空间的一个优先级值,其取值范围是 -20 至 19。这个值越小,表示进程”优先级”越高,而值越大“优先级”越低。nice 值中的 -20 到 19,中 -20 优先级最高, 0 是默认的值,而 19 优先级最低。

PR 值表示 Priority 值叫动态优先级,是进程在内核中实际的优先级值,进程优先级的取值范围是通过一个宏定义的,这个宏的名称是 MAX_PRIO,它的值为 140。Linux 实际上实现了 140 个优先级范围,取值范围是从 0-139,这个值越小,优先级越高。而这其中的 0-99 是实时进程的值,而 100-139 是给用户的。

其中 PR 中的 100 to 139 值部分有这么一个对应 PR = 20 + (-20 to +19),这里的 -20 to +19 便是 nice 值,所以说两个虽然都是优先级,而且有千丝万缕的关系,但是他们的值,他们的作用范围并不相同。

VIRT 任务所使用的虚拟内存的总数,其中包含所有的代码,数据,共享库和被换出 swap 空间的页面等所占据空间的总数。

下面来解析一下

image-20210915110618881

  • 第一行

11:05:55 : 系统当前时间

up 219min : 系统开机到现在经过了多少时间

1 user : 当前1用户在线

load average: 0.14, 0.09, 0.11: 系统1分钟、5分钟、15分钟的CPU负载信息

  • 第二行

Tasks:任务;296total:当前有87个任务,也就是87个进程。

running:2个进程正在运行

294sleeping:294个进程睡眠

0 stopped:停止的进程数

0 zombie:僵死的进程数

image-20210915111128522

  • 第三行

    Cpu(s):表示这一行显示CPU总体信息

    2.7%us:用户态进程占用CPU时间百分比,不包含renice值为负的任务占用的CPU的时间。

    0.7%sy:内核占用CPU时间百分比

    ni:改变过优先级的进程占用CPU的百分比

    96.6%id:空闲CPU时间百分比

    wa:等待I/O的CPU时间百分比

    hi:CPU硬中断时间百分比

    si:CPU软中断时间百分比

    注:这里显示数据是所有cpu的平均值,如果想看每一个cpu的处理情况,按1即可;折叠,再次按1;

image-20210915112109665

内容解释
1952.9 total物理内存总量
409.0 used使用的物理内存总量
409.0 free空闲内存总量
681.1 buffers用作内核缓存的内存量

注意:

系统中可用的物理内存最大值并不是 free 这个单一的值,而是 free + buffers + swap 中的 cached 的和。

image-20210915112325499

内容解释
total交换区总量
used使用的交换区总量
free空闲交换区总量
cached缓冲的交换区总量,内存中的内容被换出到交换区,而后又被换入到内存,但使用过的交换区尚未被覆盖

交互命令

常用交互命令解释
q退出程序
I切换显示平均负载和启动时间的信息
P根据 CPU 使用百分比大小进行排序
M根据驻留内存大小进行排序
i忽略闲置和僵死的进程,这是一个开关式命令
k终止一个进程,系统提示输入 PID 及发送的信号值。一般终止进程用 15 信号,不能正常结束则使用 9 信号。安全模式下该命令被屏蔽。

ps

1
ps aux

image-20210915112813151

  • l 长格式输出;

  • u 按用户名和启动时间的顺序来显示进程;

  • j 用任务格式来显示进程;

  • f 用树形格式来显示进程;

  • a 显示所有用户的所有进程(包括其它用户);

  • x 显示无控制终端的进程;

  • r 显示运行中的进程;

  • -e 显示所有进程,环境变量

image-20210915113103961

内容解释
F进程的标志(process flags),当 flags 值为 1 则表示此子程序只是 fork 但没有执行 exec,为 4 表示此程序使用超级管理员 root 权限
USER进程的拥有用户
PID进程的 ID
PPID其父进程的 PID
SIDsession 的 ID
TPGID前台进程组的 ID
%CPU进程占用的 CPU 百分比
%MEM占用内存的百分比
NI进程的 NICE 值
VSZ进程使用虚拟内存大小
RSS驻留内存中页的大小
TTY终端 ID
S or STAT进程状态
WCHAN正在等待的进程资源
START启动进程的时间
TIME进程消耗 CPU 的时间
COMMAND命令的名称和参数

查询正在运行的进程信息

1
$ps -rf

pstree

image-20210915113749274

可以很直接的看到相同的进程数量,最主要的还是我们可以看到所有进程之间的相关性

补充

内存管理

1
free

查看内存使用情况

image-20210915114130103

进程组与Sessions

每一个进程都会是一个进程组的成员,而且这个进程组是唯一存在的,他们是依靠 PGID(process group ID)来区别的,而每当一个进程被创建的时候,它便会成为其父进程所在组中的一员。

一般情况,进程组的 PGID 等同于进程组的第一个成员的 PID,并且这样的进程称为该进程组的领导者,也就是领导进程,进程一般通过使用 getpgrp() 系统调用来寻找其所在组的 PGID,领导进程可以先终结,此时进程组依然存在,并持有相同的 PGID,直到进程组中最后一个进程终结。

与进程组类似,每当一个进程被创建的时候,它便会成为其父进程所在 Session 中的一员,每一个进程组都会在一个 Session 中,并且这个 Session 是唯一存在的,

Session 主要是针对一个 tty 建立,Session 中的每个进程都称为一个工作(job)。每个会话可以连接一个终端(control terminal)。当控制终端有输入输出时,都传递给该会话的前台进程组。Session 意义在于将多个 jobs 囊括在一个终端,并取其中的一个 job 作为前台,来直接接收该终端的输入输出以及终端信号。 其他 jobs 在后台运行。

前台(foreground)就是在终端中运行,能与你有交互的

后台(background)就是在终端中运行,但是你并不能与其任何的交互,也不会显示其执行的过程

被停止并放置在后台的工作我们可以使用这个命令来查看:

1
jobs

通过这样的一个命令将后台的工作拿到前台来:

1
2
3
# 后面不加参数提取预设工作,加参数提取指定工作的编号
# ubuntu 在 zsh 中需要 %,在 bash 中不需要 %
fg [%jobnumber]

创建进程

​ 在Linux系统中,除了系统启动之后的第一个进程由系统来创建,其余的进程都必须由已存在的进程来创建,新创建的进程叫做子进程,而创建子进程的进程叫做父进程。那个在系统启动及完成初始化之后,Linux自动创建的进程叫做根进程。根进程是Linux中所有进程的祖宗,其余进程都是根进程的子孙。具有同一个父进程的进程叫做兄弟进程
​ 关于父进程与子进程便会提及这两个系统调用 fork()exec()

子进程就是父进程通过系统调用 fork() 而产生的复制品,fork() 就是把父进程的 PCB 等进程的数据结构信息直接复制过来,只是修改了 PID,所以一模一样,只有在执行 exec() 之后才会不同。

为了在一个进程中分裂出子进程,Linux提供了一个系统调用fork()。这里所说的分裂,实际上是一种复制。因为在系统中表示一个进程的实体是进程控制块,创建新进程的主要工作就是要创建一个新控制块,而创建一个新控制块最简单的方法就是复制。

​ 当然,这里的复制并不是完全复制,因为父进程控制块中某些项的内容必须按照子进程的特性来修改,例如进程的标识、状态等。另外,子进程控制块还必须要有表示自己父进程的域和私有空间,例如数据空间、用户堆栈等。

函数fork()分裂出了两个进程:因为自函数fork()之后执行了两遍之后的代码(先子进程一次,后父进程一次)。同时,这也证明了父进程和子进程运行的是同一个程序,也正是这个理由,系统并未在内存中给子进程配置独立的程序运行空间,而只是简单地将程序指针指向父进程的代码;
两个进程具有各自的数据区和用户堆栈,在函数fork()生成子进程时,将父进程数据区和用户堆栈的内容分别复制给了子进程。同时,接下来的内容,父进程和子进程都是对自己的数据区和堆栈中的内容进行修改运算了。
父进程中调用fork()之后会产生两种结果:一种为分裂子进程失败,另一种就是分裂子进程成功。如果fork()失败,则返回-1,;否则会出现父进程和子进程两个进程,在子进程中fork()返回0,在父进程中fork()返回子进程的ID。

在代码中获得当前进程pid的函数为:getpid();

在代码中获得当前进程父进程pid的函数为:getppid()。

这里需要注明一点:父子进程的调度的顺序是由调度器决定的,与进程的创建顺序无关。

​ 当一个子进程要正常的终止运行时,或者该进程结束时它的主函数 main() 会执行 exit(n); 或者 return n,这里的返回值 n 是一个信号,系统会把这个 SIGCHLD 信号传给其父进程,当然若是异常终止也往往是因为这个信号。

​ 在将要结束时的子进程代码执行部分已经结束执行了,系统的资源也基本归还给系统了,但若是其进程的进程控制块(PCB)仍驻留在内存中,而它的 PCB 还在,代表这个进程还存在(因为 PCB 就是进程存在的唯一标志,里面有 PID 等消息),并没有消亡,这样的进程称之为僵尸进程(Zombie)

​ 正常情况下,父进程会收到两个返回值:exit code(SIGCHLD 信号)与 reason for termination 。之后,父进程会使用 wait(&status) 系统调用以获取子进程的退出状态,然后内核就可以从内存中释放已结束的子进程的 PCB;而如若父进程没有这么做的话,子进程的 PCB 就会一直驻留在内存中,一直留在系统中成为僵尸进程(Zombie)。

虽然僵尸进程是已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,在进程列表中保留一个位置,记载该进程的退出状态等信息供其父进程收集,从而释放它。但是 Linux 系统中能使用的 PID 是有限的,如果系统中存在有大量的僵尸进程,系统将会因为没有可用的 PID 从而导致不能产生新的进程。

​ 进程 0 是系统引导时创建的一个特殊进程,也称之为内核初始化,其最后一个动作就是调用 fork() 创建出一个子进程运行 /sbin/init 可执行文件,而该进程就是 PID=1 的进程 1,而进程 0 就转为交换进程(也被称为空闲进程),进程 1 (init 进程)是第一个用户态的进程,再由它不断调用 fork() 来创建系统里其他的进程,所以它是所有进程的父进程或者祖先进程。同时它是一个守护程序,直到计算机关机才会停止。

实例

image-20210915121843368

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<unistd.h>
#include<stdio.h>
int main(void)
{
int id = getpid();
printf("my id is %d\n",id);
int num = fork();
if(num==0)
{//child
int n = getpid();
int pn = getppid();
printf("I am child\nmy id is %d and my parent's id is %d\n",n,pn);
}
else if(num>0)
{
//parent
int n = getpid();
int pn = getppid();
printf("I am parent\nmy id is %d\nmy parent's id is %d\n",n,pn);
}
return 0;
}

fork函数调用一次,会返回两个函数值,对于父进程而言,返回的是子进程的PID(因为一个父进程可能有多个子进程,并且没有一个函数可以使父进程获取其所有的子进程ID),对于子进程返回值是0(这样就能区分父子进程,子进程是可以通过getppid来获取父进程的ID,如果进程创建失败,那么返回给父进程-1)。

image-20210915125351563

杀死进程

1
kill

杀死指定PID的进程 (PID为Process ID)

1
$kill PID

杀死相关进程

1
kill -9 3434

杀死job工作 (job为job number)

1
$kill %job

可以先用 ps -aux查看进程

然后通过kill pid 杀死进程

磁盘管理

查看磁盘和目录的容量

  • 使用 df 命令查看磁盘的容量
1
df

-h: human缩写,以易读的方式显示结果(即带单位:比如M/G,如果不加这个参数,显示的数字以B为单位)

image-20210915221319880

物理主机上的 /dev/sda2 是对应着主机硬盘的分区,后面的数字表示分区号,数字前面的字母 a 表示第几块硬盘(也可能是可移动磁盘),你如果主机上有多块硬盘则可能还会出现 /dev/sdb/dev/sdc 这些磁盘设备都会在 /dev 目录下以文件的存在形式

使用 du 命令查看目录的容量

-s 递归整个目录的大小

-h 对人可读 即显示M/GB

磁盘挂载

img

lsblk

查看磁盘挂载情况

image-20210915221448745

fdisk -l 查看系统所有识别到的磁盘

image-20210915221545182

mkfs 命令格式化磁盘

image-20210915200330145

sudo mkfs.ext4 磁盘(也可以是虚拟软盘)

mount 命令挂载磁盘到目录树

用户要对该文件系统执行 mount 的指令以进行挂载。该指令通常是使用在 USB 或其他可移除存储设备上,而根目录则需要始终保持挂载的状态。又因为 Linux/UNIX 文件系统可以对应一个文件而不一定要是硬件设备,所以可以挂载一个包含文件系统的文件到目录树。

1
mount [options] [source] [directory]
实例

1.增加一块硬盘

可以在虚拟机下添加一个硬块

或者使用 dd命令制作软盘

dd if=/dev/zero of=virtual.img bs=1M count=256

image-20210915222919566

我这里添加了一个新硬盘

使用df -h 查看已挂载的磁盘

image-20210915224037960

然后使用 fdisk -l 查看系统分区情况

image-20210915224258674

发现 sda,也就是刚刚添加的5G硬盘没有挂载

继续使用lsblk还可以查看一些情况

image-20210915224357414

发现 sda确实没有挂载点

就决定挂载它了!

如果是自己创建的软盘再使用mkfs格式化

1
sudo mkfs.ext4 virtual.img

2.分区

sudo fdisk virtual.img(软盘)

sudo fdisk /dev/sdb (添加的硬盘)

按照指示完成分区

image-20210915224757951

image-20210915224847528

这样就分好区了,分了一个主区

image-20210915225032272

使用fdisk -l 发现有了标识符

3.格式化

1
mkfs -t ext4 /dev/sda1

image-20210915225208889

4.挂载

先创建一个目录,使用

mount 设备名 目录名

目录最好建立在/mnt下,这个目录是专门挂载的。

image-20210915225335936

如果重启这种挂载关系就会消失

这是临时挂载

image-20210915225425145

df -h 发现挂载成功了

设置开机挂载

  1. 查看开机挂载fstab文件
    系统开机时会主动读取/etc/fstab这个文件中的内容,根据文件里面的配置挂载磁盘。
    # cat /etc/fstab

每一列代表的含义:
第一列:磁盘的编号或者UUID //挂载点需要使用UUID来进行挂载
第二列:需要挂载到的目录
第三列:文件系统格式
第四列:系统的默认参数,一般填defaults
第五列:是否做dump备份,0表示不备份,1表示每天备份,2表示不定期备份
第六列:是否开机检查扇区:0表示不检查,1表示最早检验,2表示在1之后开始检验

解除挂载

umount 设备名

-------------本文结束感谢您的阅读-------------
感谢阅读.

欢迎关注我的其它发布渠道