【Linux】进程篇Ⅰ:进程信息、进程状态、环境变量、进程地址空间

news2024/11/17 3:49:32

文章目录

  • 一、概述
  • 二、查看进程信息
      • 1. 系统文件夹 /proc
      • 2. 用户级工具 ps
      • 3. getpid() 函数:查看进程 PID
      • 4. 用 kill 杀进程
      • 5. 进程优先级
  • 二、进程状态分析
      • 0. +
      • 1. R (running) 运行状态
      • 2. S (sleeping) 休眠状态
      • 3. D (disk sleep) 不可中断的休眠状态
      • 4. T (stopped) 暂停状态
      • 5. t (tracing stop) 追踪暂停状态
      • 6. Z (zombie) 僵尸状态
      • 7. X (dead) 终止状态
      • 8. 孤儿进程
      • 9. 一些概念
  • 三、 环境变量🔺
      • 1. 常见的环境变量
      • 2. 有关指令
      • 3. 通过代码如何获取环境变量
      • 4. 通过系统调用 获取 或 设置 环境变量
      • 5. 补充:命令行的 int argc 和 char *argv[]
  • 四、进程地址空间🔺
      • 1. 如何理解 进程地址空间
      • 2. 为什么要有地址空间?
      • 3. malloc 的本质?
      • 4. 再谈 地址空间


硬件 - - 冯诺依曼计算机
1、CPU 不和外设直接沟通,而是和内存打交道
2、数据层面:外设也只会和内存打交道
软件 - - 操作系统
手段:对下通过管理好软硬件资源
目的:对上给用户提供良好(安全、稳定…)的执行环境
管理的本质:先描述,再组织
管理的实际是数据,用面向对象进行描述,数据结构进行组织。

一、概述

进程 = 内核关于进程的相关数据结构  // task_struct
   +
   当前进程的代码和数据

这个相关的数据结构就是我们通常所说的 PCB(process control block),Linux 下的 PCB 是 task_struct

比如我们输入 ./可执行程序 的时候:数据从磁盘调到内存变成进程

阻塞:就是不被调度。
阻塞一定是因为 当前进程需要等待某种资源就绪
也一定是 进程 task _struct 结构体需要在某种被 OS 管理的资源下排队(queue)。

挂起:操作系统对阻塞的进程,为了腾出内存空间,将进程的代码和数据部分放入磁盘中,直到轮到进程被调度时,在调出代码和数据进入内存。(可以理解成一种特殊的阻塞状态)

二、查看进程信息

1. 系统文件夹 /proc

正在执行的进程,会有一个和进程 PID 同名的文件夹,存在 /proc 目录下,其中存放进程相关信息。

进程消失后,同名文件夹消失。

2. 用户级工具 ps

# 查看全部进程
ps axj 
# 查看某个程序的进程
ps axj | grep [可执行程序]
# 拿 进程表头 && 某个程序的进程
ps axj | head -1 && ps ajx | grep [可执行程序]
# 拿 进程表头 && 某个程序的进程 && 去掉自己 grep 这个进程
ps axj | head -1 && ps ajx | grep [可执行程序] | grep -v grep
# 在上面的基础上,每隔一秒打印一次结果
while :; do ps axj | head -1 && ps ajx | grep [可执行程序] | grep -v grep; sleep 1; echo "----------"; done

3. getpid() 函数:查看进程 PID

函数声明:
pid_t getpid(void); // 查看自身进程 PID
pid_t getppid(void); // 查看父进程 PID
头文件包含:
#include <sys/types.h>
#include <unistd.h>

getpid() :当前程序运行时可以获得 自身进程 PID
getppid() :当前程序运行时可以获得 父进程 PID

pid_t 相当于一个有符号整数,返回的就是 PID 号,也是 /proc 里的文件名


🐎测试代码:

在这里插入图片描述

观察结果如下:

在这里插入图片描述
频繁多次运行发现:子进程每次进入都是新的 PID,父亲的 PPID 一直都是同一个。查看这里的 3395 为例,可知父进程是 bash

在这里插入图片描述

在这里插入图片描述


结论
  1. 🎯bash(命令行解释器) 也是个进程
  1. 🎯命令行启动 的 所有程序,最终都会变成进程,而该进程 对应的 父进程 都是 bash

这里有个生动案例帮助理解:

角色设定:
	bash --> 媒婆
	子进程 --> 媒婆实习生
	
	说,村里阿猫阿狗太多,媒婆为了保护自己的声誉,放出他的实习生说媒,但凡某个实习生谈崩了或者被骗了,总之没处理好这活,坏掉的是这个实习生的声誉,媒婆狂喜...

	同样,bash 放出 子程序,去测你写的代码,如果你的代码有问题,崩的是子程序,保护了 bash...

4. 用 kill 杀进程

除了 ctrl+C,杀进程有专门的命令 kill

方法一:

kill -9 [进程PID]

方法二:

killall [可执行文件]

(如果我们不小心 bash 把他杀了,bash 会崩溃…需要重新连接一下

结论
  1. 🎯如何创建的子进程??
    fork 之后,执行流会变成两个执行流
    fork 之后,谁先运行由调度器决定
    fork 之后,fork 之后的代码共享,通常我们通过 if 和 else if 进行执行流分流

5. 进程优先级

cpu资源分配的先后顺序,就是指进程的优先权(priority)。

优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。

还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能

输入 ps -l 命令可以得到的关键信息有如下内容:

  • UID : 代表执行者的身份
  • PID : 代表这个进程的代号
  • PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
  • PRI :代表这个进程可被执行的优先级,其值越小越早被执行
  • NI :代表这个进程的 nice 值

PRI and NI

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

PRI vs NI

  • 需要强调一点的是,进程的 nice 值不是进程的优先级,他们不是一个概念,但是进程 nice 值会影响到进程的优先级变化。
  • 可以理解 nice 值是进程优先级的修正修正数据

top 命令更改已存在进程的 nice:

  • 进入 top后按 r(renice) –> 输入进程 PID –> 输入 nice 值

二、进程状态分析

task_struct 是一个结构体,内部会包含各种属性,其中就有一项是当前状态。

struct task_struct
{
	int status;
	//...
};	

Linux内核源代码(部分):

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 */
};

0. +

进程状态后面带 + 号
则说明该进程在 前台运行,可以用 ctrl + C 让程序停止。
进程状态后面没有 + 号
该进程在 后台运行,不能用 ctrl + C 让程序停止了。

1. R (running) 运行状态

进程只要是 R 状态,就一定是在CPU上运行吗?
事实上,进程是 R 状态并不直接代表进程在运行,而代表该进程在运行队列中排队,这个队列是由操作系统维护的。

操作系统在内存里,这个队列也在内存里被维护的。操作系统对 task_struct 的管理就是把他们放到不同的队列当中。

进程是什么状态,一般也看这个进程在哪里排队(是 task_struct 在排队,而不是代码和数据)。

运行状态 R 是瞬时状态。当进程会调用资源(如打印到显示器)时,由于 CPU 运行速度太快,我们去 ps axj 进程信息的时候,极大概率只能看到进程的其他状态,而无法捕捉到 R 状态。

2. S (sleeping) 休眠状态

S 休眠状态是 可中断休眠,本质上就是一种 阻塞状态,处于等待某种资源的状态。

3. D (disk sleep) 不可中断的休眠状态

D 是 不可中断休眠,也是阻塞状态的的一种(在做系统管理、运维、系统存储的时候才会遇到)。

面对普通的休眠状态的进程,在特殊场景下,操作系统可以做出判断并杀掉休眠进程。D 状态的休眠,则是操作系统无法杀掉的。只能等进程自己运作,或者拔掉电源…

4. T (stopped) 暂停状态

T 是 暂停状态

用户主动使用 kill -19 操作,可以让进程进入 T 状态:

kill -19 [进程PID]

用户主动关闭 T 状态,使进程变成 R / S(后台运行 / 休眠) 状态,继续运行:

kill -18 [进程PID]

此时进程变成后台运行,无法通过 ctrl + C 的方式结束,需要输入另一个信号 kill -9:

kill -9 [进程PID]

5. t (tracing stop) 追踪暂停状态

追踪暂停,也是暂停的一种。当我们给程序打上断点并在断点处停下时,进程会显示追踪状态。

6. Z (zombie) 僵尸状态

在了解 Z 状态之前,我们先引出一个概念。

main 函数 里的 return 0,实际上是进程退出码。可以交给程序去判断,进程结束的结果是否正确。

// 进程退出码使用举例
int main()
{
	// 算法省略
	int result = 10;
	if(result == 10)
		return 0;	// 正常退出
	else
		return 3;	// 异常退出
}

查看进程退出码:

echo $?

注意:$? 只会保存最后一次执行的退出码。

僵尸状态

子进程退出后,等待后续父进程(OS)读取子进程退出的退出结果的状态。

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

7. X (dead) 终止状态

终止状态,也是一个瞬时状态。当进程从 Z 状态被回收,会变成 X 终止状态,继而操作系统才会正真释放进程的所有资源。

8. 孤儿进程

孤儿进程:父进程退出,子进程会被 OS 自动领养(通过让 1 号进程成为新的父进程)。被领养的进程,就是孤儿进程

9. 一些概念

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

三、 环境变量🔺

环境变量(environment variables) 一般是指在操作系统中用来指定操作系统运行环境的一些参数。

环境变量本质就是一 个 内存级 的一张表,这张表由 用户在登陆系统的时候,进行给特定用户形成属于自己的环境变量表。在系统当中通常具有全局特性,可以被子进程继承

环境变量中的每一个,都有自己的用途:有的是进行路径查找的,有的时进行身份认证的,有的时进行动态库查找的,有的是用来进行确认当前路径…等等。每一个环境变量都有自己的特定应用场景。每一个元素都是 kv 的。

我们平时写代码中生成的可执行文件 xx,在我们需要运行它时输入的 ./xx 实际上就是这个可执行文件的路径。而众多的命令实际也是一个个可执行文件,为什么命令可以直接被读取,而我们生成的可执行文件则要带上路径呢?

分别 which 一下随便某个命令、再 which 我们的可执行文件可以发现。是因为我们的可执行文件不在 PATH 路径下。

两个解决思路,让我们输入可执行文件名 xx,就可以执行程序:
1、把我们生成的可执行文件 cp -rf 到 PATH 的路径下。
2、把可执行文件所在路径 export 到原有路径后面。

1. 常见的环境变量

  • PATH : 指定命令的搜索路径
  • HOME : 指定用户的主工作目录(即用户登陆到 Linux 系统中时,默认的目录)
  • SHELL : 当前 Shell,它的值通常是 /bin/bash。

2. 有关指令

which:在环境变量中查找某个命令的路径

env:输出所有 环境变量

set :同时输出 环境变量 和 本地变量

unset [变量名]:取消某个 本地 / 环境变量

echo $[环境变量名称]:查看某个环境变量

export [变量名]:设置新的 / 更新环境变量
本质上就是,把本地变量添加到环境变量表里!

(注意:如果环境变量被我们误操作不慎覆盖,导致命令无法使用,只需要重启虚拟机即可)

PATH 环境变量举例
------------------

# 添加路径到环境变量
export PATH = $PATH:[指定路径]

# 设置并覆盖原来的环境变量
export PATH = [指定路径]
环境变量
存在 shell 里
放进环境变量表
可以被子进程继承
普通本地变量
存在 shell 里
只能由 shell 内部调用
不能被子进程继承
# 设置新的环境变量,env 中可查,可以被子进程继承
export hello = 123456
# 设置普通的本地变量,env 中没有
hey = abcde

# 查看变量的值
echo $hello
echo $hey
这里要引出一个问题了:
	既然我们说,本地变量只能在 shell 内部使用,不能被子进程继承,
	echo 命令必然会调用子进程,子进程又是怎么访问到本地变量的呢?
这里要用 内建命令 来解释了。后续更新。

3. 通过代码如何获取环境变量

  1. 命令行第三个参数,就是 环境变量表
#include <stdio.h>
int main(int argc, char *argv[], char *env[])
{
	 int i = 0;
	 for(; env[i]; i++)
	 {
	 	printf("%s\n", env[i]);
	 }
	 return 0;
}
  1. 通过第三方变量 environ 获取
    (libc 中定义的全局变量 environ 指向环境变量表,environ 没有包含在任何头文件中,所以在使用时 要用 extern 声明
#include <stdio.h>
int main(int argc, char *argv[])
{
	 extern char **environ;
	 int i = 0;
	 for(; environ[i]; i++)
	 {
		 printf("%s\n", environ[i]);
	 }
	 return 0;
}

其实获取环境变量最主要的是下面这种方式:

4. 通过系统调用 获取 或 设置 环境变量

putenv
getenv

常用getenv和putenv函数来访问特定的环境变量。

🌰我们模拟实现一个pwd

#include <stdio.h>
#include <stdlib.h>

int main()
{
	 char* pwd = getenv("PWD");
	 if(pwd == NULL)
	 	perror("geienv");
	 else
	 	printf("%s\n", pwd);
	 return 0;
}

5. 补充:命令行的 int argc 和 char *argv[]

char *argv[]
命令行输入的

int argc

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") == 0) printf("打印当前目录下的文件名\n");
    else if(strcmp(argv[1], "-b") == 0) printf("打印当前目录下的文件的详细信息\n");
    else if(strcmp(argv[1], "-c") == 0) printf("打印当前目录下的文件名(包含隐藏文件)\n");
    else printf("其他功能,待开发\n");

	return 0;
}

四、进程地址空间🔺

先看如下这个测试:

🐎测试代码:

#include <stdio.h>
#include <unistd.h>
#include <assert.h>
int g_val = 100;
int main()
{
	 pid_t id = fork();
	 assert(id >= 0);
	
	 else if(id == 0)	//child
	 { 		
		while(1)
		{
			printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);  	
			g_val++;
			sleep(1);
		}
	 }
	 else	//parent
	 { 
	 	while(1)
	 	{
			printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
			sleep(1);
		}
	 }
	 
	 return 0;
}

测试结果:

parent[2995]: 100 : 0x80497d8
child[2996]: 100 : 0x80497d8
parent[2995]: 100 : 0x80497d8
child[2996]: 101 : 0x80497d8
parent[2995]: 100 : 0x80497d8
child[2996]: 102 : 0x80497d8
parent[2995]: 100 : 0x80497d8
child[2996]: 103 : 0x80497d8

我们已经知道的是:

  • 子进程对全局数据修改,并不影响父进程。进程具有独立性!

进而可以发现,父子进程,输出地址是一致的,但是变量内容不一样!能得出如下结论:

  • 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量。
  • 但地址值是一样的,说明,该地址绝对不是物理地址!
  • 在 Linux 地址下,这种地址叫做 虚拟地址 / 线性地址
  • 我们在用 C/C++ 语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理。

OS 必须负责将 虚拟地址 转化成 物理地址。

1. 如何理解 进程地址空间

进程地址空间,本质上也是一个内核数据结构,struct mm_struct{};

在这里插入图片描述


2. 为什么要有地址空间?

  • 防止地址随意访问,保护物理内存与其他进程。
  • 地址空间 和 物理内存 之间 由 页表 连接,这张表不止简单的定义了映射关系,还为用户设置了各种区域的不同权限。
  • 例如,我们知道 char* str = “hello”; 如果我们去编译 str = ‘H’; 肯定是不能通过的,因为我们通过 页表 所映射的 “hello” 所存入的物理内存的常量区,在 key 被设置为只读!我们平时说的 代码是只读的,也是这个道理。

3. malloc 的本质?

  • 当我们像 OS 申请内存时,操作系统是立马给我们这个地址,还是需要的时候再给我们?
  • 首先,OS 一般不允许任何的浪费不高效。
  • 其次,申请内存不一定立马使用。
  • 什么意思呢?在我们申请成功之后,使用之前,就有一段小小的时间窗口(这个空间没有被正常使用,但别人用不了),这块空间处于闲置状态?这个不高效的行文,OS 就不允许的!
  • 于是,我们申请成功后,地址空间给我们一个地址,连接页表的 key(此前是 进程管理),而 value 也是没有映射存在的,即此时没有开辟空间。
  • 只有当我们调用或者访问内存,OS 发现没有相应的数据,才会给我们把数据换入。(这里是内存管理
  • 这个现象叫做 缺页中断,也是一个典型的 解耦合

在这里插入图片描述

4. 再谈 地址空间

所以,为什么要有地址空间:

  1. 防止地址随意访问,保护物理内存与其他进程。
  2. 将 进程管理 和 内存管理 进行 解耦合
  3. 更重要的是,可以让进程以统一的视角,看待自己的代码和数据

(扩展)解释第三点:

  • 我们的程序在被编译,还没有被加载到内存的时候,程序内部也存在地址!

  • 编译器编译可执行程序时,本来就是按照 虚拟地址空间 的方式、各种内存布局来编译的,在磁盘上已经给我们规定好了代码区、已初始化数据区…等等这样的概念。编译时只需要进行模块式的加载,进行对应的映射到内存,则有了物理地址。而代码彼此之间是使用虚拟地址互相跳转的。所以说,程序在没加载到内存的时候就有了地址,这个地址是虚拟地址。

  • 比如,我们写一个函数,再去调用它,在反汇编中查看能看到他们的地址,这个地址就是 程序自拟的 虚拟地址。

在这里插入图片描述

  • 可以看到的是,地址空间约束的不光是 OS,我们的编译器也需要遵守这样的规则。
  • 所以一个可执行程序加载到内存,是拥有两套地址的。

最后一个问题:

进程和代码必须一直在内存中?

不一定!实际上,我们拥有了虚拟地址空间,我们的代码是可以边加载边执行的。需要的数据才加载,这样就可以保证我们在内存使用量很低的情况下,还完成了大软件的运行。



🥰如果本文对你有些帮助,请给个赞或收藏,你的支持是对作者大大莫大的鼓励!!(✿◡‿◡) 欢迎评论留言~~


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

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

相关文章

【数据结构】顺序表(SeqList)(增、删、查、改)详解

一、顺序表的概念和结构 1、顺序表的概念&#xff1a; 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下采用数组存储。在数组上完成数据的增删查改。 2、顺序表的结构&#xff1a; &#xff08;1&#xff09;静态顺序表&#xff1a;使…

Redis Cluster 在Spring中遇到的问题

Redis集群配置可能会在运行时更改。可以添加新节点&#xff0c;可以更改特定插槽的主节点。还有可能因为master宕机或网络抖动等原因&#xff0c;引起了主从切换。 无法感知集群槽位变化 SpringBoot2.x 开始默认使用的 Redis 客户端由 Jedis 变成了 Lettuce&#xff0c;但是当…

忽略nan值,沿指定轴计算标准(偏)差numpy.nanstd()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 沿指定轴方向 计算标准(偏)差 numpy.nanstd() [太阳]选择题 import numpy as np a np.array([[1,2],[np.nan,3]]) print("【显示】a ") print(a) print("【执行】np.std(a)&qu…

QT项目代码去UI界面常用开发步骤

QT项目代码去UI界面常用开发步骤 因项目开发需求&#xff0c;领导要求整个QT项目中不要用UI方式来实现界面&#xff0c;这样能保障程序运行稳定性以及代码的逻辑和可读性,先记录具体操作步骤如下&#xff1a; 1、首先我们通过拖控件的方式来实现界面的设计效果&#xff0c…

ARM汇编基本变量的定义和使用

一、ARM汇编中基本变量是什么? 数字变量: GBLA LCLA SETA 逻辑变量:GBLL LCLL SETL 字符串:GBLS LCLS SETLS 注意需要TAB键定义变量和行首改变值 二、使用步骤 1.引入库 代码如下(示例): GBLA led_num Reset_Handler PROCEXPORT Reset_Handler [WEA…

HCIP BGP综合实验

题目 1、AS1存在两个环回&#xff0c;一个地址为192.168.1.0/24该地址不能在任何协议中宣告&#xff1b; 2、AS3中存在两个环回&#xff0c;一个地址为192.168.2.0/24该地址不能在任何协议中宣告&#xff0c;最终要求这两个环回可以互相通讯&#xff1b; 3、AS间的骨干链路I…

Vue3搭建启动

Vue3搭建&启动 一、创建项目二、启动项目三、配置项目1、添加编辑器配置文件2、配置别名3、处理sass/scss4、处理tsx 四、添加Eslint 一、创建项目 npm create vite 1.project-name 输入项目名vue3-vite 2.select a framework 选择框架 3.select a variant 选择语言 二、启…

idea 安装 插件jrebel 报错LS client not configured.

这个报错找了好久&#xff0c;有博主说版本不对&#xff0c;我脑子没反应过来以为是随便换一个低版本的就行&#xff0c;没想到只能是2022.4.1 这个版本才行 一定要用jrebel 2022.4.1的插件版本&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 插件下载地址&…

网络面试合集

传输层的数据结构是什么&#xff1f; 就是在问他的协议格式&#xff1a;UDP&TCP 2.1.1三次握手 通信前&#xff0c;要先建立连接&#xff0c;确保双方都是在线&#xff0c;具有数据收发的能力。 2.1.2四次挥手 通信结束后&#xff0c;会有一个断开连接的过程&#xff0…

❤️创意网页:绚丽粒子雨动画

✨博主&#xff1a;命运之光 &#x1f338;专栏&#xff1a;Python星辰秘典 &#x1f433;专栏&#xff1a;web开发&#xff08;简单好用又好看&#xff09; ❤️专栏&#xff1a;Java经典程序设计 ☀️博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;欢迎踏入…

Codeforces Round 889 (Div. 2) 题解

晚上睡不着就来总结一下叭~&#xff08;OoO&#xff09; 赛后榜(希望不要被Hack...Orz) 终榜&#xff01;&#xff01;&#xff01; 瞬间的辉煌(呜呜呜~) 先不放图了。。怕被dalaoHack...呜呜呜~ 总结 7.29半夜比赛&#xff0c;本来是不想打的&#xff0c;感觉最近做的题太多…

Manjaro KDE 22.1.3vmware无法复制文件

Wayland 是 X11 的现代替代品&#xff0c;几十年来 X11 一直是 Linux 上的默认窗口系统。 Wayland 是一种通信协议&#xff0c;定义 X Window 显示服务器和客户端应用程序之间的消息传递。 软件还不兼容 使用X11即可

JavaScript中的switch语句

switch语句和if语句一样&#xff0c;同样是运用于条件循环中&#xff1b; 下面例子我们用switch实现 例如如果今天是周一就学习HTML&#xff0c;周二学习CSS和JavaScript&#xff0c;周三学习vue&#xff0c;周四&#xff0c;周五学习node.js&#xff0c;周六周日快乐玩耍&…

微服务项目,maven无法加载其他服务依赖

微服务项目&#xff0c;导入了工具类工程&#xff0c;但是一直报错&#xff0c;没有该类&#xff0c; 检查maven 这里的Maven的版本与idea版本不匹配可能是导致依赖加载失败的最重要原因 检查maven配置&#xff0c;我这是原来的maven&#xff0c;home 修改之后,就不报错了

39.密码长度改变图片模糊

密码长度改变图片模糊 html部分 <div class"bg"></div> <div class"container"><h1>Image Password Strength</h1><h3>Change the password to see the effect</h3><div class"email" style&quo…

Mybatis-Flex 比 MyBatis-Plus更轻量,高性能

一、Mybatis-Flex是什么&#xff1f; Mybatis-Flex 是一个优雅的 Mybatis 增强框架&#xff0c;它非常轻量、同时拥有极高的性能与灵活性。我们可以轻松的使用 Mybaits-Flex 链接任何数据库&#xff0c;其内置的 QueryWrapper^亮点 帮助我们极大的减少了 SQL 编写的工作的同时…

MQTT服务器详细介绍:连接物联网的通信枢纽

随着物联网技术的不断发展&#xff0c;MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;协议作为一种轻量级、可靠、灵活的通信协议&#xff0c;被广泛应用于物联网领域。在MQTT系统中&#xff0c;MQTT服务器扮演着重要的角色&#xff0c;作为连接物联网设备和…

MDK5__配色方案的修改

一、必要的知识 与MDK主题相关的文件有两个&#xff0c;在X:\Keil_v5\UV4路径下&#xff1a; global.propglobal.prop.def其中global.prop.def是系统默认的主题配置 如果修改过字体等&#xff0c;系统会生成一个global.prop。 二、修改的步骤 1、打开工程 菜单 Edit 下 Con…

AXI协议之AXILite开发设计(四)—Block Design使用

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 2、AXI interconnect互联组件的使用…

【Linux后端服务器开发】select多路转接IO服务器

目录 一、高级IO 二、fcntl 三、select函数接口 四、select实现多路转接IO服务器 一、高级IO 在介绍五种IO模型之前&#xff0c;我们先讲解一个钓鱼例子。 有一条大河&#xff0c;河里有很多鱼&#xff0c;分布均匀。张三是一个钓鱼新手&#xff0c;他钓鱼的时候很紧张&a…