Linux内核中的进程调度-进程调度基础

news2024/9/19 14:41:00

前言

一、进程的概念

1.概述

2.ps和top命令

3.总结

二、进程的生命周期

1.进程状态文字描述

2.进程状态程序中的体现

3.进程状态的切换

三、task_struct数据结构简述

1.数据结构成员简述

2.需要注意的成员:

3.进程优先级 

①、优先级的代码表示

②、Linux内核下的进程分类 

 ③、优先级的在不同类型进程的分配

五、进程系统调用

1.系统调用简述和框图

2.系统调用的代码体现

①fork系统调用代码

②vfork系统调用代码

③clone系统调用代码

3.进程退出

4.内核线程

总结

前言

在深入探讨Linux内核的精妙世界中,进程调度无疑是最具挑战性也最为关键的一个环节。它作为操作系统核心功能的一部分,犹如交响乐团中的指挥家,精准而高效地协调着系统中各个进程的执行顺序和时间分配,确保整个系统的性能表现与资源利用达到最佳平衡。

Linux内核的进程调度器不仅负责决定哪个进程将在CPU上运行,何时运行,还涉及到优先级管理、实时性保证、多处理器调度策略等诸多复杂问题。其设计理念和技术实现,直接影响到操作系统的响应速度、吞吐量以及整体稳定性,尤其对于现代多核、多线程环境下的计算需求来说,更是显得至关重要。

一、进程的概念

1.概述

Linux内核中,进程(Process)是最基本的执行实体,它代表了正在执行的程序的实例。

  • 进程是系统资源分配和调度的基本单位。在Linux内核中,每个进程都有一个独特的进程描述符结构体——task_struct,它包含了进程的身份信息、状态、优先级、虚拟内存、打开的文件描述符、信号处理等众多属性。
  • 进程拥有独立的地址空间,即虚拟内存空间,确保了进程间的隔离性和安全性。

在Linux内核-进程调度学习过程中,需要区分几个比较重要的概念:

轻量级进程:

  • 定义: LWP是一种内核支持的用户线程实现,每一个LWP都对应着内核中的一个实体,也就是说,每个LWP都有自己的内核级线程支持,从而能够独立地被内核调度。LWP结合了用户线程和内核线程的优点,既可以享受到多线程的优势,又能避免传统用户线程的全局阻塞问题。

内核线程:

  • 定义: 内核线程是直接在内核空间运行的线程,它没有独立的用户空间,主要执行内核任务,不与任何特定的用户进程关联。内核线程通常用于执行内核维护工作,如定时器中断处理、I/O调度、垃圾回收等后台服务。
  • 特点: 没有自己的地址空间,所有内核线程共享内核地址空间,可以直接访问硬件资源,但不能执行用户态的代码。

用户进程和用户线程:

  • 用户进程:用户进程是运行在用户空间的应用程序实例,它拥有独立的地址空间、打开的文件描述符集合以及其他系统资源。一个用户进程可以包含一个或多个线程(无论是内核线程还是用户线程/LWP)。
  • 用户线程:用户线程是在用户空间创建和管理的线程,存在于进程的地址空间内部。用户线程由进程自己或用户空间的线程库(如POSIX Pthreads)创建和调度,而非由操作系统内核直接管理。 用户线程依赖于用户态的线程库实现上下文切换,速度相对较慢。早期的用户线程在没有内核支持的情况下,如果其中一个线程阻塞在系统调用上,会导致整个进程阻塞。

轻量级进程和用户线程的关系:

        在Linux系统中,当你使用用户层的线程库(如POSIX Pthreads)创建用户线程时,大多数情况下(特别是使用Native POSIX Thread Library,NPTL时),操作系统会在内核层面对应地创建一个内核线程。NPTL实现了用户线程与内核线程的1:1映射关系,意味着每个用户线程都有一个与之紧密耦合的内核线程。

内核通过维护内核线程来确保线程的调度、上下文切换、系统调用响应等功能。这样一来,当用户线程执行系统调用或发生阻塞时,内核能够透明地调度另一个线程继续执行,避免了用户级线程模型可能导致的整个进程被阻塞的问题。此外,由于内核直接参与调度,还能保证线程在多处理器环境下的公平性和高效性。

注意:在Linux环境下,为了统一和简化表述,现代Linux内核并不区分内核进程和用户进程,一般所说的进程均包含了内核层面的支持,并且Linux内核支持的线程模型多数情况下是指LWP,即每个用户级线程背后都有一个对应的内核线程作为支撑。而在Linux内核视角看,所有的执行实体都被视为进程(task),无论是否执行用户代码还是内核代码,这被称为"一切皆进程"的哲学。因此,所谓的“用户线程”在Linux中表现为具有独立调度实体的LWP。而线程组的概念更多出现在高级编程接口或者性能测试工具中,而不是内核核心概念。

2.进程查看命令

①、ps (Process Status): ps命令是Linux及类Unix系统中最基础的进程查看工具之一,它提供了当前系统中进程状态的一次性快照视图。通过不同的选项,您可以获取到不同级别的进程信息。以下是一些常用选项及其作用:

  • -e 或 --every:显示系统中所有的进程。
  • -f 或 --full:提供完整的格式输出,包括进程树状关系和环境变量等额外信息。
  • -l 或 --long:长格式输出,包含更多详细信息,如F旗表示进程正在等待文件锁。
  • -u 或 --user:按照用户来显示进程,并显示每个进程的CPU和内存使用情况。
  • -aux 是一个常见的组合选项,用于显示系统中所有用户的全部进程,包括后台进程(不与终端关联的进程)。

例如,ps -ef 将显示出当前系统中所有进程的详细信息,包括PID(进程ID)、PPID(父进程ID)、TTY(终端类型)、CWD(当前工作目录)、CMD(启动命令)等字段。

②、top: 相比之下,top命令则提供了一个动态实时的视图,它可以持续不断地刷新并显示当前系统中各进程的资源使用情况。启动top命令后,您将看到一个全屏界面,其中包括:

  • 进程列表:按照默认排序(通常是CPU使用率或优先级)列出正在运行的进程及其相关信息,如PID、USER(执行进程的用户)、PR(优先级)、NI(nice值,影响优先级)、VIRT(虚拟内存大小)、RES(常驻内存大小)、%CPU和%MEM(CPU和内存使用百分比)等。
  • 系统总体状态:包括系统运行时间、登录用户数、系统负载、CPU和内存的整体使用状况等统计数据。
  • 交互式操作:在top运行过程中,用户可以通过键盘输入相应的命令(如按P键切换到按CPU使用率排序,按M键切换到按内存使用率排序,或使用k键杀死指定进程等)来进行进一步的进程管理和监控。

总结起来,ps命令更适合一次性快速查看特定进程或系统某一时刻的进程状态,而top命令则是实时监控和管理系统性能的理想工具,尤其是在需要跟踪和调整进程资源占用时更为实用。

3.总结

进程的几个要素:

  • 有一段程序待其执行
  • 有进程专用的系统堆栈空间
  • 在内核有task_struct结构体
  • 进程有独立的存储空间,拥有专用的用户空间

如果具备前面三条而缺少第4条就可以称为线程“”,如果完全没有用户空间,就称为“内核线程 ”,如果共享用户空间就称为“用户线程” 。

二、进程的生命周期

1.进程状态文字描述


         Linux操作系统属于多任务操作系统,系统中的每个进程能够分时复用CPU时间片,通过有效的进程调度策略实现多任务并行执行。而进程在被CPU调度运行,等待CPU资源分配以及等待外部事件时会属于不同的状态。进程状态如下:

        创建状态:新进程刚刚被创建,尚未开始执行。

        就绪状态:进程已准备好所有必需资源,等待CPU分配时间片执行。

        执行状态:进程已获得CPU资源并在其中运行。

        阻塞状态:进程因等待某个资源或事件而暂时停止运行,从CPU队列中移除。

        终止状态:进程已完成执行或被终止,不再存在。

2.进程状态程序中的体现

#define TASK_RUNNING			0x00000000
#define TASK_INTERRUPTIBLE		0x00000001
#define TASK_UNINTERRUPTIBLE	0x00000002
#define __TASK_STOPPED			0x00000004
#define __TASK_TRACED			0x00000008
  • TASK_RUNNING 表示进程处于可运行状态。这意味着进程已经准备好在CPU上执行,并且调度器可以选择它来进行运行。当进程获取到CPU时间片时,它就会进入运行状态。
  • TASK_INTERRUPTIBLE 表示进程处于可中断睡眠状态。这种状态下,进程正在等待某个事件发生(例如I/O操作完成、锁可用等),并且如果收到信号或者等待的条件满足,它可以被唤醒并重新加入到可运行队列中。在可中断睡眠期间,进程可以响应信号并改变其状态。
  • TASK_UNINTERRUPTIBLE 表示进程处于不可中断睡眠状态。类似可中断睡眠,进程同样在等待某种资源或事件,但是在此状态下,进程不会响应任何信号,即使接收到信号也不会立即醒来,除非等待的资源变为可用或特定条件达成。
  • __TASK_STOPPED 标志意味着进程已停止执行,通常是因为收到了SIGSTOP或SIGTSTP这样的停止信号,或者是调试器暂停了进程。停止的进程不会消耗CPU资源,直到收到SIGCONT信号恢复执行。
  • __TASK_TRACED 表示进程正在被调试器或其他跟踪工具追踪,并进入了跟踪停止状态。在这种状态下,进程同样不会执行,等待调试器的进一步操作,比如单步执行、继续执行等。

这些状态标志会被组合在一个进程控制块(PCB,在Linux内核中表现为task_struct结构体的一个成员变量state)中,以表示进程的当前状态。调度器根据这些状态决定何时何地将进程投入运行或从运行状态移除。在实际的内核源码中,为了准确反映进程状态,这些宏可能会与其他标志位一起使用或组合起来形成更复杂的状态标识。

3.进程状态的切换

如下图,便是进程进行状态之间的切换,这些工作都是有调度器来完成的。

三、task_struct数据结构简述

1.数据结构成员简述

        进程是操作系统调度的一个实体,需要对进程所必须资源做一个抽象化,此抽象化为进程控制块 (PCB,Process Control BLock) ,PCB在Linux内核里面采用task_struct结构体来描述进程控制块。Linux内核涉及进程和程序的所有算法都围绕名为task_struct的数据结构而建立操作。具体Linux内核源码task_struct结构体核心成员如下(task_struct结构体过于庞大,暂时了解几个重要成员)task_struct定义在include\linux\sched.h:

  • __state:表示当前进程状态,例如可运行、睡眠、僵死等。
  • stack:指向进程的内核栈。
  • usage:引用计数,用于跟踪进程使用情况。
  • priostatic_prionormal_prio:描述进程的调度优先级和策略。
  • sertdl:分别对应CFS(完全公平调度器)、实时调度和Deadline调度的调度实体。
  • mm:指向进程的内存描述符结构(mm_struct),管理进程的虚拟内存。
  • active_mm:在没有独立内存空间时,指向当前活动的内存描述符。
  • exit_stateexit_codeexit_signal:进程退出时的状态、退出码和发送给父进程的信号。
  • pidtgid:分别代表进程ID和线程组ID。
  • real_parentparentchildrensibling:用于构建进程间的父子、兄弟关系,形成进程树。
  • files:指向进程打开的文件表,即files_struct结构体,记录所有已打开的文件描述符。
  • signalsighand:管理和处理进程接收到的信号。
  • blockedreal_blockedsaved_sigmask:记录进程当前屏蔽的信号集合。
  • nsproxy:命名空间代理,用于管理和切换不同命名空间。
  • fs:指向文件系统信息结构,记录进程的当前工作目录、根目录等文件系统相关信息。
  • 其他字段还包括了进程的调度统计信息、时间统计、内存页面错误统计、POSIX定时器、安全特性、审计信息等。

2.需要注意的成员:

 内存块指针,特殊的是对于内核线程而言的mm是空指针,active_mm是内核线程在运行的时候向进程借用的地址空间:

struct mm_struct		*mm;
struct mm_struct		*active_mm;

3.进程优先级 

①、优先级的代码表示

描述进程的调度优先级和策略,之后的任务调度以及时间片分配都要用到优先级:

	int				prio;
	int				static_prio;
	int				normal_prio;
	unsigned int	rt_priority;
  • int prio: 这个字段代表进程的动态优先级,它是根据进程的行为和系统负载动态调整的。在传统的Linux调度器(如CFS调度器)中,这个优先级通常被映射到调度实体(sched_entity)的一个虚拟运行时间(vruntime),而不是一个直观意义上的数字大小,较大的vruntime意味着较低的优先级。

  • int static_prio: 静态优先级,也称为nice值,在Linux中范围是-20至19,数值越小表示优先级越高。静态优先级可以通过nice值或者用户权限改变,但不会像动态优先级那样频繁变化。

  • int normal_prio: 此字段在某些Linux调度器实现中可能用来表示经过nice值调整后的正常优先级,它结合了静态优先级和可能的额外优先级调整因素。

  • unsigned int rt_priority: 实时优先级,仅适用于实时调度策略(如SCHED_FIFO或SCHED_RR)。实时进程有固定的优先级分配,rt_priority值越大,表示进程的实时优先级越高,抢占其他进程的可能性也就越大。实时进程一般不受nice值的影响,其优先级高于普通进程。在实时调度策略下,rt_priority用于确定进程在实时进程队列中的相对位置。

②、Linux内核下的进程分类 

在Linux内核中,进程可以按照其调度需求和优先级的不同分为不同的类别,主要包括:

  • 普通进程(Normal Process)

    • 又称为分时进程,这类进程在Linux系统中遵循默认的分时调度策略,如CFS(Completely Fair Scheduler)。它们按照各自权重(nice值)和虚拟运行时间(vruntime)来获取CPU时间片。nice值可以在[-20, 19]范围内调整,数值越小,优先级越高,但总体来说,普通进程之间是公平共享CPU资源的。
  • 实时进程(Real-time Process)

    • 实时进程在满足特定条件的情况下需要得到及时响应,具有更高的优先级。Linux内核提供两种实时调度策略:SCHED_FIFO(先进先出)和SCHED_RR(轮转调度)。
      • SCHED_FIFO:实时进程中,优先级高的进程总是优先执行,一旦开始运行,除非进程主动放弃CPU(如阻塞等待I/O或睡眠),否则不会被优先级相同或更低的其他进程抢占。
      • SCHED_RR:同样是实时进程,但它在用完时间片后会重新加入队列等待下一次调度,这样可以保证在相同优先级的实时进程中实现时间片轮转。
  • 限期进程(Deadline Process)

    • 在一些文献和系统中,也可能提到限期进程这一概念,它指的是那些具有严格截止时间要求的任务,必须在规定时间内完成。在Linux内核的标准调度器中并没有直接的限期调度策略,但在实时扩展(如PREEMPT_RT补丁集)的支持下,可以通过特殊的实时调度策略或者其他方法模拟实现这种功能。实际应用中,这种类型的进程通常归入实时进程范畴,通过设定合适的实时优先级并配合调度算法确保其能够在截止时间前完成计算。

 ③、优先级的在不同类型进程的分配

  • 限期进程的优先级是-1;
  • 实时进程的优先级1-99,优先级数值最小,表示优先级越高;
  • 普通进程的静态优先级为: 100-139,优先级数值越小,表示优先级越高,可通过修改nice值改变普通进程的优先级,优先级等于120加 上nice值;

限期进程的优先级比实时进程要高,实时进程的优先级比普通进程要高

下表就是描述了不同进程对应的优先级成员的变化:

五、进程系统调用

进程的系统调用定义在kernel/fork.c文件里面

1.系统调用简述和框图

当运行应用程序的时候,调用fork()/vfork()/clone()函数就是系统调用。系统调用就是应用程序如何进入内核空间执行任务,程序使用系统调用执行一系列的操作: 比如创建进程、文件IO等等。

系统调用框图(使用Linux版本为6.1的内核,不同的内核其系统调用有点差异) 如下所示:

2.系统调用的代码体现

①、fork系统调用代码

#ifdef __ARCH_WANT_SYS_FORK
SYSCALL_DEFINE0(fork)
{
#ifdef CONFIG_MMU
	struct kernel_clone_args args = {
		.exit_signal = SIGCHLD,
	};

	return kernel_clone(&args);
#else
	/* can not support in nommu mode */
	return -EINVAL;
#endif
}

②、vfork系统调用代码

#ifdef __ARCH_WANT_SYS_VFORK
SYSCALL_DEFINE0(vfork)
{
	struct kernel_clone_args args = {
		.flags		= CLONE_VFORK | CLONE_VM,
		.exit_signal	= SIGCHLD,
	};

	return kernel_clone(&args);
}
#endif

③、clone系统调用代码

#ifdef __ARCH_WANT_SYS_CLONE
#ifdef CONFIG_CLONE_BACKWARDS
SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
		 int __user *, parent_tidptr,
		 unsigned long, tls,
		 int __user *, child_tidptr)
#elif defined(CONFIG_CLONE_BACKWARDS2)
SYSCALL_DEFINE5(clone, unsigned long, newsp, unsigned long, clone_flags,
		 int __user *, parent_tidptr,
		 int __user *, child_tidptr,
		 unsigned long, tls)
#elif defined(CONFIG_CLONE_BACKWARDS3)
SYSCALL_DEFINE6(clone, unsigned long, clone_flags, unsigned long, newsp,
		int, stack_size,
		int __user *, parent_tidptr,
		int __user *, child_tidptr,
		unsigned long, tls)
#else
SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
		 int __user *, parent_tidptr,
		 int __user *, child_tidptr,
		 unsigned long, tls)
#endif
{
	struct kernel_clone_args args = {
		.flags		= (lower_32_bits(clone_flags) & ~CSIGNAL),
		.pidfd		= parent_tidptr,
		.child_tid	= child_tidptr,
		.parent_tid	= parent_tidptr,
		.exit_signal	= (lower_32_bits(clone_flags) & CSIGNAL),
		.stack		= newsp,
		.tls		= tls,
	};

	return kernel_clone(&args);
}
#endif

3.进程退出

①、进程主动终止: 从main()函数返回,链接程序会自动添加到exit()系统调用; 

exit系统调用在内核定义如下\kernel\exit.c

SYSCALL_DEFINE1(exit, int, error_code)
{
	do_exit((error_code&0xff)<<8);
}

②、进程被动终止: 进程收到一个自己不能处理的信号;进程收到 SIGKILL等终止信息。

4.内核线程

定义:它是独立运行在内核空间的进程,与普通用户进程区别在于内核线程没有独立的进程地址空间。task_struct数据结构里面有一个成员指针mm设置为NULL,它只能运行在内核空间。

内核创建一个内核线程代码体现如下:

/*
 * Create a kernel thread.
 */
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
	struct kernel_clone_args args = {
		.flags		= ((lower_32_bits(flags) | CLONE_VM |
				    CLONE_UNTRACED) & ~CSIGNAL),
		.exit_signal	= (lower_32_bits(flags) & CSIGNAL),
		.fn		= fn,
		.fn_arg		= arg,
		.kthread	= 1,
	};

	return kernel_clone(&args);
}

总结

本文首先从进程的基本概念出发,深入剖析了进程所经历的各种状态及其转换机制。接下来,文章重点阐述了Linux内核中用于表示进程核心数据结构的task_struct,其中着重强调了优先级设定在进程调度中的重要作用。此外,文中还简明扼要地介绍了通过fork系统调用创建新进程的过程,以及通过exit系统调用结束进程的操作。最后,文章提及了内核线程这一特殊类型的进程,并指出其在内核任务调度中的地位。

整体来看,这篇文章为后续深入学习进程调度奠定了坚实基础,体现了循序渐进的学习原则,鼓励读者在理解内核复杂性时要有耐心,逐步积累,最终会达到快速掌握知识的目标。

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

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

相关文章

蓝桥杯2023真题-幸运数字

目录 进制转换&#xff1a; 思路 代码 题目链接&#xff1a; 0幸运数字 - 蓝桥云课 (lanqiao.cn) 本题就考的进制转换问题&#xff0c;要将十进制5转换成二进制&#xff0c;通过%2,和/2的交替使用即可完成&#xff0c;所得余数就是转换成的二进制各位的值&#xff0c;转换…

浅谈如何自我实现一个消息队列服务器(3)—— 细节分析

文章目录 2.2 消息存储在文件时涉及到的流对象2.3 序列化、反序列化的方法2.3.1 JSON的ObjectMapper2.3.2 ObjectOutputStream 、 ObjectInputStream2.3.3 第三方库的Hessian2.3.4 protobuffer2.3.5 thrift 2.4 使用类MessageFileManager封装文件存储操作2.4.1 sendMessage()实…

ubuntu20.04云服务器安装LXDE轻量级桌面和XRDP远程连接工具

云服务器一般都是安装命令行系统&#xff0c;用SSH连接&#xff0c;但是有时我们需要桌面来做更好的管理。 首先我们明确一下需要的东西。 一个桌面系统&#xff1a;LXDE&#xff08;最轻量级桌面&#xff09;&#xff0c;为了节省资源&#xff0c;我们只要功能够用就行。一个…

[套路] 浏览器引入Vue.js场景-WangEditor富文本编辑器的使用 (永久免费)

系列文章目录 [套路] el-table 多选属性实现单选效果[套路] 基于服务内存实现的中文拼音混合查询[套路] Bypass滑块验证码 目录 系列文章目录前言一、实现1.1 场景1.2 Window对象简介1.3 引入WangEditor1.4 页面配置 前言 公司使用freemarker的老旧SpringBootWeb后台项目, 前…

【蓝桥杯】填空题技巧|巧用编译器|用Python处理大数和字符|心算手数|思维题

目录 一、填空题 1.巧用编译器 2.巧用Excel 3. 用Python处理大数 4.用Python处理字符 5.心算手数 二、思维题 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击跳转到网站】 一、填空题 …

Python编程入门:环境搭建与基础语法

目录 1. 引言 2. Python环境搭建 3. Python基础语法 3.1. 变量与数据类型 3.2. 运算符与表达式 3.3. 控制结构&#xff1a;条件语句与循环 3.4. 函数定义与使用 3.5. 输入与输出 3.6. 列表操作 4. 总结 1. 引言 Python作为一种简洁易学、功能强大的编程语言&#xff…

hadoop伪分布式环境启动时web端访问不到

在搭建hadoop伪分布式环境时&#xff0c;开启hdfs-site.sh后&#xff0c;web端访问不到&#xff0c;但是节点已经正常开启&#xff1a; 在尝试关闭防火墙后也没有效果&#xff0c;后来在/etc/hosts文件中加入本机的ip和主机名映射后&#xff0c;重新初始化namenode&#xff0c;…

电脑桌面记事本便签软件,记事本软件哪个好用

正在电脑前忙碌工作&#xff0c;突然想起今晚有个重要的会议&#xff0c;或者是明天有一个重要的任务需要完成&#xff0c;但是手头的工作又无法让你离开电脑&#xff0c;这时候&#xff0c;你多么希望有一个便捷的电脑桌面记事本便签软件&#xff0c;可以让你快速记录下这些重…

2016年认证杯SPSSPRO杯数学建模D题(第二阶段)NBA是否有必要设立四分线全过程文档及程序

2016年认证杯SPSSPRO杯数学建模 D题 NBA是否有必要设立四分线 原题再现&#xff1a; NBA 联盟从 1946 年成立到今天&#xff0c;一路上经历过无数次规则上的变迁。有顺应民意、皆大欢喜的&#xff0c;比如 1973 年在技术统计中增加了抢断和盖帽数据&#xff1b;有应运而生、力…

软件测试/测试开发丨Docker环境安装配置(Mac、Windows、Ubuntu)

macOS 安装 Docker brew cask install docker运行 Docker Ubuntu 安装 Docker # 更新 apt update # 安装依赖 apt install apt-transport-https ca-certificates curl software-properties-common -y # 添加 key curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/…

python 读取jpg图片

pillow读取图片 from PIL import Image import numpy as np img_path ./Training/meningioma/M546.jpg # 读取图片 image Image.open(img_path) width, height image.size print("图片的宽度为{},高度为{}".format(width,height)) print("图片的mode为{}&qu…

如何做到无感刷新Token?

为什么需要无感刷新Token&#xff1f; 自动刷新token 前端token续约 疑问及思考 图片 为什么需要无感刷新Token&#xff1f; 「最近浏览到一个文章里面的提问&#xff0c;是这样的&#xff1a;」 当我在系统页面上做业务操作的时候会出现突然闪退的情况&#xff0c;然后跳转…

vulhub打靶记录——cybox

文章目录 主机发现端口扫描web渗透nikto扫描目录扫描 提权 主机发现 使用nmap扫描局域网内存活的主机&#xff0c;命令如下&#xff1a; nmap -sP 192.168.56.0/24192.168.56.1&#xff1a;主机IP&#xff1b;192.168.56.100&#xff1a;DHCP服务器IP&#xff1b;192.168.56.…

通科技新品亮相:4K60编解一体,USB透传无忧

在信息化快速发展的今天&#xff0c;音视频技术的需求与应用场景日益丰富&#xff0c;特别是在对视频画质和实时性要求极高的领域中&#xff0c;如军警、公安、金融等&#xff0c;对音视频处理设备的性能要求更为严格。为满足这些高端应用场景的需求&#xff0c;视通科技紧跟时…

2024年【道路运输企业安全生产管理人员】考试及道路运输企业安全生产管理人员考试技巧

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 道路运输企业安全生产管理人员考试是安全生产模拟考试一点通总题库中生成的一套道路运输企业安全生产管理人员考试技巧&#xff0c;安全生产模拟考试一点通上道路运输企业安全生产管理人员作业手机同步练习。2024年【…

【PLC】PROFIBUS(一):介绍

1、简介 PROFIBUS (Process Fieldbus)&#xff0c;德国SIEMENS和其它机构联合开发&#xff1b; 1999年&#xff0c;PROFIBUS成为国际工业现场总线协议标准IEC61158的组成部分&#xff1b; PROFIBUS 由三部分组成&#xff1a;PROFIBUS-DP、PROFIBUS-PA 和 PROFIBUS-FMS&#xf…

聚类分析|基于层次的聚类方法及其Python实现

聚类分析|基于层次的聚类方法及其Python实现 0. 基于层次的聚类方法1. 簇间距离度量方法1.1 最小距离1.2 最大距离1.3 平均距离1.4 中心法1.5 离差平方和 2. 基于层次的聚类算法2.1 凝聚&#xff08;Agglomerative&#xff09;2.3 分裂&#xff08;Divisive&#xff09; 3. 基于…

力扣56. 合并区间

Problem: 56. 合并区间 文章目录 题目描述思路及解法复杂度Code 题目描述 思路及解法 1.将数组按内部的一维数组的第一项按从小到大的顺序排序&#xff1b; 2.创建二维结果数组merged&#xff0c;并将排序后的数组中的第一个一维度数组存入到merged中&#xff1b; 3.从后面的一…

【C语言】【Leetcode】70. 爬楼梯

文章目录 题目思路&#xff1a;简单递归 > 动态规划 题目 链接: link 思路&#xff1a;简单递归 > 动态规划 这题类似于斐波那契数列的算法&#xff0c;结果其实就是到达前一步和到达前两步的方法之和&#xff0c;一直递归到n1和n2时就行了&#xff0c;但是这种算法有个…

STM32的CAN通信中,如何通过软件过滤来提高通信效率?

在STM32的CAN通信中&#xff0c;通过软件过滤可以有效地提高通信效率&#xff0c;减少不必要的数据处理&#xff0c;从而减轻CPU的负担并提高系统的响应速度。软件过滤通常是在硬件过滤的基础上进行的&#xff0c;用于进一步筛选特定的CAN消息。以下是如何通过软件过滤来提高ST…