当前位置: 首页 > news >正文

【Linux】冯诺依曼体系结构、操作系统、进程概念、进程状态、环境变量、进程地址空间

目录

  • 一、冯诺依曼体系结构
  • 二、操作系统(OS)
    • 1. 操作系统是什么
    • 2. 操作系统如何做管理
    • 3. 系统调用和库函数概念
  • 三、进程
    • 1. 进程是什么?
    • 2. 描述进程-PCB
    • 3. 查看进程的方法
  • 四、进程状态
    • 1 运行、阻塞和挂起状态
    • 2 Linux中的进程状态
  • 五、进程优先级
    • 1. 什么是优先级
    • 2.查看优先级
    • 3. 修改优先级
  • 六、其他概念
  • 七、环境变量
    • 1.引入
    • 2.环境变量和本地变量的关系
    • 3.命令行参数表和环境变量表
    • 4.子进程获取环境变量的方式
  • 八、进程地址空间
    • 1.虚拟地址空间的引入
    • 2.虚拟地址空间布局
  • 3.为什么要存在虚拟地址空间?

一、冯诺依曼体系结构

冯诺依曼思想包括:存储程序、程序控制和计算机的五大功能部件。
存储程序:将程序存放在计算机的存储器中。
程序控制: 按指令地址访问存储器并取出指令,经译码依次产生指令执行所需的控制信号,实现对计算的控制,完成指令的功能。
五大功能部件:控制器、运算器、存储器、输入设备、输出设备。
其中,运算器完成算术运算、逻辑运算,
控制器控制指令的执行, 根据指令功能给出实现指令功能所需的控制信号;
控制器和运算器构成了中央处理器CPU。
存储器就是内存,存放程序和数据,带电存储,具有掉电易失的特性。
输入设备能够输入操作者提供的原始信息,并将其转化为机器能识别的,如键盘、鼠标等。
输出设备将计算机处理的结果用人们或其他机器能够接受的方式输出,如显示屏。
在这里插入图片描述

  • 在不考虑缓存的情况下,CPU只能对内存进行读写,不能直接访问其他设备。
  • 外设(输入设备或输出设备)想要输入或输出数据,也只能写入内存或从内存中读取。

二、操作系统(OS)

1. 操作系统是什么

操作系统是一个进行软硬件资源管理的软件。
操作系统包括:内核(进程管理、内存管理、文件管理、驱动管理)、其他程序(如函数库,shell程序等等)
操作系统为什么对软硬件资源进行管理呢? 操作系统通过管理好软硬件资源(手段),给用户提供良好(安全、稳定、高效、功能丰富)的执行环境(目的)。

2. 操作系统如何做管理

先描述,在组织。将需要管理的对象用结构体描述,再将每个结构体进行连接,形成链表一样的数据结构。
当操作系统下达命令后,驱动程序就会对这些结构进行增删查改等操作。
计算机的层状结构:
在这里插入图片描述

3. 系统调用和库函数概念

  • 系统调用:在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。在执行一段程序时,比如printf(“hello world”)时,实际上进行了系统调用,但我们并不知道,因为编译器帮你做了。
  • 库函数:系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。

三、进程

1. 进程是什么?

进程是程序的一个执行实例,是正在执行的程序。
以上是书本中的概念,一句话来概括:进程=内核描述进程的数据结构+当前进程的代码和数据。
当我们写好一段代码经过编译、链接等过程后生成了可执行程序,此时的可执行程序是一个文件,存储在磁盘中。当我们运行该程序时,该程序的代码和数据就会被加载到内存中。此时操作系统会将进程的各种属性放在一个叫做PCB的结构体中,并将PCB用链表等数据结构管理起来,方便进行增删查改。

2. 描述进程-PCB

  • 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
  • 课本上称之为PCB(process control block),Linux操作系统下的PCB是:task_struct。

Linux中描述进程的结构体叫做task_struct,它会被装载到RAM(内存)里并且包含着进程的信息。
task_struct内容分类

  • 标示符:描述本进程的唯一标示符,用来区别其他进程。
  • 状态:任务状态,退出代码,退出信号等。
  • 优先级:相对于其他进程的优先级。
  • 程序计数器:程序中即将被执行的下一条指令的地址。
  • 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。
  • 上下文数据:进程执行时处理器的寄存器中的数据。
  • I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
  • 记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
  • 其他信息。

3. 查看进程的方法

进程的信息可以通过/proc系统文件夹查看

  • 要获取PID为1的进程信息,需要查看/proc/1这个文件夹。
    在这里插入图片描述
  • 大多数进程信息同样可以使用top和ps这些用户级工具来获取。在这里插入图片描述
    当我们执行上述程序,便可以通过以下指令查询到该进程的信息。
    在这里插入图片描述
    通过系统调用查看进程PID
  • 进程id:PID,通过getpid()获取
  • 父进程id:PPID,通过getppid()获取
    在这里插入图片描述
    在这里插入图片描述
    当我们多次执行程序,能够发现每次启动时进程id都不同,但父进程id都相同
    在这里插入图片描述
    这是因为这些进程有共同的父进程bash——命令行解释器。在这里插入图片描述

通过系统调用创建进程
创建进程需要用到fork,先通过man手册来了解一下fork。在这里插入图片描述
fork能够创建子进程,在父进程中,返回值为子进程id,在子进程中返回值为0。
在这里插入图片描述
在这里插入图片描述
显然,fork调用后的内容打印了两边,原因是fork()创建了子进程,父子进程都打印了fork后。

我们还可以通过搭配if-else来进行分流,利用fork的返回值进行区分
在这里插入图片描述

在这里插入图片描述
fork之后执行流会变成两个执行流,当有一个执行流尝试修改数据时,操作系统会为该进程将代码和数据拷贝一份,再进行修改,我们称之为写时拷贝。

四、进程状态

1 运行、阻塞和挂起状态

程序在运行时,需要CPU读取程序的数据并进行计算,但进程的数量一般会多于CPU,所以操作系统采用了运行队列来对进程进行管理。进程入队列,等待CPU资源。CPU调度进程就是从运行队列中,找到进程PCB,并执行进程对应的代码和数据。
进程的运行状态并不是指进程正在运行,而是指这个进程的PCB在CPU的运行队列中。
进程还会占用外设资源,但外设的运行速度很慢,也会出现多个进程访问一个硬件的情况,所以这里进程也需要排队。当CPU调度的某个进程需要访问外设时,操作系统就会把这个进程放到硬件的等待队列中,直到硬件准备就绪,此时这个进程的状态就是阻塞状态
当多个进程状态都是阻塞时,这些进程无法被CPU立即在执行,需要排队很长时间,进程的代码和数据始终占用着内存,操作系统为了避免这样的浪费,会将这些进程的代码和数据暂时保存在磁盘上,但保留进程的PCB在内存里,这样的进程,称之为挂起进程。等到进程需要的硬件资源准备就绪后,操作系统会将进程的代码和数据唤回内存。

2 Linux中的进程状态

运行状态:并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列
里。
在这里插入图片描述
在这里插入图片描述
只要执行一个死循环程序,即可观察到运行状态。

休眠状态:当我们像上述代码中添加一个printf语句,观察现象。
在这里插入图片描述
观察到此时状态变成了S,这就是休眠状态。
因为我们的代码访问了显示器,显示器是外设,速度很慢,99%的时间都是进程等待显示器就绪,1%的时间是CPU在运行,所以查看到进程是休眠状态,是阻塞状态的一种。

停止状态:当进程被停止时,也是阻塞状态的一种。

kill -19 + 进程id --- 停止运行进程
kill -18 + 进程id --- 继续运行进程

在这里插入图片描述
可以看见当我们输入kill -19后,进程的状态从R+变成了T,也就是停止了。
这里的+是用来标志这个进程是前台还是后台进程,状态后面带+,表示该进程在前台,不带+则是后台进程。
后台进程在运行时,shell命令行可以使用,但无法使用ctrl+C将后台进程终止,需要使用kill -9来杀掉进程。
磁盘休眠状态:有时候也叫不可中断睡眠状态,在这个状态的进程通常会等待IO的结束。
当阻塞进程太多,内存空间不足,操作系统会将一些进程挂起,但如果依旧无法解决问题,OS就会将进程杀死,但杀死进程可能会导致数据的丢失,造成巨大损失,所以就提供了磁盘休眠状态,用D来表示,这样的进程无法被OS杀掉,只能等IO结束进程自己醒来。但这种状态一般情况下不会出现,只有在高IO的情况下才可能会出现。
跟踪状态:当我们调试某个可执行程序时,如果打了断点,当进程运行到断点就会停下来,等待我们的下一步操作,此时查看进程的状态就是跟踪状态,用小写字母 t 表示。在这里插入图片描述
僵死状态
一个进程在完成任务之后,其父进程或操作系统需要知道该任务完成的结果,所以当进程终止时,OS的机制是不立即释放该进程的内存资源的,要保存到其父进程来读取进程的结果。
但如果某个进程退出后,没有被父进程或OS回收,这样的进程就是僵尸进程。
下面用一段代码演示僵尸进程的产生,首先创建一个子进程,让子进程在父进程之前退出,此时子进程就会处于僵死状态。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{pid_t id = fork();if (id == 0){printf("我是子进程,pid:%d, ppid:%d\n", getpid(), getppid());sleep(5);exit(0);}else if (id > 0){int count = 10;while (count--){printf("我是父进程,pid:%d, ppid:%d\n", getpid(), getppid());sleep(1);}}return 0;
}

在这里插入图片描述

可见当程序执行五秒之后子进程退出但父进程不回收,子进程的状态变为Z+僵死状态。
当父进程运行结束,操作系统会将父进程和子进程一起回收,避免内存泄漏。
进程的退出状态也属于进程的基本信息,会被保存在进程PCB中,也就是说如果父进程一直不读取子进程的退出状态,那么PCB就要一直维护这种状态,这是就会产生内存泄露的问题。
僵尸进程无法被杀死,因为它已经死了。
死亡状态:当进程死亡后,操作系统会很快的回收进程的所有的资源和数据,以至于我们无法观察到X状态。它的PCB和对应的代码和数据都被释放,不再占用内存资源。
孤儿进程:如果一个程序运行时,父进程先退出,那么子进程就变成了孤儿进程,此时1号init进程就会回收它。
在这里插入图片描述
当我们用刚才的程序,提前杀死父进程,可以看到子进程的PPID变成了1,并且该进程变成了后台进程。
作为操作系统是必须要领养这个孤儿进程的,为了防止内存泄漏!

五、进程优先级

1. 什么是优先级

cpu资源分配的先后顺序,就是指进程的优先权(priority)。
优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。

2.查看优先级

在这里插入图片描述
通过ps -l指令可以查看进程的信息,其中PRI和NI组合起来用来表示进程的优先级。
PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高。
那NI呢?就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值。
PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为: PRI(new)=PRI(old)+nice。
这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行。
所以,调整进程优先级,在Linux下,就是调整进程nice值。
nice其取值范围是-20至19,一共40个级别。

3. 修改优先级

想要修改优先级,需要使用top指令,并且需要管理员身份,这个指令用来修改nice值,范围是-20到19,如果输入范围以外的数,也不会获得更大或者更小的数值,因为OS不会让我们过度修改nice值。
方法:进入top后按“r”–>输入进程PID–>输入nice值

六、其他概念

竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。
独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰。
并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行。
并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。

七、环境变量

1.引入

我们平时使用的Linux指令其实是可执行程序,但我们自己创建的可执行程序在运行时需要加上./,而系统提供的指令不需要,这是为什么呢?
要执行一个程序需要先找到它,这也是为什么我们要在前面加上./,指的是当前目录,而系统指令的位置在/usr/bin目录下,想要让我们创建的可执行程序不用加上./可以将其拷贝到/usr/bin目录下,但不建议这样做,因为自己创建的程序未经过严格的测试,会污染系统的指令池。
那为什么在/usr/bin目录下系统就能够找到呢?
因为系统中存在环境变量PATH,操作系统在启动时会定义一个PATH变量,可以利用echo $PATH指令进行查看。
在执行系统指令时,操作系统会默认在PATH环境变量里面的路径查找我们输入的指令,所以想要不带./运行自己的程序可以将程序所在路径添加进环境变量PATH中。在这里插入图片描述
我们可以随意的修改PATH,因为只要重新登陆,PATH就会恢复。
使用export指令来修改环境变量。
export PATH=路径 这种操作会覆盖原有的内容不建议使用
export PATH=$PATH:路径 建议使用
当修改成功之后,就可以不加./执行自己的程序了。
不过重新登陆后环境变量就会恢复到默认,所以不加./执行自己的程序也只限定在本次登录。

2.环境变量和本地变量的关系

父进程的本地变量不会被子进程继承,但环境变量会被继承下去。

#include <stdio.h>
#include <stdlib.h>#define MY_ENV "myval"int main()
{char* myenv = getenv(MY_ENV);if (myenv == NULL){printf("%s:not found\n", MY_ENV);return 1;}printf("%s=%s\n", MY_ENV, myenv);return 0;
}

在这里插入图片描述
使用set指令可以显示所有本地变量,使用env指令可以显示所有环境变量。
取消本地变量:unset+变量名

在定义环境变量时可以带双引号也可以不带,但如果环境变量带有空格就要加上双引号了。

3.命令行参数表和环境变量表

在main函数中有几个隐藏的参数,平时在做OJ题的时候有可能会注意到。

int main(int argc, char* argv[])

不过我们平时并不使用这些参数,但在系统编程中这些参数比较常用。

int main(int argc, char* argv[])
{int i = 0;for (; i < argc; ++i){printf("argv[%d]->%s\n", i, argv[i]);}return 0;
}

在这里插入图片描述
通过上面的例子可以看出,main函数中的第一个参数argc是命令行中运行程序的时候以空格为分隔符字符串的个数,而argv指针数组中的指针,指向的就是这些字符串。
系统提供的各种指令能够通过带-a、-l等实现不同的功能就是这样实现的

#include <stdio.h>    
#include <unistd.h>    
#include <stdlib.h>    
#include <string.h>    
void Usage(const char* name)    
{    printf("\nUsage: %s -[a|b|c]\n\n", name);                                                                                                              exit(0);    
}    int main(int argc, char* argv[])    
{    if (argc != 2) Usage(argv[0]);    if (strcmp(argv[1], "-a")) printf("打印当前目录下的文件名\n");    else if (strcmp(argv[1], "-b")) printf("打印当前目录下文件的详细信息\n");    else if (strcmp(argv[1], "-c")) printf("打印当前目录下的文件名(包含隐藏文件)\n");    else printf("更多功能待开发\n");    return 0;    
}

利用这两个参数可以实现一个如上所示的进程。

4.子进程获取环境变量的方式

在前面我们知道了可以通过getenv()系统调用可获得环境变量的值。
下面介绍的一种方式,也是通过main函数的参数:char* env[]

int main(int argc, char* argv[], char* env[])
{int i = 0;for (; env[i]; ++i){printf("env[%d]->%s\n", i, env[i]);}return 0;
}

在这里插入图片描述
通过上面这段代码我们打印出了所有的环境变量。

C语言提供了一个第三方的指针变量environ,在调用main函数时,系统就把这个变量传给了main,environ就相当于指针数组env[]的另一个数组名,environ指向的就是env的第一个元素。environ是一个二级指针,下面用environ打印环境变量

int main()
{extern char** environ;int i = 0;for (; environ[i]; ++i){printf("environ[%d]->%s\n", i, environ[i]);}return 0;
}

八、进程地址空间

1.虚拟地址空间的引入

#include <stdio.h>
#include <unistd.h>int global_val = 100;int main()
{pid_t id = fork();if (id == 0){int cnt = 0;while (1){printf("我是子进程, pid:%d, ppid:%d, global_val:%d, &global_val:%p\n", getpid(), getppid(), global_val, &global_val);sleep(1);++cnt;if (cnt == 3){global_val = 300;printf("子进程已经更改了global_val的值了!\n");}}}while (1){printf("我是父进程, pid:%d, ppid:%d, global_val:%d, &global_val:%p\n", getpid(), getppid(), global_val, &global_val);sleep(1);}return 0;
}

在这里插入图片描述
从上面程序的结果来看,一个全局变量在地址并未改变的情况下,竟然出现了不同的值,这是为什么呢?首先一个变量肯定是只能有一个值的,但地址只有一个,这就说明现在内存中应该出现了两个变量。而一个变量在内存中只能有一个地址,那么我们打印出来的一定不是真实的地址,,也就是说现在的内存中有两个全局变量,分别属于父子进程,并且他们都拥有自己的物理内存地址,只是他们共用了一个虚拟地址。之前在学习C和C++时所看到的其实都是虚拟地址,真正的物理地址,用户看不到,统一由操作系统保管。

2.虚拟地址空间布局

在这里插入图片描述
代码段:存放函数体的二进制代码,代码段是只读的,可以防止其他进程恶意修改正在运行的进程的二进制指令,程序的执行就是从代码段中的main函数开始执行,程序运行结束后由操作系统回收。
初始化数据段:用于存储初始化的全局变量、static变量、对象、字符串、数组等常量。但基本类型常量不在这里,它们在代码段。
未初始化数据段:包含所有未初始化的全局变量和static变量,此段中所有变量由0或NULL初始化。
堆区:堆区时向上增长的,程序运行时动态申请的内存空间就是在堆上开辟的,由开发人员手动申请释放,。
映射段:也称共享区,存储动态链接库、以及共享文件等。
栈区:栈时向下增长的,函数的局部变量、返回值、形参等都在栈区上,函数调用时的栈帧就在栈上。

mm_struct就是操作系统描述虚拟空间所构造的结构体。

3.为什么要存在虚拟地址空间?

虚拟内存是计算机系统内存管理的一种技术,它让进程认为它拥有连续可用的内存,实际上它通常被分割成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,需要时才进行数据交换。
进程在和外设IO的过程,所占内存大小为4KB,即4096个连续的物理地址,这部分区域被称为页。
进程无法直接接触物理内存,只能通过虚拟地址依靠页表映射物理地址的方式,来间接访问物理内存。因为页表的存在,进程在访问不属于当前进程的地址时,页表就会拦截进程非法访问地址的请求。

在这里插入图片描述
如上面的例子,当子进程修改global_val时,操作系统会先拷贝这个变量,更改页表映射,最后让子进程对数据进行修改,这样的技术被称为写时拷贝。这样保证了进程的独立性,通过虚拟地址空间和页表,让不同的进程使用同一个虚拟地址,能够映射到不同的物理内存。

相关文章:

【Linux】冯诺依曼体系结构、操作系统、进程概念、进程状态、环境变量、进程地址空间

目录 一、冯诺依曼体系结构二、操作系统(OS)1. 操作系统是什么2. 操作系统如何做管理3. 系统调用和库函数概念 三、进程1. 进程是什么&#xff1f;2. 描述进程-PCB3. 查看进程的方法 四、进程状态1 运行、阻塞和挂起状态2 Linux中的进程状态 五、进程优先级1. 什么是优先级2.查…...

【hive-解决】HiveAccessControlException Permission denied: CREATEFUNCTION

文章目录 一.任务描述二. 解决 一.任务描述 Error while compiling statement: FAILED: HiveAccessControlException Permission denied: Principal [nameroot, typeUSER] does not have following privileges for operation CREATEFUNCTION [ADMIN PRIVILEGE on INPUT, ADMIN…...

内网穿透的应用-通过内网穿透快速搭建公网可访问的Spring Boot接口调试环境

文章目录 前言1. 本地环境搭建1.1 环境参数1.2 搭建springboot服务项目 2. 内网穿透2.1 安装配置cpolar内网穿透2.1.1 windows系统2.1.2 linux系统 2.2 创建隧道映射本地端口2.3 测试公网地址 3. 固定公网地址3.1 保留一个二级子域名3.2 配置二级子域名3.2 测试使用固定公网地址…...

解决 uniapp 开发微信小程序 不能使用本地图片作为背景图 问题

参考博文&#xff1a;uniapp微信小程序无法使用本地静态资源图片(背景图在真机不显示)的解决方法_javascript技巧_脚本之家 问题&#xff1a;uniapp 开发微信小程序&#xff0c;当使用本地图片作为 background-image 时&#xff0c;真机无法显示 解决&#xff1a; 方法一&am…...

常用中间件封装思路粗记

MQ 自定义注解 &#xff0c;编写配置类在bean属性初始化SmartInitializingSingleton#afterSingletonsInstantiated后至处理器 去扫描有自定义注解的bean&#xff0c;去创建对应消费者的容器 并启动消费者容器类主要组件DefaultMQPushConsumer SmartInitializingSingleton#afte…...

探索SPI:深入理解原理、源码与应用场景

文章目录 一、初步认识1、概念2、工作原理3、作用场景 二、源码分析1、ServiceLoader结构2、相关字段3、核心方法 三、案例connector连接器小案例1、新建SPI项目2、创建扩展实现项目1-MongoDB3、创建扩展实现项目2-Oracle4、测试 Spring应用1、创建study工程2、创建forlan-test…...

Web3名词解释

Web3名词解释 以太坊 ERC20 Defi去中心化金融 Defi是Decentralized Finance的英文缩写。 简单理解点就是与传统的高度中心化金融体系相比&#xff0c;去中心化金融是通过区块链技术&#xff0c;比如基于区块链技术开发的手机钱包软件&#xff0c;通过智能合约代码以实现去除…...

Vatee万腾外汇市场新力量:vatee科技决策力

在当今数字化时代&#xff0c;Vatee万腾崭露头角&#xff0c;以其强大的科技决策力进军外汇市场&#xff0c;成为该领域的新力量。这一新动向将不仅塑造外汇市场的未来&#xff0c;也展现Vatee科技决策力在金融领域的引领作用。 Vatee万腾带着先进的科技决策力进入外汇市场&…...

【HarmonyOS开发】配置开发工具DevEco Studio

1、下载 注意&#xff1a; 1、安装过程中&#xff0c;一定要自定义安装位置&#xff0c;包比较大&#xff0c;包比较大&#xff0c;包比较大&#xff01;&#xff01;&#xff01; 2、可以将该工具添加到右键中&#xff0c;否则&#xff0c;如果你的项目不是HarmonyOS&#xff…...

探索亚马逊大语言模型:开启人工智能时代的语言创作新篇章

文章目录 前言一、大语言模型是什么&#xff1f;应用范围 二、Amazon Bedrock总结 前言 想必大家在ChatGPT的突然兴起&#xff0c;大家多多少少都会有各种各样的问题&#xff0c;比如&#xff1a;大语言模型和生成式AI有什么关系呢&#xff1f;大语言模型为什么这么火&#xf…...

zabbix-proxy分布式监控

Zabbix是一款开源的企业级网络监控软件&#xff0c;可以监测服务器、网络设备、应用程序等各种资源的状态和性能指标。在大型环境中&#xff0c;如果只有一个Zabbix Server来监控所有的节点&#xff0c;可能会遇到性能瓶颈和数据处理难题。 为了解决这个问题&#xff0c;Zabbi…...

springboot生成PDF,并且添加水印

/*** 导出调查问卷*/ApiLog("导出调查问卷")PostMapping("/print/{id}")ApiOperationSupport(order 23)ApiOperation(value "导出报告", notes "导出报告")public void print(PathVariable Long id, HttpServletResponse response…...

Tensorflow2.0:CNN、ResNet实现MNIST分类识别

以下仅是个人的学习笔记 &#xff0c;内容可能是错误 CNN&#xff1a; import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers# 导入数据 (x_train, y_train), (x_test, y_test) keras.datasets.mnist.load_data()# 数据预处理 x_tra…...

本地jar导入maven

一、通过dependency引入 1.1. jar包放置&#xff0c;建造lib目录 1.2. pom.xml文件 <dependency><groupId>zip4j</groupId><artifactId>zip4j</artifactId><version>1.3.2</version><!--system&#xff0c;类似provided&#x…...

数据结构与算法【堆】的Java实现

前言 之前已经说过堆的特点了&#xff0c;具体文章在数据结构与算法【队列】的Java实现-CSDN博客。因此直接实现堆的其他功能。 建堆 所谓建堆&#xff0c;就是将一个初始的堆变为大顶堆或是小顶堆。这里以大顶堆为例。展示如何建堆。 找到最后一个非叶子节点从后向前&…...

同创永益联合红帽打造一站式数字韧性解决方案

随着AI技术的快速兴起&#xff0c;IT技术已成为推动业务持续增长的重要驱动力&#xff0c;这要求企业不断尝试新事物&#xff0c;改变固有流程&#xff0c;加强IT技术与业务的合作&#xff0c;同时提升数字韧性能力&#xff0c;以实现业务目标。10月26日&#xff0c;红帽2023中…...

c++ call_once 使用详解

c call_once 使用详解 std::call_once 头文件 #include <mutex>。 函数原型&#xff1a; template<class Callable, class... Args> void call_once(std::once_flag& flag, Callable&& f, Args&&... args);flag&#xff1a;标志对象&#xf…...

【rosrun diagnostic_analysis】报错No module named rospkg | ubuntu 20.04

ubuntu20.04使用指令报错 现象 rosrun diagnostic_analysis export_csv.py my.bag -d ~/Desktop报错 Traceback (most recent call last): File "/opt/ros/noetic/lib/diagnostic_analysis/export_csv.py", line 40, in <module> import roslib; roslib.load_m…...

高防CDN有什么作用?

分布式拒绝服务攻击&#xff08;DDoS攻击&#xff09;是一种针对目标系统的恶意网络攻击行为&#xff0c;DDoS攻击经常会导致被攻击者的业务无法正常访问&#xff0c;也就是所谓的拒绝服务。 常见的DDoS攻击包括以下几类&#xff1a; 网络层攻击&#xff1a;比较典型的攻击类…...

盛元广通开放实训室管理系统2.0

开放实训室管理系统是一种基于网络和数据库的实训室信息管理系统&#xff0c;旨在提高实训室的管理水平&#xff0c;实现实训资源的优化配置和高效利用。该系统通常包括用户管理、设备管理、课程管理、考核管理等功能模块&#xff0c;能够实现实训室的预约、设备借用、课程安排…...

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

Appium+python自动化(十六)- ADB命令

简介 Android 调试桥(adb)是多种用途的工具&#xff0c;该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具&#xff0c;其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利&#xff0c;如安装和调试…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

Linux部署私有文件管理系统MinIO

最近需要用到一个文件管理服务&#xff0c;但是又不想花钱&#xff0c;所以就想着自己搭建一个&#xff0c;刚好我们用的一个开源框架已经集成了MinIO&#xff0c;所以就选了这个 我这边对文件服务性能要求不是太高&#xff0c;单机版就可以 安装非常简单&#xff0c;几个命令就…...

Vue 3 + WebSocket 实战:公司通知实时推送功能详解

&#x1f4e2; Vue 3 WebSocket 实战&#xff1a;公司通知实时推送功能详解 &#x1f4cc; 收藏 点赞 关注&#xff0c;项目中要用到推送功能时就不怕找不到了&#xff01; 实时通知是企业系统中常见的功能&#xff0c;比如&#xff1a;管理员发布通知后&#xff0c;所有用户…...