Linux - 进程状态 - Linux 当中的进程状态是如何维护的?

进程状态

一个进程在 系统当中有很多的状态,比如:一个进程正在被cpu执行,那这个进程就是一个 r 状态;一个进程已经准备好了,但是其中的运行这个进程需要的资源没有准备好,那么这个进程一人不能运行。

比如 在一个程序当中有一个 scanf()函数,当你 执行这个程序之后,那么这个程序就变成一个 进程了,但是,在当 scanf()函数执行开始时候,你一直没有输入数据,那么这个进程就会一直等待你输入的数据,那么此时这个进程有没有被 cpu 执行呢?肯定是没有的,因为这个进程一直等不到 你输入的数据。

运行状态

操作系统当中的各个 进程都是用 某一种数据结构来把 各个进程的 PCB 链接在一起,从而,只要找到 这个数据结构的 head头,那么就可以找到 当前需要运行的进程 PCB,从而调用到这个进程带 cpu的当中进行执行。 

但是,因为 系统当中想运行的进程有很多,而 cpu 又只能运行一个进程,,cpu 又是少数的资源。所以,这些像运行的进程就会去竞争这个cpu资源,所以,就有了调度器的存在来帮助 cpu 更好的,合理的 运行进程,而不是一个进程一直在运行,其他的进程一个在等待,这些我们后面再说。

cpu 当中为了能管理好 运行的进程,就自己维护了一个数据结构来用于管理当前正在运行的程序。这个数据结构就是 --- 运行队列(runqueue)

在这个队列当中包含了很多属性,但是这些属性都不重要,最重要的是这个维护这个队列的 头尾指针:

struct runqueue
{//运行队列struct task_struct *head;struct task_struct *tail;
```````````
`````````}

 那么,当调度器告诉cpu 现在运行哪一个进程,cpu 就可以直接从这个队列当中,找到这个进程的 PCB ,放到cpu 当中就可以调用这个 进程了。这过程就是 进程在cpu 上排队

 调度器,说白就是一个函数接口,他可以把 把cpu 当中运行队列作为参数传入到函数体内,这样在函数体当中就可以找到这个 ,或者是利用算法,计算出 当前应该要执行的进程。

而,我们把处于 运行队列当中的 进程,这些进程的状态就是 运行状态(运行态 R)

在这个运行队列当中进程,就代表的是:这个进程已经准备好了,随时可以被调度


在将来,如果某一个进程准备好了,他想被cpu所执行,那么他只需要 入队到 运行队列即可。

 只需要等待操作系统的算法计算哪一个进程应该被调度,当计算到 新入队的进程,这个新进程就可以被执行了。

当然,肯定会出现进程占用cpu,不退的情况,比如你写了一个死循环的程序,那么,当这个程序死循环之后,就会一直占用这个 cpu,cpu 肯定不会一直让这个 进程占用这个cpu。所以,给每一个进程都有一个时间片

 简单来说就是,这个进程在cpu上执行的之间只能是 一(或多个)个时间片的时间。如果过了这个时间片,这个进程还没有执行完毕的话,就要在 入运行队列,然后等待操作系统调度cpu执行。

 所以,在上述简单操作系统调度进程 在 cpu 上运行的描述之下,在一段时间之内,所有的在运行队列之上的代码都会被执行。这种情况,我们称之为 --- 多个进程的并发执行

当然,在这段时间之内,肯定不只是执行刚开始的那些进程,在此之中就会,大量的把进程从cpu 上拿下来,再从输入设备当中拿上去cpu 的过程 ,这个过程称之为 --- 进程切换

  因为 cpu 处理速度很快,所以在我们看来,根本感受不到 进程 在切换的过程,这些进程都像是在同时被执行一样,所以看起来像是 这些个进程都是在一起运行一样。

 阻塞状态

 进程就是 我们写出来的程序软件,被执行时,就变成了进程,那么程序怎么多,功能也是千变万化,那么这个程序所需要的 数据也是不同的。那么,很多程序就是需要 某些数据才能运行,数据也是 进程的组成的一部分,所以,数据是不可或缺的!!

那么,进程也不是想拿到什么数据就可以拿到什么数据的,就跟最开始我们举的例子一样,程序当中有 scanf()函数,我们不在键盘上输入数据,那么这个进程就一直不会就收到数据,那这个进程是不能执行的。那这个进程就是没有就绪,也就不能放在 运行队列当中

我们把上述这种没有 就绪的进程,这种进程的状态就是 阻塞状态

当然在一个 操作系统当中,他要管理软硬件资源,就是使用 先描述在组织的方式来管理的,比如他要管理键盘这个硬件,那么就是把管理键盘需要的 属性值,都构造成一个对象,这个对象当中就存储了管理 键盘需要的 属性值。

在这些属性值当中,也有一个队列,这个队列也是像 cpu 当中的 运行队列一样,是用来给要使用这个硬件的 进程来进行排队的。

比如,当前某一个进程需要使用到 键盘,那么好,就去键盘的 队列当中排队去吧,如下图所示:

 这些队列,在 每一个可能被进程使用的硬件当中都存在,作用也是一样的。

我们把这些队列,称之为 --- 等待队列。 

 像上述 scanf()函数进程的例子,如果键盘硬件当中轮到 这个进程来执行了,键盘当中有数据了,那么这个进程就可以读取键盘当中数据,然后,如果这个进程没有数据需要接受了,这个进程也处于就绪状态了,那么这个进程就可以入到 cpu 的运行队列当中去,等待 操作系统调度器执行就行了。

 所以,阻塞队列有 N 多个!!! 有非常多个,想象我们硬件有这么多,而且不止 硬件有阻塞队列。

 挂起状态 - 操作系统对于阻塞状态的优化(换出 和 换入)

那么,这么多的,进程在 怎么多的 阻塞队列当中排队,那么操作系统所管理的内存资源总是会有不足之时。那么如果内存被占满了,不就意味着,系统要进行卡顿吗?

所以,操作系统在此处进行了优化(本来是不优化的,因为此时迫不得已,进行了优化)。

之前我们说过,在队列当中排队是 PCB 在进行排队,此处的优化就在此:

操作系统管理的内存资源已经快满了,就把 一些 进程的 数据和代码 这些 换出外设(比如是磁盘)当中,当 排队轮到这个进程执行之时,才把 外设 当中的 代码 和数据 换入 内存当中。 

 那么,当某一个进程的代码和文件已经被 换出了,那么这个进程此时就是 挂起状态

所以说,一个进程只要是 PCB 在内存当中被操作系统所管理,那么我们就认为是当前进程正在被运行,而 这个进程的 代码和数据 在不在 只是这个进程的不同 状态,代码和数据在不在 不是这个进程在不在的判断条件,PCB 在不在才是。


Linux 当中的任务管理器 -- top

 使用 top 可以查看到 当前实时状态下,所以的正在运行的进程的很多信息,包括下述要说明的 Linux 当中 进程的各种状态:

 Linux 当中的进程状态是如何维护的?

我们上述所描述的状态只是一个框架,对于 操作系统实现的细节,不同的操作系统所实现的细节是不一样的,比如在 Linux 当中就实现了更多的 进程状态,用于更好的管理 进程,这些进程的状态是保存在一个 const 字符数组当中的:

static const char* const task_state_array[] = {"R (running)", /* 0 */"S (sleeping)", /* 1 */"D (disk sleep)", /* 2 */"T (stopped)", /* 4 */"t (tracing stop)", /* 8 */"X (dead)", /* 16 */"Z (zombie)", /* 32 */
};

这就是课本上的操作系统 和  具体实现的操作系统之间差别。

接下来我们来一个一个描述这些个状态。

R(running)运行状态 和 S(sleeping)阻塞状态(浅度睡眠)

 只要进程的PCB是在 cpu 的运行队列当中,那么就说明,这个当前这个进程的状态就是 运行状态

同样,在等待外设在外设的等待队列当中时候,我们称之为 阻塞状态

我们不能拿我们之间的认知去 理解操作系统 在运行时给进程赋予的状态,因为现实在使用 操作系统时,所涉及的很多情况。比如如下情况:

此时我们编写下面这个死循环代码:

 此时,我们在运行 有上述 源文件生成的 text 可执行程序时候,我们来查看这个 text 程序运行状态:
 

发现,此时的 text 进程是 S 阻塞状态,而不是 进程状态。 

我们还发现,上述在描述状态不只是描述  R 或者是 S,而是 R+ , S+ 来描述的;那么此时的 “+”是什么意思呢?

这里的 "+" 其实代表的是,这个进程是在 前台运行的。

什么是前台呢?在上述我们运行程序之后,我们再输入执行,bash 就不会再解析这个指令了。

如果想让一个程序在后台运行的话,需要在 运行之时,加上一个 "&" 符号。这个符号表示的是 这个程序在后台运行。

 当一个程序在后台一直运行话,我们此时使用 ctrl + C 是不能把这个进程给关闭的,因为这个进程现在不受 前台管理了,所以,此时我们要是用 kiil -9 PID 来杀掉程序:

如上述所示,ctrl + C 是不能结束进程的。


所以,其实 bash 命令行解释器在我们输入命令,回车之前,都是阻塞状态(sleeping),在命令行当中,等到我们输入命令,确认输入,然后 bash在进行解析,创建子进程,等等的操作。

如下所示,bash 都是在 S 状态:
 

 D(disk sleep)-- 深度睡眠(也是一种阻塞状态)(磁盘休眠状态)

像上述的 S 状态也是一种阻塞状态,S 的阻塞状态是 浅度睡眠,处于 浅度睡眠的 进程可以被随时唤醒,可以随时响应外部的变化,而唤醒的人,可能是 用户,也可能是 操作系统。 

D 状态深度睡眠,或者称之为 -- 磁盘睡眠


 要了解 深度睡眠,我们先来了解一个场景:

我们之前谈到过挂起状态,挂起状态就是 把 进程的 数据和代码部分 放到外设当中进程暂时的存储,但是 PCB 还在内存当中进程排队,这样的可以减轻 内存的压力。那么这种情况,比如是把 数据和代码 放在 磁盘当中存储的,那么此时进程就肯定要拜托 磁盘,让磁盘帮忙把 数据写入磁盘当中。

或者说,此时某一个进程需要对磁盘当中写入数据,那么这两种情况都导致一个结果,就是 进程需要等到 磁盘写入数据。

如果,磁盘写入数据要考虑很多的情况,比如:找到一个合适空间来存储,判断是否能存储等等情况,所以,磁盘写入是需要耗费时间的。 

 那么,进程在等待的途中,就没事做,优哉游哉的等待 磁盘写好数据给他回应,进程才好把数据是否成功写入的结果返回给上层。

如果,在磁盘等待的途中,操作系统所管理的 内存资源已经严重不足了,那么操作系统就会去想办法优化内存资源,比如:该挂起的挂起,该释放掉释放等等操作。如果是是在没有办法的情况下,操作系统为了保证自己的程序,和 内存资源不会挂掉,那么他就是会去杀后台(也就是杀进程)。

如果操作系统选择杀进程的话,就是以当前能力的操作系统实在没办法优化了,只能杀掉他认为不重要的进程了。

如果此时,因为 上述在等待 磁盘回应的 进程,在操作系统看来就非常的“闲”,那么他有可能就会直接杀掉这个进程。

那么问题就来了,假设,这个进程是在写入一些非常重要的数据,而又恰好,磁盘在写入过程当中,出现了问题,此时,磁盘就要给 进程报错,但是又发现进程已经不在了。

那么这个重要数据就已经丢失了,如果为这个事情开庭的话,你是不是会认为是操作系统的问题?觉得因为是操作系统“杀的”啊,就是他的错。

但是实际不然,可能 操作系统 磁盘 进程,三者都没错。

操作系统:只是在完整自己的本职工作,他的本职就是要让用户一个稳定的,不会时不时就卡顿的平台,供用户使用,如果到了上述那种非常极端的情况,由不让它杀掉这个进程的话,那么可能就会导致整个操作系统的瘫痪,那么此时出问题的就不会这一个进程了,而且所以的进程都会崩掉。

磁盘:当然也不是他,因为它就只是一个跑腿的,只是帮助 进程来在外设当中写入数据,而且,事先 磁盘就会告诉进程,此次写入数据操作是会写入失败的。

进程就更不是了,进程本来就是受害者。

所以,在进程等待磁盘写入数据这么关键的事情之上(在磁盘写入数据完毕之前),我们要让进程不被任何“人“ ”“杀掉”。

我们把上述 这个进程 所处的状态 称之为 --- D(disk sleep)。

D 状态的进程不响应 操作系统的任何请求,所以才不会被操作系统杀掉。在这个状态的
进程通常会等待IO的结束

 当进程 从 磁盘上得到的了 数据被写入成功或者失败的 信息之后,进程才会从 D 状态解除到其他状态。


如果,此时在内存当中出现了很多的 D 状态的进程,操作系统是没有资格杀掉这些进程的,而且,只要是 磁盘没有给 进程返回写入信息,那么就算是把 操作系统重启都是没有办法解决的。 

 所以,此时要么是 直接重启(断电),要么是等待磁盘 返回写入信息

 因为,cpu 处理都是毫秒级别的速度,所以用户是感受不到 进程切换 ,挂起等等操作的过程的,所以,如果当用户已经查找了 哪怕是 一个 D 状态的进程了,说明这个进程已经存在很长时间了,那么就说明操作系统已经在奔溃的边缘了。

一般情况下,D 状态的进程 非常少,而且持续时间很短。 

 因为 D 状态的进程不好演示,演示的话有可能会弄坏操作系统,所以不建议演示,但是可以使用 "dd" 命令来操作,具体请看下述

Linux dd 命令 | 菜鸟教程 (runoob.com)

T (stopped) 停止状态

这指的停止状态不是 这个进程直接被杀掉了,而是 这个进程暂停了 ,所以暂停代表的就是,只是现在停止了,但是后序可以继续执行。

Linux 当中,我们可以使用 kill -l 来查看 kill 指令 能给进程发出的各种信号的信息
 

 在这么多信息当中,有 18 和 19 这两个信号,18 信号是 SIGCONT 就是继续执行进程,19 信号 是 SIGSTOP 就是暂停执行进程

但我们使用 19 信号暂停某一个 进程之后,这个进程当前就处于 T (stopped) 停止状态

 像上述例子当中的 text 就被暂停掉了,当前 text 进程所处的状态就是 暂停状态。

此时,可以使用 kill -18 PID恢复上述 text 进程,但是注意,此时恢复的进程是在后台运行的,在 不受前台的控制。如果此时向停止这个 进程,那么就要使用 kill -9 PID 给这个进程发出9号停止命令。

 T 状态和 S 状态的区别

 S 状态是阻塞状态,进程的PCB 在各个硬件或者其他外设等等设备的 阻塞队列当中排队的进程,此时的状态就是 阻塞状态。也就是说,处于阻塞状态的进程,是在等待某一个设备给予数据,或者是给出信号,才能继续运行。

而,T状态是 不一定需要等待 某一个设备的 数据 或者 信号,就像上述的 text 进程这个例子,我在停止之后,我想什么时候继续执行这个进程,就 使用命令 让他继续执行即可。不一定需要某些硬件的数据才能执行。

也就是说,处于 T 状态的 进程,不一定是在排队等待,而是只是单纯的暂停而已。只是目前想要 这个进程等待一下,暂停一下,可能是等待其他的事件发生。那么我们就可以把这个进程 设置为 T 状态。

进程 处于 T 状态之后,T 进程的代码就完全的暂停执行了,一般而言,进程就不再接受各种信号或数据了。而 S 状态就是要等待某种资源,信号。

其实你也可以把 T 理解成一种阻塞状态,但是不同的是,T 状态的 进程不一定是在 等待某些资源,可能就只是停止了,可以被用户所控制。但是 S 状态是一定要等待某种资源的,这点你要清楚。

 gdb调试器 举例 T 状态的运用场景

当我们在使用 gdb 调试某一个程序之时,我们可能会 逐步调试,可能会 逐块调试,或者是 直接运行到 断点处,运行到条件断点处等等调试操作。

那么这些操作在执行 的之间时刻,我们是要分析这个代码,在当前状态下的各个信息是否正确。以此来逐步推断出 可能的错误所在。

那么,其中判断,推断的过程肯定是需要时间的,在此期间,代码是停止运行的,那么如何让正在运行的进程停止下来,那么其实也是一种阻塞,但是其中不会只有S状态的阻塞,还有T状态的阻塞。

如下所示,当我们开始执行 text 的gdb 调试时,就是 gdb 是处于  S 阻塞状态下的,但是 text 是处于 T 停止运行状态下的:

 所以,T 状态有自己的运用场景,和 S 状态是有区分的。

X (dead)死亡状态 -  Z (zombie) 僵尸进程 - 孤儿进程

 这个状态就顾名思义了,一个进程运行完毕了,那么这个进程就是退出内存,释放资源,那么这个进程所处的状态就是死亡状态了。

Linux 当中的 X 状态就和 很多教科书当中所说的 终止态是一样的。

 但是,你会很好奇,为什么一个进程已经死亡了,那么为什么要有这种状态呢?

其实你想得没错,X 状态只是一个返回状态,你不会在任务列表里看到这个状态

所以,在程序死亡之前(处于 X 状态之前),还有一个状态,叫做 Z 僵尸状态操作系统要在 处于僵尸状态的 进程给 做检查,此时,操作系统不是直接就把这个进程的 资源等等信息直接释放清除了,而是还有维持一段时间,当都检查确认之后再进行释放。

那么,这个信息维持给谁看呢?就是这个僵尸进程的父进程,因为此时最关心当前这个僵尸进程的就是父进程,因为父进程 创造这个 子进程是需要耗费资源的,同时也可能是 互相之间是有 交互的。

当父进程拿到 这个 僵尸状态的子进程的信息之时,才会把这个子进程的状态 变成 死亡状态,释放空间 。

把这种 已经死亡的课程,但是需要有父进程来收集子进程的信息,操作系统为这个子进程维持的状态,就叫做 Z (zombie) 僵尸 状态

 进程一般在推出时,如果父进程没有主动回收子进程的信息,子进程就会一直处在Z的状态。而进程相关的资源,尤其像是 task_struct 结构体对象不能被释放。

僵尸进程在死亡时候 ,如果父类没有做出回应(接收信息),那么这个僵尸进程就会一直占用 内存资源,我们把这种由于僵尸进程占用内存资源的情况,称之为 --- 因为僵尸进程而引发的 内存泄漏 问题。

 当然,如果你想看某一个进程的 僵尸状态话,可以使用一下代码:

#include <stdio.h>
#include <stdlib.h>
int main()
{pid_t id = fork();if (id < 0) {perror("fork");return 1;}else if (id > 0) { //parentprintf("parent[%d] is sleeping...\n", getpid());sleep(30);}else {printf("child[%d] is begin Z...\n", getpid());sleep(5);exit(EXIT_SUCCESS);}return 0;
}

因为,cpu 处理的速度很快,假设你是使用使用 "./" 来运行的某个程序的话,是查看不了 这个进程的 僵尸状态的。因为 此时这个进程是 bash 这个命令行解释器进程的 子进程,当 这个子进程结束运行时,就会立马被 bash 父进程接受信息,从而转变为 死亡状态,释放资源


 孤儿进程

如果,在一对父子进程当中,父进程先死亡了,但是此时子进程还在运行,没有挂掉,在这种情况下,这个子进程的 PPID 会变为1;也就是,此时这个子进程的父进程变为了 PID 为1 的这个进程。

使用 top 命令 来查看 PID 为1 的进程是什么:
 

发现是 systemd 这个进程。 
我们来查看 systemd 这个进程的详细信息:
 

 其实 1 号进程,也就是这个 systemd 进程,其实就是操作系统本身。(有些操作系统当中和可能不是 systemd ,也可能是 inti,都是代表的是 操作系统进程)

 我们把上述的,父进程先退出,子进程 的 父进程就是 1 ,操作系统进程了,这个子进程 就称之为 -- “孤儿进程”。这个子进程就被 操作系统给领养了

为什么要被领养呢?原因很简单,当一个进程在结束之时,要先变成僵尸状态,就要被父进程回收信息,总是要有一个为这个子进程回收信息的,那么就让操作系统帮忙了。

 其实,如果这个子进程是在父进程 运行之时才创建的(就是我们上述写的代码,查看僵尸进程的例子),那么,不是 bash 不想管这个子进程,是因为 这个子进程不是 bash 进程所创建的,是有 bash 创建的这个子进程的 父进程 所创建的。而 "爷爷不管孙子

 而。操作系统不一样,不需要使用一些接口来转移关系,直接在内核层面就可以直接 修改父子关系,做上“收尸者”。


僵尸进程的危害

其实关于僵尸进程的危害,我们在上述已经阐述过一次了,就是当父进程如果一直没有接收 子进程僵尸状态的信息的话,那么子进程就会一直处于僵尸状态,操作系统就会一直帮助 子进程来维持在内存当中,等待父进程来接收信息;

那么,就会造成因为 僵尸进程 带来的 内存泄漏问题。

 父进程之所以要接收子进程返回的信息,是因为:父进程花了资源和时间去创建了子进程,目的就是为了让子进程帮忙父进程来办事,那么子进程现在要 死亡了,称为僵尸进程了,那么子进程现在把这个事情办得怎么样了,父进程是需要知道的。

像操作系统要维护 僵尸状态的子进程的话,就要维护这个子进程的 task_struct 结构体对象;而且,如果父进程创建了多个子进程的话,那么如果此时 变成僵尸状态的不止 一个 子进程的话,那么所造成的内存泄漏问题就更严重了。


关于 Linux操作系统当中的 进程执行状态的关系图

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/120126.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在群晖NAS中搭建WebDav服务,并实现公网访问

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《速学数据结构》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 1. 在群晖套件中心安装WebDav Server套件1.1 安装完成后&#xff0c;启动webdav服务&#xff0c;并勾选HTTP复选…

物流数字化转型有什么意义?

物流领域的数字化转型具有深远的意义&#xff0c;因为它彻底改变了供应链中的货物和信息流动方式。这一转变是由数字技术集成推动的&#xff0c;旨在提高效率、可见性和客户满意度。以下是其重要性的一些关键方面&#xff1a; 1.提高效率和降低成本&#xff1a;数字化转型通过…

使用了lua-resty-http库进行 爬虫

lua-resty-http是一个基于OpenResty的HTTP客户端库&#xff0c;用于在Lua中进行HTTP请求和响应的处理。它提供了简单易用的接口&#xff0c;可以方便地进行网页抓取和爬虫开发。 使用lua-resty-http进行爬虫&#xff0c;需要先安装OpenResty和lua-resty-http库&#xff0c;并将…

Hadoop 请求数据长度 Requested Data length 超过配置的最大值

一、问题 现象 Spark 任务速度变慢&#xff0c;也不失败。 DataNode 内存足够 CPU 负载不高 GC 时间也不长。 查看 DataNode 日志&#xff0c;发现有些日志出现很多 Netty RPC 超时。超时的 destination 是一个 NameNode 节点&#xff0c;然后查看 NameNode 节点的日志&…

Java支付宝沙箱环境支付,官方Demo远程调试【内网穿透】

文章目录 前言1. 下载当面付demo2. 修改配置文件3. 打包成web服务4. 局域网测试5. 内网穿透6. 测试公网访问7. 配置二级子域名8. 测试使用固定二级子域名访问 前言 在沙箱环境调试支付SDK的时候&#xff0c;往往沙箱环境部署在本地&#xff0c;局限性大&#xff0c;在沙箱环境…

Unity的碰撞检测(四)

温馨提示&#xff1a;本文基于前一篇“Unity的碰撞检测(三)”继续探讨两个游戏对象具备刚体的触发检测&#xff0c;阅读本文则默认已阅读前文。 &#xff08;一&#xff09;测试说明 在基于两个游戏对象都具备触发器和刚体且属性一致的条件下&#xff0c;若二者刚体的BodyType…

分布式日志和链路追踪

分布式日志 实现思路 分布式日志框架服务的实现思路基本是一致的&#xff0c;如下&#xff1a; 日志收集器&#xff1a;微服务中引入日志客户端&#xff0c;将记录的日志发送到日志服务端的收集器&#xff0c;然后以某种方式存储数据存储&#xff1a;一般使用ElasticSearch分…

【Mybatis源码】XMLConfigBuilder构建器 - 读取XML配置初始化Configuration对象

XMLConfigBuilder是Mybatis中定义的进行构建Configuration对象的类,此类用于读取XML配置文件创建并初始化Configuration对象; 上一篇中我们介绍了XMLConfigBuilder构建器加载XML配置文件以及创建Configuration对象https://blog.csdn.net/m1729339749/article/details/133983…

护眼灯买哪种好? 推荐五款儿童护眼台灯

台灯如何选择&#xff0c;随着人们生活水平的提高及科技的不断进步&#xff0c;台灯的品质也得到了极大的提高&#xff0c;在生活中很多时候都需要使用台灯&#xff0c;但是市面上的台灯那么多&#xff0c;台灯如何选择。本次小编为大家推荐五款好用的护眼灯。 1.书客护眼台灯L…

NodeRed 配置 Modbus

2023-10-27 点右上角添加之后&#xff0c;选择这个服务。为了好记。我把名称改成了&#xff1a;127.0.0.1:502 点右上角[完成] 出来了

70 搜索插入位置

搜索插入位置 题解1 二分查找防越界写法 题解2 STL大法两行 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c; 并返回其索引。如果目标值不存在于数组中&#xff0c; 返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O ( l o g n ) O(log n…

node开发微信群聊机器人第⑤章

▍PART 序 看本文时&#xff0c;请确保前4章都已经看过&#xff0c;不然本章你看着看着思维容易跳脱&#xff01;再一个机器人教程只在公众号&#xff1a;“程序员野区”首发。csdn会跟着发一份&#xff0c;未经博主同意&#xff0c;请勿转载&#xff01;欢迎分享到自己的微信…

记一次EasyExcel导出时将空格丢失的问题

EasyExcel的issues可以看到 需求&#xff1a;导出的数据以树形展示&#xff0c;如下&#xff1a; 导出结果&#xff1a; 原因&#xff1a; EasyExcel会默认去除掉空格&#xff0c;autoTrim熟悉默认为true&#xff0c;只需要设置为false即可。

低代码软件的价格考量:成本效益与投资回报

数字化转型的今天&#xff0c;我们常听到“低代码”这个概念&#xff0c;那低代码软件价格到底如何呢&#xff1f;很多厂商并没有公布软件价格情况&#xff0c;让很多企业在采购的时候也是一头雾水。当然&#xff0c;市场上也存在一些厂商公开透明价格&#xff0c;比如Zoho Cre…

Blueprints - 虚幻中的行为树(Behavior Tree)

一些以前的学习笔记归档&#xff1b; 简单的说&#xff0c;行为树可以让agent&#xff08;代理&#xff0c;指可以自主活动的游戏角色等&#xff09;做出决定&#xff0c;可以理解为AI的大脑&#xff1b; 行为树自上而下的从树的根节点&#xff08;Root节点&#xff09;开始读…

2016年亚太杯APMCM数学建模大赛C题影视评价与定制求解全过程文档及程序

2016年亚太杯APMCM数学建模大赛 C题 影视评价与定制 原题再现 中华人民共和国成立以来&#xff0c;特别是政治改革和经济开放后&#xff0c;随着国家经济的增长、科技的发展和人民生活水平的提高&#xff0c;中国广播电视媒体取得了显著的成就&#xff0c;并得到了迅速的发展…

Ajax、Json深入浅出,及原生Ajax及简化版Ajax

Ajax 1.路径介绍 1.1 JavaWeb中的路径 在JavaWeb中&#xff0c;路径分为相对路径和绝对路径两种&#xff1a; 相对路径&#xff1a; ./ 表示当前目录(可省略) ../ 表示当前文件所在目录的上一级目录 绝对路径&#xff1a; http://ip:port/工程名/资源路径 2.2 在JavaWeb中…

智慧停车视频解决方案:如何让AI助力停车管理升级?

一、项目背景 停车场的管理区域由于面积比较大&#xff0c;进出车辆多&#xff0c;所以在保安方面决不能有任何的麻痹和松懈&#xff0c;继续采用过去保安方式已远远不能满足现代安全防范的需求。为满足停车场的安全和科学系统化管理的需要&#xff0c;以及为了对随时发生的情…

STM32-程序占用内存大小计算

STM32中程序占用内存容量 Keil MDK下Code, RO-data,RW-data,ZI-data这几个段: Code存储程序代码。 RO-data存储const常量和指令。 RW-data存储初始化值不为0的全局变量。 ZI-data存储未初始化的全局变量或初始化值为0的全局变量。 占用的FlashCode RO Data RW Data; 运行消…

支持向量机(SVM)

一. 什么是SVM 1. 简介 SVM&#xff0c;曾经是一个特别火爆的概念。它的中文名&#xff1a;支持向量机&#xff08;Support Vector Machine, 简称SVM&#xff09;。因为它红极一时&#xff0c;所以关于它的资料特别多&#xff0c;而且杂乱。虽然如此&#xff0c;只要把握住SV…