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

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

文章目录

  • 一、概述
  • 二、查看进程信息
      • 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;	// 正常退出elsereturn 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");elseprintf("%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,我们的编译器也需要遵守这样的规则。
  • 所以一个可执行程序加载到内存,是拥有两套地址的。

最后一个问题:

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

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


🔗下接 进程篇Ⅱ:进程开始、进程终止、进程等待、程序替换


👉🔗链接在此



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


相关文章:

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

文章目录 一、概述二、查看进程信息1. 系统文件夹 /proc2. 用户级工具 ps3. getpid() 函数&#xff1a;查看进程 PID4. 用 kill 杀进程5. 进程优先级 二、进程状态分析0. 1. R (running) 运行状态2. S (sleeping) 休眠状态3. D (disk sleep) 不可中断的休眠状态4. T (stopped) …...

保护 TDengine 查询性能——3.0 如何大幅降低乱序数据干扰?

在时序数据库&#xff08;Time Series Database&#xff09;场景下&#xff0c;乱序数据的定义为&#xff1a;“时间戳&#xff08;timestamp&#xff09;不按照递增顺序到达数据库的数据。”虽然它的定义很简单&#xff0c;但时序数据库需要有相应的处理逻辑来保证数据存储时的…...

状态机实现N位按键消抖

状态机实现N位按键消抖 1、原理 利用状态机实现按键的消抖&#xff0c;具体的原理可参考 (50条消息) 基于FPGA的按键消抖_fpga 按键消抖_辣子鸡味的橘子的博客-CSDN博客 状态机简介&#xff1a; 状态机分类可以主要分为两类&#xff1a;moore和mealy 根据三段式状态机最后…...

uniapp自定义消息语音

需求是后端推送的消息APP要响自定义语音&#xff0c;利用官方插件&#xff0c;总结下整体流程 uniapp后台配置 因为2.0只支持uniapp自己的后台发送消息&#xff0c;所以要自己的后台发送消息只能用1.0 插件地址和代码 插件地址: link let isIos (plus.os.name "iOS&qu…...

k8s安装Jenkins

目录 ​编辑 一、环境准备 1.1 环境说明 二、安装nfs 2.1 安装NFS 2.2 创建NFS共享文件夹 2.3 配置共享文件夹 2.4 使配置生效 2.5 查看所有共享目录 2.6 启动nfs 2.7 其他节点安装nfs-utils 三、创建PVC卷 3.1 创建namespace 3.2 创建nfs 客户端sa授权 3.3 创建…...

共筑开源新长城 龙蜥社区走进开放原子校源行-清华大学站

6 月 28 日&#xff0c;以“聚缘于校&#xff0c;开源共行”为主题的 2023 年开放原子校源行活动在清华大学成功举行。本次活动由开放原子开源基金会和清华大学共同主办&#xff0c;来自各行业的 22 位大咖共聚校园共话开源。龙蜥社区技术专家边子政受邀进行技术分享&#xff0…...

Jgit 工具类 (代码检出、删除分支(本地、远程)、新建分支、切换分支、代码提交)

https://blog.csdn.net/qq_37203082/article/details/120327084 Jgit 工具类 (代码检出、删除分支&#xff08;本地、远程&#xff09;、新建分支、切换分支、代码提交)_jgit删除远程分支_CJ点的博客-CSDN博客 <!--JAVA操作GIT--><dependency><groupId>org.…...

什么是redux?如何在react 项目中使用redux?

redux 概念 redux是一种用于管理JavaScript应用程序的状态管理库。它可以与React、Augular、Vue等前端框架结合使用&#xff0c;但也可以纯在JavaScript应用程序中独立使用。redux遵循单项数据流的原则&#xff0c;通过一个全局的状态树来管理应用程序的状态&#xff0c;从而使…...

mysql的json处理

写在前面 需要注意&#xff0c;5.7以上版本才支持&#xff0c;但如果是生产环境需要使用的话&#xff0c;尽量使用8.0版本&#xff0c;因为8.0版本对json处理做了比较大的性能优化。你你可以使用select version();来查看版本信息。 本文看下MySQL的json处理。在正式开始让我们先…...

前端学习——Vue (Day8)

Vue3 create-vue搭建Vue3项目 注意要使用nodejs16.0版本以上&#xff0c;windows升级node可以西安使用where node查看本地node位置&#xff0c;然后到官网下载msi文件&#xff0c;在本地路径下安装即可 安装完可以使用node -v检查版本信息 项目目录和关键文件 组合式API - s…...

Windows环境下安装及部署Nginx

一、安装Nginx教程 1、官网下载地址&#xff1a;https://nginx.org/en/download.html 2、下载教程&#xff1a;选择Stable version版本下载到本地 3、下载完成后&#xff0c;解压放入本地非中文的文件夹中&#xff1a; 4、启动nginx&#xff1a;双击nginx.exe&#xff0c;若双击…...

使用AOP切面对返回的数据进行脱敏的问题

1.注解类 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/*** Author: xiaoxin* Date: 2023/7/21 17:15*/ Retention(RetentionPolicy.RUNTIME) Targe…...

TDengine时区设置

一般来说&#xff0c;时序数据就是带有时间序列属性的数据。在处理时序数据时&#xff0c;TDengine有着自己独特的方式。但是如果没有正确理解TDengine在写入和查询上的行为&#xff0c;极可能会因为配置了错误的时区&#xff08;timezone&#xff09;&#xff0c;而导致写入和…...

站外引流效果差?一文带你搞懂解海外主流社交媒体算法!

在流量成本越来越高的当下&#xff0c;无论是平台卖家还是独立站卖家都在努力拓展流量渠道。站外引流是推动业务增长的关键策略&#xff0c;很多卖家会把重点放在内容营销上&#xff0c;但其实除了做好内容之前&#xff0c;了解社交媒体的算法才能让营销效果最大化。 01.Faceb…...

css 动画之旋转视差

序&#xff1a;网上看到的一个例子&#xff0c;做一下 效果图&#xff1a; 代码&#xff1a; <style>.content{width: 300px;height: 300px;margin: 139px auto;display: grid;grid-template-columns: repeat(3,1fr);grid-template-rows: repeat(3,1fr);grid-template:…...

maven项目、springboot项目复制文件进来后没反应、不编译解决方法

问题如下 把文件复制进springboot项目后&#xff0c;没反应&#xff0c;不编译。 解决 在maven工具框中选择compile工具&#xff0c;运行即可。...

android jetpack App Startup 应用启动时初始化组件(java)

有什么用&#xff1f; 应用启动时初始化组件。 怎么用 添加依赖 dependencies {implementation "androidx.startup:startup-runtime:1.1.1" }创建类&#xff0c;继承Initializer。 public class AppInit implements Initializer<String> {NonNullOverride…...

【设计模式|行为型】命令模式(Command Pattern)

说明 命令模式&#xff08;Command Pattern&#xff09;是一种行为设计模式&#xff0c;它将请求封装为一个对象&#xff0c;以便在不同的请求者和接收者之间进行解耦、参数化和操作的队列化。命令模式允许你将具体的请求封装为对象&#xff0c;这些对象之间彼此独立&#xff…...

SqlServer 批量删除表

SqlServer 批量删除表 直接上SQL脚本吧 SELECT row_number()over(order by Name) as FID,Name into #temp FROM SysObjects Where XTypeU --类型&#xff0c;U为实体表 and name like TMP% --表名过滤&#xff08;自定义就好&#xff09; ORDER BY Namedeclare count int 0…...

[Linux]线程基本知识

概念 进程 一个正在执行的程序&#xff0c;它是资源分配的最小单位 进程中的事情需要按照一定的顺序逐个进行 进程出现了很多弊端: 一是由于进程是资源拥有者&#xff0c;创建、撤消与切换存在较大的时空开销&#xff0c;因此需要引入轻型进程&#xff1b; 二是由于对称多…...

STM32 串口基础知识学习

串行/并行通信 串行通信&#xff1a;数据逐位按顺序依次传输。 并行通信&#xff1a;数据各位通过多条线同时传输。 对比 传输速率&#xff1a;串行通信较低&#xff0c;并行通信较高。抗干扰能力&#xff1a;串行通信较强&#xff0c;并行通信较弱。通信距离&#xff1a;串…...

页面滚动时隐藏element-ui下拉框/时间弹框

场景 在系统中&#xff0c;当&#xff08;有垂直滚动时&#xff09;点击下拉框后滚动页面&#xff0c;会发现下拉项会遮盖住页面中的元素&#xff0c;不会隐藏 解决&#xff1a;(以vue为例) 在页面滚动或者缩放时隐藏下拉项即可&#xff08;借助点击目标元素&#xff0c;下…...

C#中i++和++i的底层原理

一&#xff1a;前言 我们都知道&#xff0c;i是先取值&#xff0c;后计算。i是先计算&#xff0c;后取值。下面说下它的底层原理 二&#xff1a;原理 int i 0; i; Console.WriteLine(i); 结果是1 执行步骤是&#xff1a; 1.将常量0压入栈中 2.从栈中取出元素0&#xff0c;局…...

在win10下安装verilator

主要参考文章 Verilator简介及其下载安装卸载_徐晓康的博客的博客-CSDN博客https://blog.csdn.net/weixin_42837669/article/details/114505364上面的文章可以解决大部分问题,但是可能是方案有些老了,已经安装最新的版本,下面对最新的版本安装提供解决方案 一 预备工作 安…...

java设计模式-建造者(Builder)设计模式

介绍 Java的建造者&#xff08;Builder&#xff09;设计模式可以将产品的内部表现和产品的构建过程分离开来&#xff0c;这样使用同一个构建过程来构建不同内部表现的产品。 建造者设计模式涉及如下角色&#xff1a; 产品&#xff08;Product&#xff09;角色&#xff1a;被…...

iOS开发-实现获取下载主题配置动态切换主题

iOS开发-实现获取下载主题配置动态切换主题 iOS开发-实现获取下载主题配置更切换主题&#xff0c;主要是通过请求服务端配置的主题配置、下载主题、解压保存到本地。通知界面获取对应的图片及颜色等。 比如新年主题风格&#xff0c;常见的背景显示红色氛围图片、tabbar显示新…...

react经验4:动态组件

什么是动态组件&#xff1f; 在页面的一小块区域切换显示不同的组件 实现方法 1.声明示例组件 //写在component1.tsx中 const Component1()>{return (<div>组件1</div>) } //写在component2.tsx中 const Component2()>{return (<div>组件2</div…...

Java maven的下载解压配置(保姆级教学)

mamen基本概念 Maven项目对象模型(POM)&#xff0c;可以通过一小段描述信息来管理项目的构建&#xff0c;报告和文档的项目管理工具软件。 Maven 除了以程序构建能力为特色之外&#xff0c;还提供高级项目管理工具。由于 Maven 的缺省构建规则有较高的可重用性&#xff0c;所以…...

Java课题笔记~数据库连接池

一、数据库连接池 1.1 数据库连接池简介 数据库连接池是个容器&#xff0c;负责分配、管理数据库连接(Connection) 它允许应用程序重复使用一个现有的数据库连接&#xff0c;而不是再重新建立一个&#xff1b; 释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数…...

设计模式-单例模式

文章目录 单例模式饿汉式单例懒汉式单例懒汉式加锁单例双重锁校验单例静态内部类单例枚举单例 单例模式 单例模式主要是确保一个类在任何情况下都只有一个实例&#xff0c;并提供一个全局访问的点。 主要有以下几种 饿汉式单例 /*** 饿汉式* 类加载到内存后&#xff0c;就实…...