深度理解进程的概念(Linux)

news2024/11/28 11:57:11

目录

一、冯诺依曼体系

二、操作系统(OS)

设计操作系统的目的

核心功能

 系统调用

三、进程的概念与基本操作

简介

查看进程

通过系统调用获取进程标识符 

通过系统调用创建进程——fork()

四、进程的状态 

 操作系统中的运行、阻塞和挂起

 理解linux内核链表

 Linux的进程状态

孤儿进程 

五、进程的优先级

查看进程优先级的命令

优先级的极值

进程的竞争、独立和并发

六、进程切换

Linux真实调度算法:O(1)调度算法 


一、冯诺依曼体系

我们所知的计算机大部分都遵循冯诺依曼体系,他们都是由一个个硬件组成的 

输入单元:包括键盘,鼠标,扫描仪等

中央处理器(CPU):含有运算器和控制器等

输出单元:显示器,打印机

(图1) 

理解:

这里的存储器指的是内存。

不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设。

外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。

CPU在数据层面只和内存打交道,所有的设备都只能和内存打交道。

知识点:

1、 体系结构规定,软件没有运行的时候在外设磁盘里面,运行的时候再把它加载到内存里面来,CPU再从内存读取数据来执行代码,所以数据流动的本质就是从一个设备“拷贝”到另一个设备,体系结构效率的高低由设备的“拷贝”效率决定。

2、为什么数据不直接从输入设备直接到CPU在从CPU直接到输出设备呢?

因为存储分级的时候,不同的设备对应的“拷贝”效率,越靠近CPU,效率就越高,CPU的速率要远远高于IO设备,CPU都把数据处理完了,而IO设备还在写入或者读取数据,所以就会导致整个体系的效率由IO设备决定,那为什么不把整个体系中需要处理数据的设备都安排存储分级更高的寄存器呢?因为成本太高了,冯诺依曼体系结构的历史意义就是在保证一定的效率前提下使成本减低。

(图2)

 3、在数据流层面上理解冯诺依曼体系

如我们用qq发信息的数据流动过程,先把qq加载到内存上,把从键盘上的数据存入到内存中,CPU接收到内存中的数据后,经过计算处理后写回内存,再通过qq流入到输出设备,也就是网卡,通过网络传给另一台计算机的输入设备(网卡),存到内存后通过CPU解码后写回到存储器,再刷新到外设(显示器)上。

二、操作系统(OS)

任何一个计算机体系都包含一个基本的程序集合,称之为操作系统。

包括内核(进程管理,内存管理,文件管理,驱动管理)

及其他程序(例如数据库,shell程序等等)

(图3) 

设计操作系统的目的

体系结构:

(图4) 

对下 ,与硬件交互,管理所有的软硬件资源。

硬件部分根据冯诺依曼体系结构组成,访问不同的硬件就要不同的驱动程序,这些工作就是由操作系统完成。

对上,为用户提供一个良好的执行环境。

 知识点

1、软硬件体系结构都是层状结构。

2、访问操作系统,必须使用系统调用,其实就是由系统提供的函数。

3、我们写的程序只要你判断出它访问了硬件,它必须贯穿整个软硬件体系结构。

4、库在底层封装了系统调用。

核心功能

在整个计算机软硬件架构中,操作系统的定位就是一款纯正的"搞管理"的软件。

管理把它理解为“先描述,再组织”。

描述就是把被管理者的属性罗列出来,放在一个结构体中。

组织就是把结构体用指针或者其他方式连接起来。

操作系统在对软硬件管理的时候其实就是“先描述,再组织”,先把软硬件属性、状态等以结构体的方式罗列出来,再把结构体用队列或者链表等方式组织起来,所以操作系统对软硬件的管理就变成了对队列或者链表的增删改查

 系统调用

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

系统调用在使用上,功能比较基础,对用户的要求相对比较高,所以,有心的开发者可以对部分系统调用进行适度的封装,从而形成库,有了库,就更利于上层用户或者开发者进行二次开发,所以库函数和系统调用是一种上下层的关系

系统调用本质上就是操作系统和用户之间进行某种形式的数据交换,如果让用户直接去访问操作系统的系统调用接口成本高,而且不安全。

其实就是保证在整个环境的安全运行环境下给用户提供服务。

三、进程的概念与基本操作

简介

进程=PCB+自己代码和数据

PCB是存放进程的状态,代码地址,数据地址,id,优先级等信息的,里面包含进程的所有属性,通过PCB提供的属性来对辰戌进行管理,在Linux操作系统中PCB是task_struct.

我们历史上执行的所有指令,工具,自己的程序,运行起来,全部都是进程

查看进程

1、可以通过 /proc系统文件查看,如要获取PID为1的进程信息,ls /proc/1

2、大多数进程可以通过top、ps这些用户级工具来查

补充知识点 

(图5)

exe是进程对应的可执行文件 

cwd是当前工作目录,在使用fopen函数对一个没有创建的文件进行写操作时,可以带路径,也可以不带路径,因为进程启动时会找到cwd,并把新文件创建到cwd下,chdir可以改变cwd

通过系统调用获取进程标识符 

gitpid(),获取进程id

gitppid(),获取父进程id

输入的命令、代码运行起来后它们的父进程都是bash,也就是命令行解释器。

OS会给每一个登录用户分配一个bash。

通过系统调用创建进程——fork()

fork()有两个返回值,进程创建成功,会把子进程的pid返回给父进程,0返回给子进程,失败返回-1给父进程。

默认情况下,父进程与子进程共享代码,在子进程创建成功后子进程会把父进程的pcb拷贝下来,再做与其属性对应的修改

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
	int ret = fork();
	if (ret < 0) {
		perror("fork");
		return 1;
	}
	else if (ret == 0) { //child
		printf("I am child : %d!, ret: %d\n", getpid(), ret);
	}
	else { //father
		printf("I am father : %d!, ret: %d\n", getpid(), ret);
	}
	sleep(1);
	return 0;
}

(代码1) 

运行结果:

(图6) 

 知识点:

对于代码1

1、为什么要给父进程和子进程返回不同的值?

一个父进程可能有多个子进程,父进程要通过子进程的pid对他进行管理,所以要返回子进程的pid给父进程

子进程只有一个父进程,他可以通过ppid获取父进程,所有只需要通过返回值来知道它是否被创建成功即可

2、为什么一个函数函数会返回两次

在执行fork函数时,在执行return语句之前子进程已经被创建好了,他和父进程共享代码,所以父子进程都会执行return语句

3、为什么一个变量既大于0,又等于0,代码1的if 和else同时成立

进程具有独立性,他们的PCB不同,默认指向相同的代码和数据,代码是常量不能修改,在数据修改时不管是父子的哪一方进行数据修改,os都会把对修改的数据在底层拷贝一份,让目标进程修改这个拷贝,来保证进程的独立性,这种形式叫做写时拷贝。

四、进程的状态 

(图7) 

 操作系统中的运行、阻塞和挂起

(图8)

运行:只要进程在调度队列中,它的状态就是running。

阻塞:进程在等待某种设备或者资源就绪(如键盘、网卡、磁盘等),比如程序运行到scanf或者cin这条语句,现在进程就是在等到键盘的反应,而cpu对硬件的管理也会进行结构体描述再组织一个队列,这个结构体中就包含了 “等待队列” 这个成员,在os没有收到键盘的反应时,进程就会从调度队列被链入到等待队列,直到os收到键盘的反应后再重新链入到调度队列。

挂起:如果更多的进程被链入到等待队列导致cpu资源紧张的时候,在磁盘中有一个“swap交换分区”,OS会把阻塞状态的进程的代码和数据唤出到这个分区而PCB不动,此时他就处于阻塞挂起状态等收到资源的反应时再把对应的代码和数据唤入回来;如果是调度队列的太多导致资源紧张,os也会按照进程重要性与“swap交换分区”进行唤入和唤出操作,此时进程就属于运行挂起状态。

进程状态的变换表现之一就是要在不同的队列中进行流动。本质上都是对某种数据结构的增删查改。

 理解linux内核链表

在task_struct里面还存在一个list_head的结构体,

 正是因为有了它,我们的task_struct就可以以这样的方式连接起来:

(图9)

找到首地址的方式(struct task_struct*)(next-&(struct task_struct*)0)。

 task_struct中也可以有多个不同的list_head,所以一个PCB可以隶属于多种数据结构。

(图10)

 Linux的进程状态

进程状态就是task_struct内的一个整数

R状态对应的就是操作系统中运行状态。

S和D状态对应的就是操作系统中阻塞状态,在linux中叫休眠状态。

其中S状态是可中断休眠(浅睡眠)。

D状态是不可中断休眠(深睡眠),比如有一个进程需要在磁盘中写入数据,在写的过程中进程属于S状态,此时内存的资源紧张,os就把这个进程释放掉了,而磁盘恰好又写入失败了,在CPU中控制这个任务的进程不在了,接收不到成功与否的信息的同时数据也不知道如何管理,就有可能会丢失,而D状态的就可以保证这时候的进程不被“杀死”,规避了数据丢失的风险。

T和t是暂停状态输入Linux操作系统的特有状态,当操作系统觉得当前进程有问题或者其他情况,就会把它暂停,在由用户决定这个进程是否继续。

使用gdb时对语句打断点就会使得程序被debug,此时进程就是 “t” 状态;在键盘中输入ctrl+r就进入T状态

X状态(死亡状态)对应操作系统中的结束状态

Z状态(僵尸状态),在Linux操作系统中所有的进程一定是某一个进程的子进程,创建子进程的目的是为了让子进程执行任务的,任务的结果父进程需要知道,子进程的数据可以清除掉,但是他的PCB必须要维持起来,所以在子进程退出之后,父进程获取退出信息之前,子进程的状态就是Z状态

僵尸状态的模拟:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	pid_t id = fork();
	if (id < 0) {
		perror("fork");
		return 1;
	}
	else if (id > 0) { //parent
		printf("parent[%d] is sleeping...\n", getpid());
		sleep(30);
	}
	else {
		printf("child[%d] is begin Z...\n", getpid());
		sleep(5);
		exit(EXIT_SUCCESS);
	}
	return 0;
}

结果:

(图11)

可以看到子进程变成了Z状态,当时他还有PID,所以如果父进程对子进程的状态一直不管 ,信息一直不回收,那么子进程就会一直处于僵尸状态,PCB就一直需要维护,并会造成内存资源的浪费。

slab机制

当一个进程结束后他的PCB选择可以不释放,而是把他放到一个表中存起来,当再有新进程运行起来的时候,就可以再表里面把PCB拿出来,把它初始化后就可以使用了,节省了一定的效率

孤儿进程 

父进程比子进程先退出,子进程就叫做 “孤儿进程”

模拟孤儿进程代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
    pid_t id = fork();
    if (id < 0) {
        perror("fork");
        return 1;
    }
    else if (id == 0) {//child
        printf("I am child, pid : %d\n", getpid());
        sleep(10);
    }
    else {//parent
        printf("I am parent, pid: %d\n", getpid());
        sleep(3);
        exit(0);
    }
    return 0;
}

进程的结果:

可以看到, 当父进程退出后,子进程就被一个PID为1的进程“领养”了,对于1号进程暂时可以把它理解为操作系统,如果不被“领养”的话,子进程进入僵尸状态后就会造成资源浪费,被“领养”后子进程就可以被1号进程回收了,被1号进程“领养”后子进程就会自动变成后台进程,ctrl+c就不能关闭它,用 “kill -9 pid”才可以关闭这个进程。

对于1号进程,在前面的 “进程概念” 中提到,当我们登录Linux操作系统时,操作系统会给用户分配一个bash,这个bash就是1号进程分配的;0号进程也是存在的,它在开机的时候就被1号进程给替换掉了。

五、进程的优先级

优先级是进程得到CPU资源的先后顺序。

优先级也是一种数字,是int类型,在task_struct里面。

值越低,优先级越高,反之优先级越低。

计算机的操作系统是基于时间片的分时操作系统,规定了每个进程的调度时间问题,时间片到了就会把进程从CPU上面剥离下来因为要考虑公平性,所以改变优先级的幅度不能太大。

使用ps -l命令查看优先级的时候能看到UID、PRI和NI等属性

 UID就是用户名,

PRI是进程的优先级,默认值为80,

NI是进程优先级的修正数据,

进程的真实优先级=PRI的默认值(也就是80)+NI

查看进程优先级的命令

进入top后按“r”,再输入进程PID,再输入nice值。

也可以通过系统调用:

优先级的极值

Linux进程的优先级范围是[60,99],

nice值:[-20,19],

基于时间片的分时操作系统,规定了每个进程的调度时间问题,如果优先级设计的不合理就会导致优先级低的进程长时间的得不到CPU资源,进而导致进程饥饿。

进程的竞争、独立和并发

竞争性:系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级

 独立性:多进程运行,需要独享各种资源,多进程运行期间互不干扰并行:多个进程在多个CPU下分别,同时进行运行,这称之为并行

并行:多个进程在多个CPU下分别,同时进行运行,这称之为并行

并发:多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发

六、进程切换

引言

1、死循环进程不会打死系统,它不会一直占有CPU,因为存在时间片,规定时间到了就把进程切换了。

2、一个CPU里有很多寄存器,它就是CPU内部的临时空间,可以用来存放正在运行的进程的临时数据,但寄存器并不等于寄存器里面的数据

(图12) 

进程切换最核心的就是保存和恢复当前进程的硬件上下文的数据,即CPU内存的内容,

当前进程要把自己的进程硬件上下文数据保存起来,就是保存到task_struct里面:

(图13) 

Linux真实调度算法:O(1)调度算法 

(图14)

 解释:

1、一个CPU拥有一个调度队列

2、queue[140]

一个元素就是一个进程队列,相同优先级的进程按照FIFO规则进行排队调度,所以数组下标就是优先级,其中[0,99]下标主要用于实时操作系统,应用于制造业、工业等领域,[100,140]就是我们要了解的,进程在进程队列的下标等于:进程优先级-60+(140-40)

3、bitmap[5]

称之为位图,unsing int bitmap[5],它有32x5个比特位,每个比特位里面的数据1/0表示在queue的队列有没有进程,比如101.......111,就表示queue数组中下标为0的数组里面有数据,下标为1的数组里面没有数据,所以调度器调用进程就变成了,遍历比特位中的数据,再看queue数据,使调度的时间复杂度为O(1)

4、*active和*expired

操作系统具有分时的特性,当一个进程在它的调度时间片用完后,就被按优先级放入到了*expired所指向的队列中,*active队列中所有的进程时间片用完后,在把两个队列进行交换swap(&active,&expired),像这样一直循环这个操作

七、环境变量 

概念

环境变量一般指在操作系统中用来指定操作系统运行环境的一些参数,

如:在编写c/c++代码的时候,链接过程中,我们不知道所链接的动静态库在哪里,但是照样可以链接成功,输出可执行程序,原因就是有相关的环境变量帮助编译器进行查找

环境变量通常具有某些特殊用途,它在系统中通常具有全局特性

 命令行参数

main函数的两个参数argc,argv[ ]作用

(图15)

用上图15的理论来实现自己写的code的不同子功能

//指令选项的原理
//main函数的参数,就是用来实现程序不同子功能的
int main(int argc,char* argv[])
{
	if (argc != 2)
	{
		printf("Usage: %s [-a|-b|-c]", argv[0]);
		return 1;
	}
	const char* arg = argv[1];
	if (strcmp(argv, "-a") == 0)
		printf("这是功能1\n");
	else if (strcmp(argv, "-b") == 0)
		printf("这是功能2\n");
	else if (strcmp(argv, "-c") == 0)
		printf("这是功能3\n");
	else
		printf("Usage: %s [-a|-b|-c]", argv[0]);
	return 0;
}

结果:

 所以得出结论进程会拥有一张命令行表,argv表来实现功能选项。

 5个问题切入环境变量

问题1:为什么我们自己写的程序需要带路径才能执行,./表示当前路径,而系统自带的不用带路径就可以执行?

答:因为系统自带的配置了环境变量

问题2:根据问题1的回答,现在ls这个指令在"/usr/bin"目录下面,那我们把自己写的code也把它拷贝进入"/urs/bin“目录下面是不是就可以和系统的指令一样,不用带路径了呢?
答:是的,但是不建议 

问题3:它为什么会自己去“use/bin”目录下面去查呢?
答:还是因为配置了环境变量:PATH,PATH的作用是告诉系统怎么找文件 

env:查看所有的环境变量;

echo &+环境变量名:仅查看一个环境变量

问题4:那如果我把我的"code”文件的路径加入到这个PASH环境变量里面,是不是也可以达到上面的效果?
答:是的!

方法

问题5:把Xshell关掉,重新进来的时候发现PASH路径又回复原有的样子了,为什么?

答:这要在从存储的角度去理解环境变量。
当我们登录的时候系统给我们创建了一个bash进程,bash必须从系统中读取所有环境变量的
信息,随后在bash进程的内部就形成了一种环境变量表;当我们输入命令时,bash先拿到命令,把命令进行拆分后,放入到命令行参数表,再找需要执行的命令是否存在。
env命令查的就是这张环境变量表,bash进程就有命令行表和环境变量表,
所以要执行一个程序bash要先通过环境变量找到他。

获取环境的方法

操作

export 变量名=变量值,自己导入环境变量,它是一个内建命令,它在执行时不需要创建子进程,由bash自己执行,不会经手子进程

env:查看所有环境变量

echo &变量名:仅查看一个环境变量

unset 变量名:删除环境变量

代码:

1、main函数的参数共有三个,除了“argc”和“argv”,还有一个“char* env[ ]”(环境变量指针数组),这三个参数都是父进程传给main的。

int main(int argc,char* argv[])
{
	//env数组的最后一个元素也是NULL
	for (int i = 0; env[i]; i++)
	{
		printf("env[%d] -> %s\n", i, enc[i]);
	}
	return 0;
}

2、getenv(“变量名”)

环境变量为什么是全局变量可以传给子进程呢?因为它可以让子进程有一些个性化的操作,

比如我想让代码只能由我一个人使用:

int main(int argc, char* argv[])
{
	const char* who = getenv("USER");
	if (who == NULL) return 1;
	if (strcmp(who, "whb") == 0)
	{
		printf("正常执行");
	}
	else
		printf("only whb\n");
	return 0;
}

3、变量environ

//libc中定义的全局变量environ指向环境变量表,
//environ没有包含在任何头⽂件中, 所以在使⽤时 要⽤extern声明。
int main(int argc, char* argv[])
{
	extern char** environ;
	int i = 0;
	for (; environ[i]; i++) {
		printf("%s\n", environ[i]);
	}
	return 0;
}

 八、程序地址空间

引言

学习c语言的时候脑海中会有这样的图:

它就是程序地址空间图,也就是进程地址空间图(虚拟地址空间图),它属于系统层面的概念

其中,字符串是只读的原因就是它被编码成了代码,在代码区只读,

static修饰的变量本质就是全局变量,只是让他只能在某个函数内被访问而已

 那程序地址空间是内存吗?

跟随代码:

int gval = 100;
int main()
{
	pid_t id = fork();
	if (id == 0)
	{
		//子进程创建成功
		while (1)
		{
			printf("子:gval:%d,&gval;%p,pid:%d,ppid:%d\n", gval, &gval, getpid(), getppid());
			sleep(1);
			gval++;
		}
	}
	else
	{
		//父进程
		while (1)
		{
			printf("父:gval:%d,&gval;%p,pid:%d,ppid:%d\n", gval, &gval, getpid(), getppid());
			sleep(1);
		}
	}
} 

结果:

(图16)

结论:父子进程地址一样,如果这个地址是内存地址的话,同一个地址空间怎么会有两个不同的值呢? 所以这个地址一定不是内存地址,它是虚拟地址,所学的c/c++指针用的地址全都是虚拟地址!

 通过一张图了解页表、物理地址和虚拟地址的关系

(图17) 

解释:

一个进程就有一个虚拟地址空间和一套页表。

页表当中存了虚拟地址和对应的物理地址,用户是看不到物理地址的,当用户要访问这块数据时,页表就会通过虚拟地址映射到物理地址。

子进程的PCB、虚拟地址、页表全部都是拷贝父进程的,所以在默认情况下。父进程和子进程的代码和数据都是共享的。

对图16的运行结果,子进程在进行gval++时,他会把物理地址的数据拷贝到一块新空间,在对新空间里面的数据进行修改,并且会更新页表,这叫做写时拷贝。

 虚拟地址空间是什么?

虚拟地址空间本质就是操作系统给进程创建的一个结构体对象,一部分数据如下:

问题:为什么要有虚拟地址空间呢?为什么不直接去物理空间上找,而是要通过虚拟地址映射找呢?

因为:

1、页表当中除了虚拟地址和物理地址之外,还有对应地址的权限,在
地址转化的过程中,也可以对地址的的操作合法性进行判断,进而保护
内存,所以在对字符常量区的数据写入失败时的原因就是,查找页表的
时候权限拦截了

2、将地址从“无序”变为“有序”

物理空间的地址可以不是连续的,因为每一个物理空间地址对应的虚拟空间是有序的

3、节省内存空间,缺页中断,动态加载

4、让进程管理和内存管理进程解耦合

知识点:

1、在创建一个进程时,可以不加载代码和数据,只有task struct,mm struct和页表 

因为当虚拟地址想要通过页表映射找物理地址时,没有发现物理地址时进程就会暂停进行缺页中断们进行补全,如果代码和数据非常大就达到可节省内存空间的目的

2、创建进程先有task struct,mm_struct等,再加载代码和数据

3、进程挂起时,进程的唤出换入swap分区操作时,只把代码和数据换入/唤出,保存PCB,这就是解耦合的作用

4、在mm_struct里面还有一个vm_area_struct,它里面细分了每一段地址空间的起始和结束地址,而mm_struct就是包含整体的栈、堆、代码段等空间的起始地址。

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

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

相关文章

自媒体图文视频自动生成软件|03| 页面和结构介绍

代码获取方式在文本末尾&#x1f51a; *代码获取方式在文本末尾&#x1f51a; *代码获取方式在文本末尾&#x1f51a; *代码获取方式在文本末尾&#x1f51a; 视频图片生成器 一个基于 Python 和 Web 的工具&#xff0c;用于生成带有文字和语音的视频以及图片。支持多种尺寸、…

STM32的一些知识技巧

STM32的一些知识技巧 STM32命名规则 查看代码编译所占用的flash和SRAM的大小 单位为字节&#xff08;Byte&#xff09; 1、使用keil编译结果进行计算 2、查看.map文件 STM32启动模式 主闪存启动地址为0x08000000 查看程序段/函数执行时间 global.prop文件 保存字体配置&…

我们来学mysql -- EXPLAIN之rows(原理篇)

EXPLAIN之rows 题记rows 题记 书接上文《 EXPLAIN之ref》2024美国大选已定&#xff0c;川普剑登上铁王座&#xff0c;在此过程中出谋划策的幕僚很重要&#xff0c;是他们决定了最终的执行计划在《查询成本之索引选择》中提到&#xff0c;explain的输出&#xff0c;就是优化器&…

【AI系统】昇腾 AI 核心单元

昇腾 AI 核心单元 本文将深入介绍昇腾 AI 处理器的核心单元——AI Core&#xff0c;以及其背后的达芬奇架构。昇腾 AI 处理器是华为针对 AI 领域设计的专用处理器&#xff0c;其核心 AI Core 采用了特定域架构&#xff08;Domain Specific Architecture&#xff0c;DSA&#x…

Hadoop生态圈框架部署(九)- Hive部署

文章目录 前言一、Hive部署&#xff08;手动部署&#xff09;下载Hive1. 上传安装包2. 解压Hive安装包2.1 解压2.2 重命名2.3 解决guava冲突 3. 配置Hive3.1 配置Hive环境变量3.2 修改 hive-site.xml 配置文件3.3 配置MySQL驱动包3.3.1 下在MySQL驱动包3.3.2 上传MySQL驱动包3.…

RHCE——SELinux

SELinux 什么是SELinux呢&#xff1f;其实它是【Security-Enhanced Linux】的英文缩写&#xff0c;字母上的意思就是安全强化Linux的意思。 SELinux是由美国国家安全局(NSA)开发的&#xff0c;当初开发的原因是很多企业发现&#xff0c;系统出现问题的原因大部分都在于【内部…

Vue3的通灵之术Teleport

前言 近期Vue3更新了一些新的内容&#xff0c;我都还没有一个一个仔细去看&#xff0c;但是还是有必要去解读一下新内容的。就先从Teleport 开始吧。 官方对 Teleport 的解释是&#xff1a;<Teleport> 是一个内置组件&#xff0c;它可以将一个组件内部的一部分模板“传…

介绍一下atof(arr);(c基础)

hi , I am 36 适合对象c语言初学者 atof(arr)&#xff1b;是返回浮点数(double型)&#xff0c;浮点数数是arr数组中字符中数字 格式 #include<stdio.h> atof(arr); 返回值arr数组中的数 未改变arr数组 #include<stdio.h> //atof(arr) 返 <stdlib> int…

STM32 USART配置库函数

单片机学习&#xff01; 目录 一、USART配置函数 1.1 USART_DeInit函数 1.2 USART_Init函数 1.3 USART_StructInit函数 二、配置同步时钟输出函数 2.1 USART_ClockInit函数 2.2 USART_ClockStructInit函数 三、USART的外设与中断函数 3.1 USART_Cmd函数 3.2 USART_IT…

通俗理解人工智能、机器学习和深度学习的关系

最近几年人工智能成为极其热门的概念和话题&#xff0c;可以说彻底出圈了。但人工智能的概念在1955年就提出来了&#xff0c;可以说非常古老。我在上小学的时候《科学》课本上就有人工智能的概念介绍&#xff0c;至今还有印象&#xff0c;但那些年AI正处于“寒冬”&#xff0c;…

2024数学建模亚太赛【C题】赛题详细解析

目录 &#x1f4d1;一、竞赛时间 &#x1f5dd;️二、奖项设置 ✏️三、选题思路 &#x1f50d;阶段一&#xff1a;【数据预处理与探索性分析】 1.【数据清洗与预处理】 2.【探索性数据分析&#xff08;EDA&#xff09;】 &#x1f50d;阶段二&#xff1a;【时间序列建模…

数据结构 【堆实现】

上文提到堆是一种特殊的二叉树&#xff0c;其中它的父结点均不大于或者不小于其子结点的值。堆总是一棵完全二叉树。其中&#xff0c;堆的父节点全部不小于它的子结点时称为大堆&#xff0c;堆的父结点全部不大于其子结点的堆称为小堆。 堆可以由两种结构来实现&#xff0c;分别…

【AI绘画】Midjourney进阶:色调详解(下)

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: AI绘画 | Midjourney 文章目录 &#x1f4af;前言&#x1f4af;Midjourney中的色彩控制为什么要控制色彩&#xff1f;为什么要在Midjourney中控制色彩&#xff1f; &#x1f4af;色调纯色调灰色调暗色调 &#x1f4af…

[代码随想录Day24打卡] 93.复原IP地址 78.子集 90.子集II

93.复原IP地址 一个合法的IP地址是什么样的&#xff1a; 有3个’.分割得到4个数&#xff0c;每个数第一个数不能是0&#xff0c;不能含有非法字符&#xff0c;不能大于255。 这个是否属于合法IP相当于一个分割问题&#xff0c;把一串字符串分割成4部分&#xff0c;分别判断每…

“harmony”整合不同平台的单细胞数据之旅

其实在Seurat v3官方网站的Vignettes中就曾见过该算法&#xff0c;但并没有太多关注&#xff0c;直到看了北大张泽民团队在2019年10月31日发表于Cell的《Landscap and Dynamics of Single Immune Cells in Hepatocellular Carcinoma》&#xff0c;为了同时整合两类数据&#xf…

贴代码PasteForm框架之框架核心帮助类PasteFormHelper说明

简介 PasteForm是贴代码推出的 “新一代CRUD” &#xff0c;基于ABPvNext&#xff0c;目的是通过对Dto的特性的标注&#xff0c;从而实现管理端的统一UI&#xff0c;借助于配套的PasteBuilder代码生成器&#xff0c;你可以快速的为自己的项目构建后台管理端&#xff01;目前管…

杂7杂8学一点之ZC序列

重要的放在前面&#xff0c;优秀文章链接&#xff1a;5GNR漫谈13&#xff1a;Zadoff –Chu&#xff08;ZC&#xff09;序列性质 目录 1. ZC序列 1.1 ZC序列的表达式 1.2 ZC序列的特点 2. PRACH中的ZC序列 2.1 为什么要有逻辑根序列与物理根序列的概念 1. ZC序列 ZC序列&…

matlab代码--卷积神经网络的手写数字识别

1.cnn介绍 卷积神经网络&#xff08;Convolutional Neural Network, CNN&#xff09;是一种深度学习的算法&#xff0c;在图像和视频识别、图像分类、自然语言处理等领域有着广泛的应用。CNN的基本结构包括输入层、卷积层、池化层&#xff08;Pooling Layer&#xff09;、全连…

【Linux】—简单实现一个shell(myshell)

大家好呀&#xff0c;我是残念&#xff0c;希望在你看完之后&#xff0c;能对你有所帮助&#xff0c;有什么不足请指正&#xff01;共同学习交流哦&#xff01; 本文由&#xff1a;残念ing原创CSDN首发&#xff0c;如需要转载请通知 个人主页&#xff1a;残念ing-CSDN博客&…

基于 Flask 和 RabbitMQ 构建高效消息队列系统:从数据生成到消费

简介 在构建 Web 应用时&#xff0c;处理和传输大量数据是不可避免的。对于需要高效、可扩展的消息处理和异步任务执行的场景&#xff0c;使用 RabbitMQ&#xff08;一种流行的消息队列中间件&#xff09;与 Flask&#xff08;一个轻量级的 Python Web 框架&#xff09;结合&a…