LInux系统编程(二)操作系统和进程

目录

一、前言:冯诺依曼体系结构

1、图中各个单元的介绍

2、值得注意的几点

二、操作系统

1、操作系统分层图

2、小总结

三、 进程(重点)

1、进程的基本概念

2、存放进程信息的数据结构——PCB(Linux 下称作 task_struct)

2.1  task_struct 的内容分类

 3、在终端中通过输入命令查看进程信息

4、父子进程

4.1 1号进程

4.2 shell 进程与用户程序

5、通过系统调用来创建进程与获取进程标识符(pid) 

5.1  创建子进程的系统调用接口:fork()的初识(这里浅尝辄止,后面章节会详细介绍)

5.2 获取父进程的ppid和自己进程的pid

四、进程的状态

1、阻塞状态

2、挂起状态

3、阻塞与挂起的区别

4、Linux 内核源代码中的进程状态分类

1.1  R(Running)运行状态

1.2  S(Sleep)睡眠状态

1.3  D(Disk sleep)磁盘休眠状态

1.4  T(Stopped)停止状态

1.5  t(tracing stop)调试用停止状态

1.6  X(dead)死亡状态(终止状态)

1.7  Z(Zombie)僵尸状态

5、查看进程状态的命令

六、僵尸状态(重点)

1、僵尸状态的演示

2、僵尸进程的危害

七、孤儿进程

八、进程优先级

1、引子:查看进程信息

2、PRI 和 NI

3、如何修改 nice 谦让度

九、并行与并发

1、前言:进程与寄存器

2、并行

3、并发

十、进程地址空间(重点,底层之根基)

1、引子

2、基于地址空间,重新理解进程地址

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

4、缺页中断


本章将介绍操作系统的基本概念,特别是Linux操作系统,并详细解释Linux中的重要概念——进程及其分类。本章同样是个长篇,但是了解透彻后对后面操作系统的学习将很有帮助。

一、前言:冯诺依曼体系结构

我们所认识的计算机,大部分都遵循冯诺依曼体系结构,即由一个个硬件组合而成。

1、图中各个单元的介绍

1、输入设备:键盘、鼠标、网卡、显卡、声卡、磁盘、固态硬盘(SSD)等等

2、存储器:即内存内存实际上就是一个硬件级别的大的缓存

3、中央处理器:包含运算器+控制器+寄存器+各种级别的缓存。

4、输出设备:显示器、磁盘、网卡、显卡等等各种外设。

2、值得注意的几点

1、数据处理速度:CPU > 内存 > 各种外设(如磁盘)

2、在数据层面上,CPU优先和内存进行交互,不和外设直接交互(直接交互会因为外设的速度慢而导致 CPU 需要等待,从而影响整体性能因此外设要输入输出数据,也只能向内存中写入和从内存中读取。

3、一句话总结,所有的设备,都只能直接和内存进行交互。

4、程序在运行前,也必须加载进内存。(因为程序 = 代码 + 数据,最终都要交给 CPU 来处理,但是程序的可执行文件是储存在磁盘(外设)中的,而CPU只和内存交互,因此想让CPU读取到这些代码和数据,必须加载进内存)

二、操作系统

操作系统是一款对软硬件进行资源管理的软件,笼统概括包括内核与其它程序;目的是管理所有的软硬件资源,为用户的程序提供一个良好的执行环境。

1、操作系统分层图

操作系统层状结构图

从上往下介绍:

1、用户:广义上指所有计算机使用者,狭义上指开发者。只要有操作系统存在,用户便只能通过操作系统访问底层硬件,无权直接访问。 

2、用户操作接口:包括图形化界面,lib 库等等。

3、系统调用接口:操作系统被上层访问的唯一渠道。操作系统会提供一些系统调用接口(大部分是函数),用户只能通过这些系统调用接口访问操作系统,有效防止操作系统收到侵害。如果一个用户想访问很底层的数据 / 访问硬件,就必须通过调用系统调用接口来贯穿整个层状结构。

4、操作系统:真正的软硬件管理者;其内部必然会存在大量的对象和数据结构内存管理、进程管理、文件管理、驱动管理,是操作系统的四大核心管理模块

5、驱动程序:用来从硬件中获取数据,交给操作系统。除了CPU和内存外,基本都需要驱动程序来获取数据。

6、底层硬件:被管理者;以冯诺依曼体系结构组织。

2、小总结

硬件方面:操作系统把硬件用 struct 结构体描述起来,再用链表 / 其它高效的数据结构组织起来。

系统调用:在开发的角度来看,操作系统对外会表现为一个整体,并暴露自己的部分接口供上层开发使用。但是系统调用接口往往功能比较基础,对用户的要求也相对较高。

库函数:有心的开发者大佬们,为了便于上层用户开发,对部分系统调用接口进行了适度的封装,从而形成了用户操作接口层面的库

三、 进程(重点)

核心:进程 = 可执行程序(进程的代码和数据)+ 内核数据结构(PCB)

1、进程的基本概念

进程在课本中的概念一般被定义为:程序的一个执行实例 / 内存中正在执行的程序等

进程在内核中的观点一般是:担当分配系统资源(CPU时间、内存等)的实体。

2、存放进程信息的数据结构——PCB(Linux 下称作 task_struct)

进程本身肯定是有一些属于自己的信息的,这些信息就被放在一个叫做进程控制块(process control block,即PCB)的数据结构中,PCB可以理解为是进程属性的一个集合。

Linux 下描述进程的结构体有一个专有的名称,叫做 task_struct,是PCB的一种;它是 Linux 内核中的一种数据结构,会被装载到内存里,并包含着进程的信息。

// task_struct 的形象化代码
struct task_struct
{// 进程ID// 进程程序的代码和数据的地址// 进程状态// 进程优先级// 
}

2.1  task_struct 的内容分类

1、标识符:即 pid(process id),是描述本进程的唯一标识符,用来区分各个进程;每次启动进程,pid 都会发生变化。

2、进程状态:任务状态,退出代码,退出信号等。

3、优先级:相对于其他进程的优先级。

4、程序计数器:程序中即将被执行的下一条指令的地址。

5、内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针

6、上下文数据:进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。

7、I/O状态信息:包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。

8、记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。

9、其他信息

 3、在终端中通过输入命令查看进程信息

我们给出一段最基础的示例代码 test.cpp:

#include <iostream>
#include <unistd.h>
using namespace std;int main()
{while (1){cout << "进程正在运行" << endl;sleep(1);}return 0;
}
这里的22849就是进程的PID

 

ps aux | grep test | grep -v grep

命令解释:

ps aux:用来列出系统上所有正在运行的进程的信息(其中 ps 是 "process status" 的缩写,用于显示当前系统的进程快照;a 表示显示所有用户的进程,不仅仅是发出命令的用户;u 表示以用户友好的长格式显示进程信息;x 表示显示没有控制终端的进程)

grep test:用来搜索包含字符串“test”的行(grep 是一个强大的文本搜索工具,一般用来搜索固定的字符串行) 

grep -v grep:这里的用于从上一步的结果中排除包含 “grep”的行,参数 -v 表示反向选择,即显示出不包含 “grep”的行。 由于使用了管道,这里的目的就是为了排除由于 grep test 而产生的与 grep命令自身相关的行输出。

4、父子进程

操作系统中除了1号进程(即操作系统本身)外,所有的进程都有一个父进程,由父进程来创建当前进程。

4.1 1号进程

1号进程是系统启动时由内核创建的第一个用户空间进程,负责初始化系统,加载必要的服务和守护进程。

4.2 shell 进程与用户程序

当通过终端登录系统时,系统会启动一个登录 shell 进程(如 bash 或 zsh)。这个 shell 进程的父进程通常是进程 1。

当在 shell 中运行一个程序时,shell 会自动调用使用 fork 系统调用创建一个子进程,然后在子进程中使用 exec 系统调用替换为我们要运行的程序。这个子进程就是程序的主进程。

5、通过系统调用来创建进程与获取进程标识符(pid) 

5.1  创建子进程的系统调用接口:fork()的初识(这里浅尝辄止,后面章节会详细介绍)

1、fork() 有两个返回值,子进程返回值为0,父进程返回值为子进程的 pid(因此通常在调用 fork() 之后会用 if 进行分流,区分父子进程)

2、父子进程之间代码共享,数据各自开辟空间私有一份。(不过子进程是写时拷贝,即暂时与父进程共用一块空间,等到需要修改数据的时候才开辟空间私有一份)

3、在C/C++中,调用 fork() 函数需要包含头文件 <sys/types.h> 和 <unistd.h>

5.2 获取父进程的ppid和自己进程的pid

获取进程id需要使用系统调用 getpid();父进程的 pid 在子进程中被称作 ppid,需要调用系统调用 getppid() 获取。

示例代码:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{printf("我是一个进程,我的pid是:%d\n",getpid());pid_t ret = fork();if(ret == 0){while(1){printf("我是一个子进程,我的pid是:%d,我的ppid是:%d\n",getpid(),getppid());sleep(1);}}else if (ret < 0){perror("fork 创建子进程失败!\n");return 1;}else{while(1){printf("我是一个父进程,我的pid是:%d,我的ppid是:%d\n",getpid(),getppid());sleep(1);}}return 0;
}

四、进程的状态

教材上把进程状态分为6种:创建、就绪、阻塞、执行、挂起、终止其本质就是PCB中的一个字段(变量) status

进程状态变化的本质更改 PCB 中的 status 整数变量 -> 再将PCB从一个队列移动到另一个队列(因为在操作系统中,不同状态的进程交由不同的队列进行管理)

1、阻塞状态

操作系统会维护一个等待队列,我们的代码一定会或多或少访问系统中的某些资源,如果该资源没有就绪,则不具备访问条件,会导致代码无法向后执行,这时就会进程就会陷入阻塞状态。

这时候操作系统就是把该进程的 PCB 放入维护的等待队列(也可以称作阻塞队列)中,表示进程正在等待系统资源就绪,无法被调度。

所以阻塞状态就是进程PCB被链入等待队列中的状态,此时该进程会卡住,无法被CPU调度。

2、挂起状态

处于阻塞状态的进程,仍然在内存中等待,如果此时OS的内存资源已经严重不足了,怎么办呢?
这时OS就会把内存中的阻塞进程的代码和数据移动到磁盘里面腾出空间,这个过程称作挂起(针对所有阻塞进程,将内存数据置换至外设)(因为是由于阻塞进程导致的,所以又称阻塞挂起。当进程被OS调用的时候,进程的代码和数据又会加载进来)(如果磁盘资源也不够,万不得已,操作系统便只能杀掉进程以保持OS的正常运转)

3、阻塞与挂起的区别

阻塞的进程还在内存中,挂起的进程代码和数据已经放入外存里了。

阻塞状态通常是由于某些资源未就绪而使进程被动进入的状态,资源就绪后进程就会被唤醒。

挂起状态通常是由于系统 / 用户要求,主动把进程置于一种停止的状态,需要额外动作才会恢复。

4、Linux 内核源代码中的进程状态分类

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
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 */
}; 

可以发现,在 Linux 下的进程,总共有七种状态:运行、睡眠、磁盘休眠、通用停止、调试用停止、死亡、僵尸。 

Linux进程状态图

1.1  R(Running)运行状态

表示进程在运行中 / 在运行队列里,并不是代表一定在运行。

运行队列:

运行队列类似于其它OS中的就绪队列,每个CPU都会在其内核维护一个运行队列;只要在运行队列中的进程,状态都是运行态。

运行队列中的进程,除了需要CPU资源以外,其它资源都已就绪,可以随时被调度;只要分配到CPU时间片,就可以立刻运行。(操作系统把某进程 PCB 放进运行队列中,称作唤醒该进程)

1.2  S(Sleep)睡眠状态

这里的睡眠状态是可中断睡眠(又名浅度睡眠),是一种特殊的阻塞状态,可以对外部信号作出相应,也可以被终止。通常是用户主动请求,让进程进入睡眠状态暂停一段时间。

1.3  D(Disk sleep)磁盘休眠状态

磁盘休眠状态又称不可中断睡眠,也是一种特殊的阻塞状态,处于深度睡眠状态下的进程无法被杀掉,不能通过任何信号停止,只能让它自己醒来,一般会等待IO的结束。 

1.4  T(Stopped)停止状态

发送 SIGSTOP 信号,就可以让进程进入停止态,被暂停的进程不会被调度,会停留在当前位置;发送 SIGCONT 信号,就可以让进程继续执行。

1.5  t(tracing stop)调试用停止状态

当进程因调试目的被停止时,会被设置为“tracing stop”状态。

这种状态通常发生在使用调试工具(如gdb)对进程进行调试时,当调试器请求停止进程以检查其内部状态时。进程在接收到 SIGTRAP 信号后也会进入这种状态,这通常发生在执行了一个断点指令之后。

在这种状态下,进程同样不会被调度执行,但其目的是为了允许调试器获取进程的信息或修改其状态。要恢复这种状态的进程,通常也是通过调试工具发送继续执行的命令,或者直接发送SIGCONT信号。

1.6  X(dead)死亡状态(终止状态)

死亡状态又名终止状态,是个瞬时状态,只是一个返回态,不会体现在任务列表中。 

1.7  Z(Zombie)僵尸状态

僵尸状态是个很重要的状态,也是 Linux 的独有状态,会产生僵尸进程,由于篇幅较长,后文会开辟单独一个小节介绍。

5、查看进程状态的命令

ps aux 或 ps axj

六、僵尸状态(重点)

僵尸状态是 Linux 的独有状态,当一个进程在系统中被终止后,其 PCB 暂时不会被释放,而是会先把 PCB 信息采集给父进程,读取子进程的退出信息。在读取成功后,该进程才会先变成死亡装填,随后释放 PCB。

进程退出后,从 进程退出 -> 父进程 / OS 读取该进程PCB退出信息 这段期间,该退出进程的PCB结构仍然被维护,此时该进程所处的状态,就是僵尸状态。这个进程,就是僵尸进程。

1、僵尸状态的演示

#include <stdio.h>
#include <unistd.h>int main()
{int res = fork();if (res < 0){perror("子进程创建失败!\n");return 0;}else if (res == 0){// 子进程printf("子进程[%d]变成僵尸模式……\n", getpid());sleep(5);exit(0);}else{// 父进程printf("父进程[%d]正在休眠……\n", getpid());sleep(30);}return 0;
}

在 5-30秒期间,子进程已经退出,父进程还在运行,没有读取子进程的退出信息,子进程就会陷入僵尸状态。

可以发现,子进程陷入僵尸状态
(这里的 ps -eo pid,ppid,cmd,state:
-e:显示所有进程。-o:指定输出格式。pid,ppid,cmd,state:输出进程 ID(PID)、父进程 ID(PPID)、命令行(CMD)和进程状态(STATE))

2、僵尸进程的危害

僵尸进程会一直以僵尸状态保留在进程表中,一直等待父进程读取退出信息;如果父进程一直不读取,该进程的 PCB 就会一直存在,就会造成内存的浪费,并可能出现内存泄漏问题。

七、孤儿进程

如果父进程先退出,子进程后退出,陷入僵尸状态的子进程就没有父进程来读取退出信息,回收资源了,就会变成孤儿进程。

孤儿进程会被1号进程所领养,由1号进程回收资源,否则会因为无人回收造成资源泄露。 

#include <stdio.h>
#include <unistd.h>int main()
{int res = fork();if (res < 0){perror("创建子进程失败!\n");return 1;}else if (res == 0){// 子进程sleep(30);exit(1);}else{// 父进程,父进程先退出,子进程变成孤儿进程sleep(5);printf("父进程已退出\n");exit(1);}return 0;
}
可以发现,父进程退出后,子进程变为孤儿进程,被1号进程领养

八、进程优先级

进程优先级其实就是指CPU分配资源的先后顺序,本质也是 PCB 中的一个字段,数值越小,优先级越高,优先级高的进程有优先执行权。队列中排队的实质,也就是在确认优先级。

配置进程优先级对多任务环境下的 Linux 比较有用,还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能。(不过进程优先级不要轻易更改,降低优先级不要权限,提升优先级需要权限)

1、引子:查看进程信息

ps -l

在 Linux 中,我们可以用 ps -l 来显示当前终端会话中的进程列表:

这里有几个主要的内容:

1、UID:代表执行者身份

2、PID:进程ID

3、PPID: 父进程ID

4、PRI:进程优先级,值越小,越早被执行

5、NI:nice值,进程优先级的修正数据,又名谦让度

2、PRI 和 NI

PRI 比较好理解,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小

进程的优先级别越高。

NI 就是是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值。

PRI值越小越快被执行,加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice

这当nice值为负值的时候,该程序的优先级值将变小,其优先级将会变高,越快被执行。

所以,调整进程优先级,在Linux下,实际就是调整进程nice值。(值得注意的是,nice值只是进程优先级的修正数据,和进程优先级不是一个概念)

nice其取值范围是-20至19,一共40个级别。(Linux 进程的默认优先级都是80,所以Linux的优先级范围就是 60 - 99)

为什么 Linux 要把优先级设定在一定范围之内?

因为要保证操作系统调用进程的时候较为均衡地确保每一个进程都能得到调度,否则容易导致某些优先级较低的进程始终得不到CPU资源,导致进程饥饿。

3、如何修改 nice 谦让度

top -> 进入top后按“r ”–> 输入进程PID –> 输入nice值

九、并行与并发

1、前言:进程与寄存器

想要了解并行与并发,势必要了解寄存器。

1、我们的进程是怎么知道当前程序运行到了哪里?又是怎么做到函数之间跳转的?

答:CPU内有一个 eip 寄存器(程序计数器),会保存正在执行指令的下一条指令的地址。

2、程序代码运行时产生的各种数据(进程的上下文数据)都会在寄存器中临时保存;如果有多个进程,每一个进程在寄存器中形成的临时数据都应该不同。(有几个进程,就应该有几个上下文数据保存在寄存器中)

3、我们在函数中定义的栈临时变量为什么可以返回到外部?

栈的临时变量会被释放,但是在返回的时候返回语句会把对应的变量内容放在寄存器中(一般是 eax 寄存器),寄存器会充当代码的临时空间。

2、并行

多个进程多个CPU下,分别同时运行,叫做并行。

3、并发

多个进程一个CPU下,采用进程切换的方式,让多个进程在一段时间内都可以推进(实际上是一个CPU在高频率地来回切换各个进程,速度很快,也因此,每一个进程不是独占CPU直到运行结束,而是会隔一段时间就从CPU上剥离下来,让进程之间能更均衡地调度),称为并发。 

十、进程地址空间(重点,底层之根基)

我们在C/C++专栏中的《 C/C++(三)C/C++内存管理 》中,介绍了程序的地址空间分布,这里的程序地址空间的叫法其实并不规范,规范叫法其实是进程地址空间

因为每个进程都有其地址空间,都会占用内存,所以OS一定要对地址空间做管理。 

1、引子

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
using namespace std;int main()
{pid_t pid = fork();int flag = 0;if (pid == 0){// 子进程,先结束flag = 2014;printf("我是子进程,进程ID:%d, 我的flag:%d,flag地址:%p\n", getpid(), flag, &flag);}else{// 父进程,后结束flag = 2015;sleep(2);printf("我是父进程,进程ID:%d, 我的flag:%d,flag地址:%p\n", getpid(), flag, &flag);}
}

父子进程共享代码,但是当数据要修改的时候,在各自的空间开辟了一份空间,进而达成各自修改变量,但是我们运行可以发现,父子进程的变量的值虽然各自不同了,但是他们的地址却是一样的!

变量的值不一样,说明父子进程输出的一定不是同一个值,但是地址却一样;不是同一个值,地址却一样,这只能说明,这个“地址”,不是真正意义上的物理地址!在Linux下,我们把这个“地址”叫做虚拟地址。所以,进程地址空间又称虚拟地址空间

我们在C/C++日常调试,输出的时候看到的地址,一律都是虚拟地址。用户看不到物理地址,由操作系统统一把虚拟地址转换成物理地址。

2、基于地址空间,重新理解进程地址

每一个进程运行后都会有一个进程地址空间存在,都会在系统层面建立起自己的页表映射结构

上面的图就足以说明问题,同一个变量,地址相同,其实是虚拟地址相同,而内容不同其实是因为被映射到了不同的物理地址!通过页表之间进行转化

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

1、让进程可以以统一的视角看待内存,把无序变有序,让任意一个进程都可以通过地址空间+页表的形式把乱序的内存数据分门别类规划好。

2、存在虚拟地址空间,就可以有效地对进程访问内存进行安全检查,对非法访问直接拦截。

3、把内存管理和进程管理实现代码上的解耦,通过页表可以让进程映射到不同物理内存处,进而实现进程独立性。

4、缺页中断

当访问虚拟地址的代码时,如果页表中没有对应的物理地址和代码,操作系统就会触发缺页中断,暂停访问请求,先在内存中重新开辟空间,再把对应可执行程序需要执行的对应虚拟地址的代码加载进内存,然后把将虚拟地址与新分配的物理地址之间的映射关系添加到页表中,最后把标记字段设置为1,表示内存已经分配,对应内容已经填充,把代码解除暂停,继续访问。

可以概括为:

暂停访问 -> 申请内存 -> 填充内容 -> 填入页表 -> 建立映射关系

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

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

相关文章

加法电路和减法电路

一、加法电路 下边为加法电路的拓扑结构 加法电路作用1: 直流量叠加 如上图仿真所示,利用放大器LM324AD进行加法电路的仿真,输入为直流+1V和直流+2V,经过加法运算,根据上边Uo的计算公式进行计算,可得Uo=-3V,和仿真结果保持一致。如下图所示。 加法电路作用2: 信号叠加…

8. 数据结构——邻接表、邻接矩阵的基本操作

一、邻接表 1. 内容 2. 实现代码(直接可以复制使用) //邻接表的相关操作 #include<bits/stdc.h> #define MVnum 100 #define OK 1 #define ERROR -1 using namespace std;typedef int Status; typedef char VerTexType; //假设顶点的数据类型为char typedef int ArcT…

自动化研磨领域的革新者:半自动与自动自磨机的技术突破

据QYResearch调研团队最新报告“全球半自动和自动自磨机市场报告2023-2029”显示&#xff0c;预计2029年全球半自动和自动自磨机市场规模将达到5.3亿美元&#xff0c;未来几年年复合增长率CAGR为3.5%。 图00001. 半自动和自动自磨机&#xff0c;全球市场总体规模 如上图表/数据…

基于RFID的智能门禁系统的设计(论文+源码)

1系统总体设计 本次基于RFID的智能门禁系统的设计与实现课题&#xff0c;在功能上设计如下&#xff1a; 1.可以通过RFID模块&#xff0c;实现对IC卡的注册&#xff0c;注销操作&#xff1b; 2.在IC卡解锁时&#xff0c;如果非注册IC卡刷卡时蜂鸣器将报警提示&#xff1b; 3…

CAD图纸防泄密|哪些措施可以加密公司图纸?五个宝藏方法分享,2024必读!

在工程设计领域&#xff0c;CAD图纸作为企业的核心资产&#xff0c;其安全性至关重要。一旦图纸泄露&#xff0c;不仅可能给企业带来重大的经济损失&#xff0c;还可能损害企业的声誉和竞争力。 那么&#xff0c;怎么实现CAD图纸防泄密呢&#xff1f; 以下是五个CAD图纸防泄密…

java项目之文理医院预约挂号系统源码(springboot)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的文理医院预约挂号系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息。 项目简介&#xff1a; 本系统的使用角色可…

spring 学习路线梳理(二)注解

1.通过注解的方式创建bean 1.1 定义dao层的接口和实现 public interface ILoginDao {public String login(); }Slf4j Repository public class LoginDaoImpl implements ILoginDao {public LoginDaoImpl(){System.out.println("spring create bean call");}Override…

Blender进阶:着色器节点

11 着色器节点 11.1着色器 着色器Shader&#xff0c;负责给物体表面着色。 综合以下参数&#xff1a; -基础色-金属度、粗超度、透明度-法向-入射光颜色、强度、角度。。 着色器本质上是一段程序、算法&#xff0c;即着色器编程。 在节点编辑器中&#xff0c;支持算法的可…

状态检测防火墙报文处理发流程

华为大同&#xff1a;全系列状态检测防火墙报文处理流程 华为防火墙在处理报文时&#xff0c;其核心在于其状态检测与会话机制&#xff1a;当防火墙接收到报文后&#xff0c;它会在特定时间和条件下创建会话&#xff0c;并对那些与会话表匹配的报文进行特定的转发处理。基于这一…

深入探讨SEO分析技巧助力网站流量提升

内容概要 在当前的数字化时代&#xff0c;SEO分析的重要性不言而喻。它是提升网站流量的关键工具&#xff0c;帮助站长有效地优化网站内容和结构。通过系统的SEO分析&#xff0c;站长可以掌握用户搜索行为和需求&#xff0c;从而制定出更具针对性的内容策略。例如&#xff0c;…

【天线&通讯】电力设施检测系统源码&数据集全套:改进yolo11-RFCAConv

改进yolo11-DAttention等200全套创新点大全&#xff1a;电力设施检测系统源码&#xff06;数据集全套 1.图片效果展示 项目来源 人工智能促进会 2024.11.01 注意&#xff1a;由于项目一直在更新迭代&#xff0c;上面“1.图片效果展示”和“2.视频效果展示”展示的系统图片或者…

18.农产品销售系统(基于springboot和vue的Java项目)

目录 1.系统的受众说明 2.开发环境与技术 2.1 Java语言 2.2 MYSQL数据库 2.3 IDEA开发工具 2.4 Spring Boot框架 3.系统分析 3.1 可行性分析 3.1.1 技术可行性 3.1.2 经济可行性 3.1.3 操作可行性 3.2 系统流程 3.2.1 操作流程 3.2.2 登录流程 3.2.3 删除信…

uni-app发起请求以及请求封装,上传及下载功能(六)

文章目录 一、发起网络请求1.使用及封装2. https 请求配置自签名证书3.拦截器 二、上传下载1.上传 uni.uploadFile(OBJECT)2. 下载 uni.downloadFile(OBJECT) 一、发起网络请求 uni-app中内置的uni.request()已经很强大了&#xff0c;简单且好用。为了让其更好用&#xff0c;同…

地理信息科学专业想搞GIS开发:学前端还是后端?

地理信息科学专业的同学是学前端开发比较好呢还是学后端开发比较好呢&#xff1f; 部分网友&#xff1a;学前端更好 主修前端更好&#xff0c;因为地信学后端&#xff0c;是卷不赢学计算机的 本科卷前端&#xff0c;硕士阶段可以卷后端 甚至有网友直呼&#xff0c;地信根本没有…

美格智能5G车规级通信模组:以连接+算力驱动智能化进阶

2023年3月&#xff0c;基于高通公司第二代骁龙汽车5G调制解调器及射频系统平台SA522M/SA525M&#xff0c;美格智能在德国纽伦堡嵌入式系统展上正式发布全新一代5G车规级C-V2X通信模组MA922系列&#xff0c;迅速引起行业和市场关注。随着5G高速网联逐步成为智能汽车标配&#xf…

win11不好用怎么退回win10_win11退回win10多种方法

最近有网友问我win11不好用怎么退回win10&#xff1f;win11感觉不好用&#xff0c;很多用户这时候想退回win10系统。电脑安装Win11系统觉得操作起来不太习惯&#xff0c;想要重新回到上一个系统&#xff0c;有什么办法实现?其实&#xff0c;微软也在Win11中准备了回退功能,让用…

「Mac畅玩鸿蒙与硬件19」鸿蒙UI组件篇9 - 自定义动画实现

自定义动画让开发者可以设计更加个性化和复杂的动画效果,适合表现独特的界面元素。鸿蒙提供了丰富的工具,支持通过自定义路径和时间控制来创建复杂的动画运动。本篇将带你学习如何通过自定义动画实现更多样化的效果。 关键词 自定义动画动画路径贝塞尔曲线动画控制一、Animat…

【分立元件】贴片电阻过电压故障机理

在文章:【分立元件】贴片电阻器的故障现象和原理 中我们讲到电阻故障现象类型。其中包括了由电气过载导致的电阻体烧损。 在文章:

ubuntu-开机黑屏问题快速解决方法

开机黑屏一般是由于显卡驱动出现问题导致。 快速解决方法&#xff1a; 通过ubuntu高级选项->recovery模式->resume->按esc即可进入recovery模式&#xff0c;进去后重装显卡驱动&#xff0c;重启即可解决。附加问题&#xff1a;ubuntu的默认显示管理器是gdm3,如果重…

Java已死,大模型才是未来?

作者&#xff1a;不惑_ 引言 在数字技术的浪潮中&#xff0c;编程语言始终扮演着至关重要的角色。Java&#xff0c;自1995年诞生以来&#xff0c;便以其跨平台的特性和丰富的生态系统&#xff0c;成为了全球范围内开发者们最为青睐的编程语言之一 然而&#xff0c;随着技术的…