CPU、进程相关知识点

CPU利用率和CPU负载的区别是什么

前瞻知识

提到CPU利用率,就必须理解时间片。什么是CPU时间片?我们现在所使用的Windows、Linux、Mac OS都是“多任务操作系统”,就是说他们可以“同时”运行多个程序,比如一边打开Chrome浏览器浏览网页还能一边听音乐。

cpu时间片

但是,实际上一个CPU内核在同一时刻只能干一件事,那操作系统是如何实现“多任务”的呢?大概的方法是让多个进程轮流使用CPU一小段时间,由于这个“一小段时间”很短(在linux上为5ms-800ms之间),用户感觉不到,就好像是几个程序同时在运行了。上面提到的“一小段时间”就是我们所说的CPU时间片,CPU的现代分时多任务操作系统对CPU都是分时间片使用的。

cpu使用率

CPU使用率,就是程序对CPU时间片的占用情况,即CPU使用率 = CPU时间片被程序使用的时间 / 总时间。比如A进程占用10ms,然后B进程占用30ms,然后空闲60ms,再又是A进程占10ms,B进程占30ms,空闲60ms,如果在一段时间内都是如此,那么这段时间内的CPU占用率为40%。CPU利用率显示的是程序在运行期间实时占用的CPU百分比。

大多数操作系统的CPU占用率分为用户态CPU使用率和系统态CPU使用率。用户态CPU使用率是指执行应用程序代码的时间占总CPU时间的百分比。相比而言,系统态CPU使用率是指应用执行操作系统调用的时间占总CPU时间的百分比。系统态的CPU使用率高意味着共享资源有竞争或者I/O设备之间有大量的交互。

cpu负载

而CPU负载显示的是一段时间内正在使用和等待使用CPU的平均任务数。
简单理解,一个是CPU的实时使用情况,一个是CPU的当前以及未来一段时间的使用情况。举例来说:如果我有一个程序它需要一直使用CPU的运算功能,那么此时CPU的使用率可能达到100%,但是CPU的工作负载则是趋近于“1”,因为CPU仅负责一个工作嘛!如果同时执行这样的程序两个呢?CPU的使用率还是100%,但是工作负载则变成2了。所以也就是说,CPU的工作负载越大,代表CPU必须要在不同的工作之间进行频繁的工作切换。无论CPU的利用率是高是低,跟后面有多少任务在排队(CPU负载)没有必然关系。
如果单核CPU的话,负载达到1就代表CPU已经达到满负荷的状态了,超过1,后面的进行就需要排队等待处理了。如果是是多核多CPU,假设现在服务器是2个CPU,每个CPU有2个核,那么总负载不超过4都没什么问题。
可以通过uptime、w命令查看CPU平均负载,使用top命令还能看到CPU负载总体使用率以及各个进程占用CPU的比例。
在这里插入图片描述

查看物理CPU个数

cat /proc/cpuinfo| grep “physical id”| sort | uniq| wc -l

查看每个物理CPU中core的个数(即核数)

cat /proc/cpuinfo| grep “cpu cores” | uniq

查看逻辑CPU的个数

cat /proc/cpuinfo| grep “processor”| wc -l

如果CPU负载很高,利用率却很低该怎么办

CPU负载很高,利用率却很低,说明处于等待状态的任务很多,负载越高,**代表可能很多僵死的进程。**通常这种情况是IO密集型的任务,大量任务在请求相同的IO,导致任务队列堆积。
生产环境造成CPU利用率低负载高的具体场景常见的有如下几种。

场景一:磁盘读写请求过多就会导致大量I/O等待

进程在cpu上面运行需要访问磁盘文件,这个时候cpu会向内核发起调用文件的请求,让内核去磁盘取文件,这个时候cpu会切换到其他进程或者空闲,这个任务就会转换为不可中断睡眠状态当这种读写请求过多就会导致不可中断睡眠状态的进程过多,从而导致负载高,cpu低的情况。

场景二:MySQL中存在没有索引的语句或存在死锁等情况

我们都知道MySQL的数据是存储在硬盘中,如果需要进行sql查询,需要先把数据从磁盘加载到内存中。当在数据特别大的时候,如果执行的sql语句没有索引,就会造成扫描表的行数过大导致I/O阻塞,或者是语句中存在死锁,也会造成I/O阻塞,从而导致不可中断睡眠进程过多,导致负载过大。
同样,可以先通过top命令观察,假设发现现在确实是高负载低使用率。
在这里插入图片描述

然后,再通过命令ps -aux查看是否存在状态为D的进程,这个状态指的就是不可中断的睡眠状态的进程。处于这个状态的进程无法终止,也无法自行退出,只能通过恢复其依赖的资源或者重启系统来解决。以下图中没有D状态**(不可中断睡眠状态)的进程**。

在这里插入图片描述

Linux上进程的五种状态

  1. R (TASK_RUNNING):可执行状态,只有在该状态的进程才可能在CPU上运行。而同一时刻可能有多个进程处于可执行状态。
  2. S (TASK_INTERRUPTIBLE):可中断的睡眠状态,处于这个状态的进程因为等待某某事件的发生(比如等待socket连接、等待信号量),而被挂起。
  3. D (TASK_UNINTERRUPTIBLE):不可中断的睡眠状态,进程处于睡眠状态,但是此刻进程是不可中断的。TASK_UNINTERRUPTIBLE状态存在的意义就在于,内核的某些处理流程是不能被打断的。
  4. T (TASK_STOPPED or TASK_TRACED):暂停状态或跟踪状态。Z (TASK_DEAD - EXIT_ZOMBIE):退出状态,进程成为僵尸进程。进程已终止,但进程描述还在,直到父进程调用wait4()系统调用后释放。

如果CPU负载很低,利用率却很高该怎么办

这表示CPU的任务并不多,但是任务执行的时间很长,大概率就是你写的代码本身有问题,通常是计算密集型任务,生成了大量耗时短的计算任务
怎么排查?直接top命令找到CPU使用率最高的进程,定位到去看看就行了。如果代码没有问题,那么过段时间CPU使用率就会下降的
CPU利用率达到100%怎么排查问题

1、通过top找到CPU占用率高的进程

top -c -o %CPU

2、通过top -Hp pid命令查看CPU占比靠前的线程ID

在这里插入图片描述

3、再把线程ID转化为16进制,printf “0x%x\n” 74317,得到0x1224d

printf “0x%x\n” 13729
0x35a1

4、通过命令jstack 72700 | grep ‘0x1224d’ -C5 --color找到有问题的代码

注意:jstack的对象是java进程的PID,而不是java线程的PID

ps命令获取cpu前5的进程信息

ps -eo pid,ppid,%mem,%cpu,cmd --sort=-%cpu | head -n 5

Linux中进程的六种状态

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在Linux内核里,进程有时候也叫做任务)。
在这里插入图片描述

R运行状态(running)

并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列。
问题:如果只有一个CPU,可不可以存在多个R状态的进程。
现场试一下:用fork创建子进程,试一下

在这里插入图片描述
在这里插入图片描述

可以看到,是可以存在多个运行状态的。
进程是R状态,不代表正在运行,代表可被调度。换句话说,进程只有是R状态才可被调度,其他状态要先转为R状态,才能被OS调度。
在这里插入图片描述

S睡眠状态(sleeping)
意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。
代码实现一下:
在这里插入图片描述
在这里插入图片描述

休眠状态是在等待某种条件就绪,在休眠状态,可被操作系统杀死,也叫浅度睡眠。
处于这个状态的进程因为等待某某事件的发生(比如等待socket连接、等待信号量),而被挂起。这些进程的task_struct结构被放入对应事件的等待队列中。当这些事件发生时(由外部中断触发、或由其他进程触发),对应的等待队列中的一个或多个进程将被唤醒。在这里插入图片描述

由上图看到,一般情况下,进程列表中的绝大多数进程都处于S状态(除非机器的负载很高)。毕竟CPU就这么一两个,进程动辄几十上百个,如果不是绝大多数进程都在睡眠,CPU又怎么响应得过来。

D磁盘休眠状态(Disk sleep)

有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
可是为什么有了S状态,又来一个D状态呢?有什么作用呢?
举例说明:在这里插入图片描述

T停止状态(stopped)

可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
###Z僵尸状态(Zombies)
代码实现一下僵尸状态:
子进程5秒结束,父进程死循环,不读取子进程结束信息。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5秒之后,子进程变为僵尸状态。

僵尸进程是什么

是一个比较特殊的状态。当子进程退出并且父进程没有读取到子进程退出的返回代码时,就会产生僵死(尸)进程, 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。

为什么要有僵尸进程

因为我们必须得保证一个进程跑完,启动这个进程的父进程或是操作系统必须得知道这个进程退出时,把我们交代得任务完成得怎么样了,成功还是失败了。必须要知道子进程得运行结果。当子进程退出的时候,它的信息不会立即释放,会存在PCB中,没有人读取,这个状态不会被释放掉,这个状态就是僵尸状态。

僵尸进程的危害

进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎样了。可父进程如果一直不读取,那子进程就一直处于Z状态?是的!
维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护?是的!
那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!
因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!内存泄漏?是的!

X死亡状态(dead)

这个状态只是一个返回状态,你不会在任务列表里看到这个状态。当父进程读取子进程的返回结果时,子进程立刻释放资源。死亡状态是非常短暂的,几乎不可能通过ps命令捕捉到。

孤儿进程

所谓孤儿进程,故名思义,和现实生活中的孤儿有点类似,当一个进程的父进程结束时,但是它自己还没有结束,那么这个进程将会成为孤儿进程。
孤儿进程会被init进程(1号进程)的进程收养,当然在子进程结束时也会由init进程完成对它的状态收集工作,因此一般来说,孤儿进程并不会有什么危害.
代码实现一下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
    pid_t id = fork();
    if(id < 0){
    perror("fork");
    return 1;
    }
    else if(id == 0){//child
        printf("I am child, pid : %d\n", getpid());
        sleep(10);
    }else{//parent
        printf("I am parent, pid: %d\n", getpid());
        sleep(3);
        exit(0);
    }
    return 0;
}

在这里插入图片描述
在这里插入图片描述

由图可以看出,当父进程结束时,父进程由4416变为1号进程,子进程4417被1号进程领养。

进程组与session

在Linux中:

  • 打开terminal,也就是终端程序,之后可以获得一个shell
  • 通过ssh连接到linux的ssh-server 服务器
    通常我们都是通过以上两种方式来获得一个shell,之后运行程序的,此时我需要纠正一个概念,我们通常都说获得一个shell,本质上来说,我们获取了一个session(会话,以下session都是会话)

案例1 前台ping百度

ping www.baidu.com

我们使用 ps ajfx 来看一下

  1. pid,pgid,sid均为890的 sshd 守护进程生成一个SID为1494的session,同时创建了一个pid为1494的子进程“sshd: helper [priv]” ,并且创建了一个进程组,此进程就是进程组的leader,进程组的PGID等于此进程的pid 1494,这个进程就是该session的leader
  2. “sshd: helper [priv]”创建了一个PID为1518子进程 “sshd: helper@pts/2” ,其实就是开了一个虚拟终端 pts
  3. 虚拟终端pts生成了一个SID为1519的session,创建了一个pid为1519的子进程 “bash”,并且创建了一个新的进程组,新进程组的PGID等于新的子进程的PID为1519,这个子进程为进程组的leader,也是这个session的leader。
  4. bash创建了一个pid为1779的子进程 “ping www.baidu.com”,同时创建一个新的进程组,PGID为1779,并且这是一个前台进程

案例2 后台ping 百度

ping www.baidu.com &

可以看到,ping百度 这个操作的“交互”已经放到后台了,但是依旧像终端输出,我们可以正常输入命令ls,pwd等,执行返回也都正常
ps ajfx
在这里插入图片描述

不一样的地方在于

在这里插入图片描述
在这里插入图片描述

下面一些是BSD风格的参数

  • < 高优先级进程
  • N 低优先级进程
  • L 在内存中被锁了内存分页
  • s 主进程
  • l 多线程进程
    • 代表在前台运行的进程
      可以看出
  • 执行 ping www.baidu.com 的时候ping是前台运行的进程, bash是后台运行的进程
  • 执行 ping www.baidu.com & 的时候ping是后台运行的进程, bash是前台运行的进程

进程组

进程的概念大家都能理解的话,进程组就很好说了,其实就是一堆进程捆一起了,之后形成一个组就叫进程组了
这么做肯定是有意义的,不然Linux也不会这么搞,主要还是为了方便管理。
公司为了方便管理,给人分组,方便分配工作;社会为了方便管理,给人区分成年人,未成年人,老人;我们又因为爱好,信念等被分成了各种各样的小组…
系统把同一个job(作业)的进程分成一个组,既然有组织肯定得有组长,组的ID(PGID)就采用组长的PID
这里有一个问题,如果组长进程死亡了,小组还存在吗?如果存在组长归谁?
如果组长进程死亡了,小组只要还剩下进程就会存在,此时组长不会变,PGID也不会变;就像纪念一样…

#include <unistd.h>
#include <stdio.h>

int main()
{
    setbuf(stdout, NULL);
    pid_t pid;
    pid = fork();
    if(pid == 0){
        printf("child pid: %d\n", getpid());
        while(1){
            sleep(1);
            printf("child\n");
        }
    } else {
        printf("father pid %d\n", getpid());
        while(1){
            sleep(1);
            printf("father\n");
        }
    }

}

在这里插入图片描述
在这里插入图片描述

从ps的结果可以看到,我们的程序创建了两个进程,两个进程属于同一个进程组,PGID为29938
现在我们kill 掉进程组leader 29938

kill -9 29938

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

好家伙,父进程被杀死后,子进程直接把PPID设置为1,但是进程组PGID依旧没变,还是29938 ,session的id SID也没有发生变化,还是29756
此时这个子进程被称为孤儿进程
这里我们就需要注意了,一个木马或者后门如果主进程还存在子进程,仅仅 kill -9 pid 杀死主进程可能是没用的,因为不会杀死子进程

问题来了,如果我想把这些木马病毒进程都干掉,怎么操作?

我见过各种骚操作,有的是写脚本,有的是手动挨个杀,用killall、pkill等等,这种回复一看就是没遇到那种进程pid,进程名称一直变化的
其实非常简单,我们只需要把这个进程组给杀死就好了

kill -9 -PGID #没有看错,其实就是在 PGID前面加个减号
在这里插入图片描述

可以看到,父子进程都起来了,pid分别为29949和29950
这个时候我们杀掉这个进程组

kill -9 -29949

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

可以看到,这个进程组已经没有了,渣都不剩!
这里一定要注意,你杀的是一个进程组,一定要注意,进程组里是否有正常业务进程,别杀错了

Session

其实文章开头我们已经简单提到过了,我们一般讨论的都是shell
session,我们打开一个新的终端就会创建一个session,每个session都是由一个或者多个进程组组成的,每个进程组称为
job,这里job不是任务,而叫作业

从描述中可以看出,session管理的范围要比进程组大,打开一个终端,你执行100条命令,只要没有新的session生成(调用 setsid()函数可以生成新的session ),那么这些命令可以通过session进行统一管理,当然最常见的管理方式还是全部杀死,但是这个杀伤力太大了,所以一般不使用,主要还是了解session的概念,从web安全过来对于session这种机制应该很容易理解。
session中的第一个进程(一般是bash)的PID就是session的SID
现在大招来了,如何干掉整个session呢?

pkill -s SID
在这里插入图片描述

可以看到,fk的SID为29756

pkill -e -s 29756

在这里插入图片描述

可以看到,杀掉了这个SID下的三个进程,分别为 29756, 29957, 29958
-e 参数是现实杀掉了谁,多人性化
在这里插入图片描述

可以看到,杀掉了bash进程后,ssh链接就断开了

  • 27
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值