Linux:进程

news2024/12/24 2:23:36

进程

  • 知识铺垫
    • 冯诺依曼体系结构
    • 操作系统(OS)
  • 进程概念
  • 进程的查看
    • ps 命令
    • 获取进程 pid
    • 文件内查看进程
    • 终止进程的方式
      • kill命令
      • 快捷键
  • 进程的创建 fork
    • fork 返回值问题
  • 进程状态
    • 运行状态 :R
    • 休眠状态:S (可中断)和 D(不可中断)
    • 暂停状态:T 和 t(追踪式暂停)
    • 死亡状态:X
    • 僵尸状态:Z
  • 孤儿进程

知识铺垫

冯诺依曼体系结构

  • 冯诺依曼提出程序存储执行以及计算机由五个部分组成:输入设备、输出设备、存储器、运算器、控制器。

结构如下:
在这里插入图片描述

输入设备:键盘、鼠标、麦克风
输出设备:显示屏、扬声器、打印机
这里的存储器指的是: 内存

输入设备 和 输出设备 统称为外围设备,外围设备交互速度一般是比较慢的;

存储器也被称为内存,相较于外设数据的访问速度是快得多。最明显的例子:磁盘 和 内存;

中央处理器(CPU)访问数据的速度是最快的。

在数据层面上,中央处理器(CPU)是不会对输入设备和输出设备进行直接交互的,而是直接对内存进行交互;而外设只会对内存进行打交道。

要交互也不是不行,但是整体的效率就会以外设为主了。为什么这么说呢?

如果将外设之间与CPU直接进行数据交互,我们都知道CPU的计算速度是很快的,从外设中获取数据时,得让外设处理完成后才能得到数据,那么就会出现木桶效应。所以说CPU直接对外设进行交互的话整体数据的访问速率就会以外设为主了。

因为有了内存的存在,我们可以对数据进行预加载处理:CPU在对数据进行计算的时候,可以直接在内存中获取数据,就不需要访问外设了。从而提高了数据计算的效率。

操作系统(OS)

  • 操作系统:一款对软硬件做管理的软件

操作系统包括有:内核 和 其他程序

内核:进程管理,内存管理,文件管理,驱动管理
其他程序:函数库, shell程序

设计操作系统的目的:下至与硬件进行交互,管理所有的软硬件资源;上至为用户程序提供一个良好的执行环境。

操作系统是一款纯正搞管理的软件。为什么这么说呢?

管理者在管理一部分事务时,管理者和被管理者其实是没有之间进行沟通的
例如:校长在管理一所偌的大学校时,校长会直接对每位学生进行管理吗?并不会,如果校长针对每位学生都做管理的话,那么他将忙的不可开交。那么校长是如何管理好一所学校的呢?他只需要将他需要管理的方式下达给主任,然后主任在将需要管理的方式在下达到辅导员,每个辅导员又会将信息转达到班长上,最后每位学生都会接收到对应的要求。之后辅导员会将每位学生的被管理的信息汇总起来,传达给主任,然后各位主任会将所有学生的信息都交到校长手上。至此,校长就对学校内的所有学生信息进行处理,将每个学生描述成一个对象,将这些对象用链表组织连接起来形成一个表格,对整个表格的学生信息进行增删查改,从而起到管理者管理被管理者的效果。

操作系统的管理也是如此,对软硬件做管理时,OS并不会直接对这些软硬件进行管理做处理,会先将这些软硬件先用struct结构体进行属性封装描述成一个个对象,然后对这些结构体对象用链表或更高效的数据结构组织起来进行管理。

可以说操作系统中管理的本质就是:先描述,再组织

  • 系统调用:在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分
    由操作系统提供的接口叫做系统调用。

  • 库函数:开发者对部分系统调用进行适度封装,从而形成库

有了上述知识铺垫那么就很容易理解进程了。

进程概念

可执行文件的本质其实是一个普通的二进制文件。文件存储在磁盘当中(文件 = 文件内容 + 文件属性)。

当在Windows下对一个可执行文件进行双击,又或者在Linux命令行下进行 ./执行可执行文件时,其实就是将文件的文件内容加载到内存中。

现在来分析一句话:将磁盘中的可执行文件内容加载到内存,这个可执行文件的就是被操作系统管理的进程。这句话准确吗?

诸如,一个外国人进入不属于他的国家中,那么就可以说这个人就是这个国家的公民吗?

这个说法是不对的。一个合法的公民需要有这个国家的公民身份证,接受这个国家的法律约束等等。满足这个国家对这个人的管束才能说这个人是属于这个国家的公民。

进程也是如此,当一个可执行文件的代码被加载到内存中时,需要被操作系统管理起来,才能被称为进程。

当然这样说进程概念还是不够具体的。

加载到内存中的可执行文件并不是只有一个,会有许许多多的二进制文件被加载到内存中。面对内存中如此多且杂乱的程序,操作系统该如何去管理呢?

对于进程的管理,OS当然是进行:先描述,再组织

  • 描述:在每个可执行程序加载到内存时,在OS内核里面,会对这些程序的数据或代码创建一个数据结构对象PCBPCB 在Linux操作系统中的名称为: task_struct 。这些PCB对象中会提取进程中的所有属性,在PCB的结构体中保存有指向该进程的地址,方便找到进程在内存中的位置。每个进程都有属于自己的PCB,这就是OS对进程描述的过程。
  • 组织:每个PCB都有指向下一个PCB的结构体指针,诸多PCB之间就可以通过链表的方式组织起来。OS通过管理这些PCB从而达到间接管理到各个进程的效果。结束一个进程就好比,在PCB链表中找到对应PCB进程属性节点,然后对该节点进行释放删除的操作,对每个进程的管理就变成了对PCB链表的 增删查改

在这里插入图片描述

因此进程概念应该是这样的:

  • 进程内核关于进程的数据结构加上当前进程的代码和数据

进程的查看

ps 命令

ps ajx                  #查看当前机器下所有正在运行的进程

提示:在使用 ps 查看进程命令时应该和 grep 过滤行代码进行配合,方便我们查找进程的信息。

在这里插入图片描述
在这里简单介绍一下:pidppid

pid:当前进程的进程编号
ppid:当前进程的父进程编号

获取进程 pid

在C语言中,可以通过 getpid() 来获取 pid 编号;通过 getppid() 来获取 ppid 编号。

在使用这些函数前需要包含库函数#include <unistd.h>

下面来举个例子:
在这里插入图片描述
编译运行:

在这里插入图片描述
运用 ps 命令来查看一下对应的进程状态:

ps ajx | head -1 && ps ajx | grep 2964 | grep -v grep 

上述命令行中,grep也是一个进程,在同时输入ps命令和grep命令时会将grep进程也显示到终端,因此在这里需要将grep进程过滤掉:
在这里插入图片描述
仔细观察上述代码,在创建的./myporcess程序中,出现一个 ppid 编号为:29043 的父进程( bash 进程)。

为什么会出现 bash 进程呢?

bash 是一个命令处理器,能够处理用户输入命令的进程。每当用户输入一个命令时,都是在 bash 进程中创建一个子进程

这也是为什么新创建程序的中会存在 bash 进程的原因。

文件内查看进程

在根目录下存在一个叫 proc 的目录,该目录是保存进程相关属性的目录,属于内存级别的目录,只有当OS启动的时候该目录才会存在。

ll /proc

在这里插入图片描述

查看 proc 目录,可以看到很多蓝色数字编号的目录,这些编号正是操作系统中正在运行的进程编号 pid。

还是上述代码为例子,当新运行起来一个程序时在 proc目录下就会创建一个进程的目录:
在这里插入图片描述
在这里插入图片描述
查看 proc 目录下的 编号为32217的pid目录:
在这里插入图片描述
在编号为32217的pid这个文件下就是该进程的所有属性内容了。

当我们终止 pid为32217这个进程后,再次查看 proc 目录下的文件就会发现编号为 32217 的目录不存在了:
在这里插入图片描述
在这里插入图片描述

终止进程的方式

kill命令

使用 kill 命令不仅可以杀死前台进程,后台进程也可以被杀掉

  1. 方法一:利用 kill 命令,使用终止信号 -9
kill -9  进程编号
  1. 方法二:killall
killall 进程名称

快捷键

  • 利用快捷键方式:ctrl + c

进程的创建 fork

通常情况下,在window下双击一个可执行文件 或者 在Linux下 ./执行一个可执行文件,都是将二进制文件加载到内存,然后被操作系统管理起来,变成一个进程。

有没有一种方式就是手动创建进程呢?答案是可以的。

在Linux下可以通过调用 fork 函数来创建子进程,使用该函数时需要包含 #include<sys/types.h> 头文件

下面来举个例子,编写这样一串代码:

在这里插入图片描述
编译后结果如下:在这里插入图片描述
结果很奇怪,为什么会打印出两行b的结果?

代码是不会骗人的,在代码执行过程中,代码执行过程是顺序执行的。

下面来查对应的 pid 和 ppid ,就可以一目了然:
在这里插入图片描述

运行:
在这里插入图片描述

第一行b中的 pid:742 指代是 myporcess 进程的编号,可以由打印a的pid推断出来
第二行b中的 pid:743 指代是 myporcess 进程创建的子进程编号,可以由第二行 b 的 ppid :742 推断出来

fork 单词含义为分支的意思,上述代码中在打印a和b之间调用了 fork 函数,此时代码就不会按照原来的线性规则执行,会在调用 fork 函数之后创建一个新的执行流,原来的执行流会继续执行后续代码,新的执行流也会在 fork函数调用完后,继续执行后续代码。这也是终端为什么会打印出两行b的原因。

上述代码只是举例,当然正常情况下 fork 不会去这样使用,会根据 fork 函数的返回值判断进行使用。

下面来介绍一下 fork 函数返回值:

#include <unistd.h>
pid_t fork(void);

当父进程创建成功了后,会将子进程的 pid 返回给父进程,子进程的返回值为0,失败返回-1

还是上述代码,但是进行稍微修改,将fork返回值、地址进行打印显示:

在这里插入图片描述
结果如下:
在这里插入图片描述

父子进程执行先后顺序是不一定的,不同机器下会有不同结果,这个完全取决于调度器的调度管理。

上面的进程创建是成功的,因此可以看到的结果是:

在第一行b的打印结果中,父进程在fork函数执行后, ret 接收到了子进程的pid 1436
在第二行b的打印结果中,子进程在fork函数执行后, ret 接收到了返回值为:0

但是为什么 ret 的地址会一样呢?甚至奇怪一个函数会有两个返回值。

地址一样是关乎到进程地址空间的话题再后续会讲到这里先不提。不过可以确定的是这里的地址并不是物理上的地址,而是虚拟地址。

首先来简单使用一下 fork 函数的使用场景,再来回答为什么一个函数会有两个返回值的问题。

根据fork 的返回值进行 if 条件判断,限定父子进程执行代码:

在这里插入图片描述

父进程执行一次,停顿两秒,子进程执行一次,停顿一秒。运行结果如下:

在这里插入图片描述
前面提到过执行流问题问题,在 fork 函数之后会出现两个执行流

上面代码的也演示了,可以通过 if-else 分支语句来控制执行流

正常的C语言,是不能同时满足 if 和 else,而且还是不能同时执行两个 while,在这里为什么又可以了呢?

因为,在 fork函数执行后,所创建的子进程会共享同一份代码

创建进程时,是将磁盘中的文件和数据加载到内存,然后OS通过创建PCB结构体对象来管理这些加载到内存中的数据和代码。PCB结构体中存储的是加载到内存中的数据和代码的属性,记住了是属性。当我们在执行 fork 函数时,就会创建子进程,创建方式就是:将父进程的PCB拷贝一份下来(当然子进程的PCB也有属于自己的私有属性),父进程的PCB指向内存中对应进程的数据和代码,子进程的PCB也是如此,这也是为什么fork 函数之后会公用同一份代码的原因了。
在这里插入图片描述

这里的子进程PCB的创建方式只能说是比较笼统介绍一下,具体会更加复杂一些。

每个进程之间是独立的,一个进程的运行是不会影响到另一个进程的运行。

举例:将上面的代码跑起来
在这里插入图片描述
此时,再将2066杀掉
在这里插入图片描述
可以看到,2065 进程不受影响。

加载到内存中的文件内容是:代码 和 数据。代码是写死的,没有人就是在代码运行时会将代码更改掉,因此每次运行代码时是只读的。那么加载到内存中的数据呢?

下面再来看个问题:同时共享同一份代码,一个进程在运行时修改数据内容时,另一个进程会被影响吗?

在这里插入图片描述
在 fork 函数之前创建一个x变量,在父进程打印一次内容后将x值进行修改,运行看看结果:
在这里插入图片描述
fork 函数值后,代码是公用的。当有一个执行流尝试更改数据内容时,OS会自动触发一种机制,这个机制叫做写实拷贝。写实拷贝会将原有的数据内容拷贝到父进程或是子进程当中,这样就可以保证各个进程之间的独立性。

fork 返回值问题

一个函数最多一个返回值,为什么 fork 函数可以出现两个返回值?

当一个函数执行到 return 语句的时候可以说该函数的主体功能是已经完成的了。

fork 是一个系统调用的函数,功能是创建一个子进程,将父进程的PCB进行拷贝,然后形成子进程的PCB,当fork函数执行到 的return语句时,说明上述功能就是已经执行完的了。那么就形成了两个执行流:一个是父进程的执行流,另一个是子进程的执行流。但是 fork函数还没有完,还需要执行完最后一句return语句,因此就形成了一个函数会出翔两个return返回值的现象。
在这里插入图片描述

进程状态

在讲进程状态前,先来提两个概念:阻塞挂起

  • 阻塞:进程因为等待某种条件就绪,从而导致一种不推进的状态(进程等待某种资源就绪的过程)

进程阻塞在用户角度看了就是运行的程序卡住了,进程阻塞就是在等待某种资源就绪。这些资源包括有 硬件资源、系统调用、内存资源、I/O操作等等。资源是有限的,当进程发生阻塞时就是有其他进程正在使用这些资源,只有等资源被其他进程用完后才能轮到自己。

举例:当我们下载一个比较大的文件时,突然有一段时间网断了。此时,CUP会将该进程停止运行,等什么时候网络资源空闲了再来运行该进程。下载的进程就被中断了被称为该进程阻塞,这个阻塞过程就是等待网络资源的过程。

OS是一款搞管理的软件,不仅仅是进程会被管理,硬件也会被管理起来。OS搞管理的本质就是先描述,再组织
OS为了管理好硬件,会将每一个硬件都创建对应的结构体对象,然后通过数据结构的模型将这些结构体对象链接起来。
每一个硬件对应就是一个结构体对象节点。
在这里插入图片描述

在CPU运行的进程不只有一个,当某个进程缺少资源时,CPU会要求该进程先获取对应的资源,此时的OS会将当前进程的PCB链接到对应资源尾部,等待该资源能被自己利用。当资源获取到后,OS会将该进程的PCB链接回CPU继续运行起来。

在这里插入图片描述

PCB可以被维护到不同的结构体对象的队列中!!!

  • 挂起:操作系统在管理内存时,会将闲置的某个进程的数据和代码暂时性的存放到磁盘中(闲置:表示进程再等待某种资源的过程)。内存中只保存该进程的PCB结构体对象,这个状态叫做挂起状态。

当该进程获取到资源时,操作系统再将该进程的数据和代码加载到内存中,进而可以达到减少内存资源开销的作用。

挂起状态严格意义上被称为阻塞挂起

下面以Linux操作系统进程状态展开分析:

在Linux中,task_struct 表示进程的PCB。task_struct 是一个结构体内部会包含进程的诸多属性,其中就包含有状态属性

struct task_struct
{
	int status; //状态属性
	//... 其他属性
}

其中,不同的状态会被定义成不同的宏。例如:

#define R 0   
#define S 1
#define D 2
#define T 4
#define t 8
#define X 16
#define Z 32

下面来简单介绍不同的进程状态:

运行状态 :R

进程处于 R 状态时,并不是就在 CPU 正在运行。在 CPU 上运行的进程并不是就只有一个,会有很多进程。

为了调度运行这些进程,操作系统会维护一个叫进程的运行队列 (简化伪代码:struct task_struct* runqueuq )CPU只需要调用该队列中的进程即可。进程在该队列中,那么该进程就被称为运行状态

所以,看一个进程处于什么状态时,就看这个进程在哪里排队。

举例看一下正在运行的进程的状态:

在这里插入图片描述

直接一个死循环,运行查看对应进程状态:

在这里插入图片描述

休眠状态:S (可中断)和 D(不可中断)

那么,下面将代码更改一小部分,添加一行打印代码:

在这里插入图片描述
运行后,查看一下对应进程状态:
在这里插入图片描述
为什么会这样?进程明明就是正在死循环打印。

死循环是没有问题,当我们在往显示屏打印内容的时候就是在访问硬件资源,访问期间该进程就是在等待显示器的资源。OS会将该进程的PCB就会被链接到硬件资源的队列中进行排队,CPU计算速度是很快的,所以该进程的大部分时间都是在等待资源的过程,只有那么一瞬间是被执行的

可以说在上万次查看该进程的状态时,只有那么一瞬间是运行状态。大部分时间都是在等待资源的过程,所以查看进程时的状态会被显示为S+状态。

除了上面例子,最典型的就是使用 scanf 函数时,该进程在为了获取键盘输入资源时,将会卡在终端,等待用户的输入。

所以可以说:休眠状态本质就是一种阻塞状态

  • 进程D状态:不可中断的休眠状态

这种状态通常发生在等待磁盘I/O操作完成的进程上,因为进程正在与硬件交互,且这个交互过程不允许被其他进程或中断打断。在此状态下,进程无法被信号中断,很多时候甚至连关机都很难关机,若要强行中断只能是拔电源,但是会造成磁盘数据丢失。

一般 D 状态出现的概率非常非常小,若是电脑出现了D的进程状态,说明该电脑快宕机了,内存吃紧,OS腾不开空间。

暂停状态:T 和 t(追踪式暂停)

  • T状态:将正在运行的进程设置为暂停状态,一般为用户需求

  • 使用 kill 命令,向指定的进程发送 -19 信号(19 号信号为 SIGSTOP 意思表示为暂停一个进程)。

kill -19 进程编号

以代码为例:

在这里插入图片描述
编译运行:

在这里插入图片描述
当前进程处于 S+ 状态,上面提到过,这里不在赘述。下面将该进程置于暂停状态:

在这里插入图片描述

如果要将该进程恢复,那么就可以使用 18 号信号(SIGCONT)让当前进程继续运行

kill -18 进程编号

在这里插入图片描述

注意:使用该信号后,会将进程处于后台运行(后台运行的进程会显示为 S 状态,没有+的符号 )。在没有 + 号状态下的进程我们使用快捷键 Ctrl+c是杀不了后台进程的,此时只能使用 kill 的 9 号信号才能杀掉。前台运行的进程,都可以使用快捷键 Ctrl + c杀掉

  • t 状态:追踪式暂停状态

最典型的例子就是:在调试代码时,只要在某行代码处设置了断点,那么调试中就不会将所有代码一次性跑完,会卡在断点处。这个暂停方式就是追踪式暂停。

以上面代码为例子,用gdb进入调试状态:
在这里插入图片描述
在第 10 行代码打个断点,然后输入 r 命令将代码调试起来:

在这里插入图片描述
此时再来查看当前进程状态:

在这里插入图片描述

死亡状态:X

进程处于终止状态表示死亡状态,这个状态过程是一瞬间,在进程中很难查看到,终止的一瞬间就结束了。

僵尸状态:Z

  • 进程退出码:一个用于表示进程终止原因的数字代码

创建进程是为了计算机能够帮助我们去办事,办事的结果无疑就是两个:完成和未完成。

对于一些进程,我们会寻求他们执行后的结果,也就是事办的怎么样了?

我们编写的代码都是以 main函数为入口,然后完成程序的主体功能,再到 return 语句结束,不过一般我们自己写的main函数的返回值都会设置成返回 0。

其实 main 函数的返回值是进程的退出码。

查看当前进程退出码的方式:

echo $?

在Linux输入的代码也是进程,我们可以尝试输入一条命令后查看进程退出码:
在这里插入图片描述
如果我们输入命令时带入错误选项,结果会报错,此时再来查看一下进程的退出码:
在这里插入图片描述
进程退出码:2 表示 ls 命令执行后未能执行正常的工作。

进程退出码是为了获取进程工作的结果。如果一个进程退出后直接就是 X 死亡状态,那么父进程或者就是OS能够获得进程退出码吗?

答案是不行的。

  • 僵尸状态:在Linux中,一般进程退出后并不会即可退出,它会维持一个状态,这个状态被称为僵尸状态(状态的维持是为了OS或者是父进程去获取当前退出进程的退出码)

举个简单的例子:当一个人正常非正常死亡,警察为了调查。不会立即将尸体交还给家属,会将尸体交给法医去调查死因结果,等到结果出来后才会交给家属处理。

下面用代码来看看进程的僵尸状态,创建子进程:
在这里插入图片描述
运行,查看进程状态:

在这里插入图片描述

此时将子进程杀掉:

在这里插入图片描述

僵尸状态的维持是需要消耗内存的,就好比动态开辟的内存空间,如果使用了内存空间不去释放就会导致内存泄漏。

操作系统为了避免内存泄漏问题,会将僵尸进程进行回收处理,当然我们也可以自己手动回收,回收方式会在后续博客中提及。

孤儿进程

  • 孤儿进程:父进程创建子进程,当父进程被提前终止运行,这个子进程就被称为孤儿进程

上面提到过,一个进程的终止并不会直接就被干掉,会将进程结束的退出码返回给父进程。

如果父进程被干掉了,bash进程会来接收父进程的退出码。问题来了,子进程的退出码谁来接收?

下面来举个例子:

在这里插入图片描述
父子进程同时运行,当5秒后父进程结束运行,再来查看结果:

在这里插入图片描述
下面是进程在第五秒前和第五秒后的进程状态:
在这里插入图片描述
可以看到子进程(11673),在父进程结束运行后 ,子进程的 ppid 直接变成了 1 ,进程状态也由 S+ 变成了 S。

进程 1 代表的是 Init 进程,当一个进程结束运行后会进入僵尸状态,这是为了让父进程获取退出码。上面的父进程结束后,子进程变成了孤儿进程,为了能够让子进程在结束后的僵尸状态得到回收,Init (1号)进程就充当了领养孤儿的角色。在子进程退出后,子进程的退出码得到处理,子进程的僵尸状态会被Init 进程回收,从而避免进程僵尸没有及时回收造成的危害。

这篇博客内容就介绍到这里,感谢观看!

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

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

相关文章

PCBA方案设计充气泵设计

随着科技的不断进步&#xff0c;充气泵在户外活动、露营和旅行中变得越来越常见。而充气泵的性能和稳定性主要依赖于其控制系统&#xff0c;其中芯片的设计和开发是充气泵方案的关键。SIC8833芯片是一款专门为充气泵设计的芯片&#xff0c;接下来我们来讲下充气泵方案芯片SIC88…

[java基础揉碎]面向对象多态

目录 问题引出多态: 代码如下: 多态(多种状态)的介绍: 多态的具体体现: 方法的多态: 对象的多态: 我们用多态解决最上面的问题: ​编辑 ​编辑 多态的注意事项和细节: 多态的向上转型: ​编辑 多态的向下转型 属性的重写问题 问题引出多态: 代码如下: 新建一个食物…

Open3D 利用四个点计算球心和半径 (28)

Open3D 利用四个点计算球心和半径 (28) 一、算法介绍二、算法实现1.代码2.结果一、算法介绍 给定的四个点坐标,计算球心和半径,提供验证的四个点来比较最终的结果是否准确。 二、算法实现 1.代码 代码如下(示例): import numpy as npdef calculate_sphere_center_…

如何选择AI项目:从任务自动化到社会价值的全面考虑

目录 前言1 任务自动化的首要选择1.1 公司痛点分析&#xff1a;深入挖掘潜在问题1.2 数据集的收集与大小考虑&#xff1a;确保数据质量和规模匹配 2 AI项目的商业潜力2.1 技术考察与性能目标&#xff1a;确保技术选择符合项目需求2.2 商业考虑与成本效益分析&#xff1a;全面评…

《向量数据库指南》——Milvus Cloud BYOC 是什么?

Milvus Cloud BYOC 是什么? 10 个月前,Zilliz 正式发布了全托管的 Milvus 云服务——Milvus Cloud,为用户提供低成本、高性能的向量数据库服务。Zilliz 在打磨细节之际,也收到了来自管控较为严格的行业公司和组织的需求,他们大多来自于汽车、金融、医疗等行业,需要将数…

【Java设计模式】十六、责任链模式

文章目录 1、责任链模式2、案例&#xff1a;批假3、在源码中的实际应用 1、责任链模式 一个请求可以有多个对象处理&#xff0c;但这些对象能处理的权限不同。比如公司请假&#xff0c;部门负责人、副经理、经理都能批&#xff0c;但他们能批的最大天数不一样。每次让员工直接…

加密与安全_PGP、OpenPGP和GPG加密通信协议

文章目录 PGPOpenPGPGPG工作原理工作流程用途案例说明过程 代码实现pom依赖PgpEncryptionUtilPgpDecryptionUtilCommonUtilsPgpEncryptionTest 小结 PGP PGP (Pretty Good Privacy) 是一种加密通信协议&#xff0c;用于保护电子邮件和文件的安全性和隐私。它通过使用加密、数字…

福派斯狗粮质量怎么样?

亲爱的宠物家长们&#xff0c;你们是不是也在为选择一款高质量的狗粮而犯愁呢&#xff1f;今天&#xff0c;我要和大家分享一下福派斯狗粮的质量问题&#xff0c;希望能够为你们提供一些参考。 &#x1f43e; 首先&#xff0c;让我们来聊聊福派斯狗粮的原材料。福派斯狗粮选用的…

机器学习模型—支持向量机 (SVM)

机器学习模型—支持向量机 (SVM) 支持向量机 (SVM) 是一种强大的机器学习算法,用于线性或非线性分类、回归,甚至异常值检测任务。SVM 可用于各种任务,例如文本分类、图像分类、垃圾邮件检测、笔迹识别、基因表达分析、人脸检测和异常检测。SVM 在各种应用中具有适应性和高效…

【VUe】简略学习 vue

Vue 是一套用于构建用户界面的渐进式框架。要想使用这个框架&#xff0c;就需要先在页面中引用&#xff1a; 如何使用&#xff1a; 来到控制台&#xff1a; 数据绑定 若要在标签里替换&#xff0c;就需要使用 v-bind 指令了&#xff1a; 在标签里&#xff08;尖括号里&#xf…

jpg 转 ico 强大的图片处理工具 imageMagick

点击下载 windows, mac os, linux版本 GitHub - ImageMagick/ImageMagick: &#x1f9d9;‍♂️ ImageMagick 7 1. windows程序 链接&#xff1a;https://pan.baidu.com/s/1wZLqpcytpCVAl52pIrBBEw 提取码&#xff1a;hbfy 一直点击下一步安装 2. 然后 winr键 打开cmd 然…

软文推广怎么做效果更好?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 软文推广想要效果好&#xff0c;你可以这么做&#xff1a; 1. 选对平台很重要 首先&#xff0c;你得找准你的目标受众喜欢去哪&#xff0c;然后就去那儿发软文。别盲目乱投&#xff0c;…

【计算机网络】1.5 分组交换网中的时延、丢包和吞吐量

A.分组交换网中的时延 当分组从一个节点沿着路径到后一节点时&#xff0c;该分组在沿途的各个节点经受了几种不同类型的时延。 时延的类型 处理时延 - d n o d a l d_{nodal} dnodal​ 处理时延包括以下部分—— a. 检查分组首部 b. 决定分组导向 排队时延 - d p r o c d_{…

原生JavaScript,根据后端返回扁平JSON动态【动态列头、动态数据】生成表格数据

前期准备&#xff1a; JQ下载地址&#xff1a; https://jquery.com/ <!DOCTYPE html> <html><head><meta charset"utf-8"><title>JSON动态生成表格数据,动态列头拼接</title><style>table {width: 800px;text-align: cen…

2.1 塑性力学——应力分析基本概念

个人专栏—塑性力学 1.1 塑性力学基本概念 塑性力学基本概念 1.2 弹塑性材料的三杆桁架分析 弹塑性材料的三杆桁架分析 1.3 加载路径对桁架的影响 加载路径对桁架的影响 目录 个人专栏—塑性力学 应力 \color{blue}应力 应力&#xff1a;内力的分布集度 单元体 \color{blue}单…

C++初学

1>思维导图 2>试编程 提示并输入一个字符串&#xff0c;统计该字符中大写、小写字母个数、数字个数、空格个数以及其他字符个数要求使用C风格字符串完成 #include <iostream> #include<string.h> using namespace std;int main() {string str;cout <<…

选择短信群发平台要小心陷阱

你知道短信群发平台也有陷阱吗&#xff1f;选择短信群发平台很重要&#xff0c;今天小编就为您介绍短信群发平台有哪些陷阱&#xff1f; 这几点你要注意了&#xff1a; 1、扣量&#xff0c;有些不靠谱的短信群发平台开始以低价诱惑“客户”&#xff0c;但是发送过程中就暗中扣…

不锈钢电阻器-栅极电阻器 - 为什么要使用它们呢?

常规 如果你看一个典型的吉他放大器的原理图&#xff0c;你会注意到有一个电阻器与第一个电子管的栅极串联&#xff0c;通常在68K左右&#xff0c;还有一个电阻器与功率管的栅极串联&#xff0c;通常为1.5K或5.6K&#xff0c;你可能会偶尔看到非常大的电阻&#xff0c; 例如 4…

学习JavaEE日子 Day24 TreeSet,内置比较器,外置比较器,HashMap

Day24 TreeSet 1.TreeSet 1.1 TreeSet的使用 注意&#xff1a;TreeSet的使用和HashSet一样 public class Test01 {public static void main(String[] args) {//创建TreeSet集合的对象TreeSet<String> set new TreeSet<>();//添加元素set.add("麻生希"…

Excel生成 chart 混合图表

在开发中有这样一个需求&#xff0c;邮件预警的时候&#xff0c;要求邮件主体内容是一个Chart 图表&#xff08;生成后的img&#xff09;&#xff0c;邮件需要有附件&#xff0c;且附件是Excel列表加图表&#xff0c;图表类型是混合图。 回顾&#xff1a;在之前一篇讲到如何使用…