Linux:进程
进程
- 知识铺垫
- 冯诺依曼体系结构
- 操作系统(OS)
- 进程概念
- 进程的查看
- ps 命令
- 获取进程 pid
- 文件内查看进程
- 终止进程的方式
- kill命令
- 快捷键
- 进程的创建 fork
- fork 返回值问题
- 进程状态
- 运行状态 :R
- 休眠状态:S (可中断)和 D(不可中断)
- 暂停状态:T 和 t(追踪式暂停)
- 死亡状态:X
- 僵尸状态:Z
- 孤儿进程
知识铺垫
冯诺依曼体系结构
- 冯诺依曼提出程序存储执行以及计算机由五个部分组成:输入设备、输出设备、存储器、运算器、控制器。
结构如下:
输入设备:键盘、鼠标、麦克风
输出设备:显示屏、扬声器、打印机
这里的存储器指的是: 内存
输入设备 和 输出设备 统称为外围设备,外围设备交互速度一般是比较慢的;
存储器也被称为内存,相较于外设数据的访问速度是快得多。最明显的例子:磁盘 和 内存;
中央处理器(CPU)访问数据的速度是最快的。
在数据层面上,中央处理器(CPU)是不会对输入设备和输出设备进行直接交互的,而是直接对内存进行交互;而外设只会对内存进行打交道。
要交互也不是不行,但是整体的效率就会以外设为主了。为什么这么说呢?
如果将外设之间与CPU直接进行数据交互,我们都知道CPU的计算速度是很快的,从外设中获取数据时,得让外设处理完成后才能得到数据,那么就会出现木桶效应。所以说CPU直接对外设进行交互的话整体数据的访问速率就会以外设为主了。
因为有了内存的存在,我们可以对数据进行预加载处理:CPU在对数据进行计算的时候,可以直接在内存中获取数据,就不需要访问外设了。从而提高了数据计算的效率。
操作系统(OS)
- 操作系统:一款对软硬件做管理的软件
操作系统包括有:内核 和 其他程序
内核:进程管理,内存管理,文件管理,驱动管理
其他程序:函数库, shell程序
设计操作系统的目的:下至与硬件进行交互,管理所有的软硬件资源;上至为用户程序提供一个良好的执行环境。
操作系统是一款纯正搞管理的软件。为什么这么说呢?
管理者在管理一部分事务时,管理者和被管理者其实是没有之间进行沟通的。
例如:校长在管理一所偌的大学校时,校长会直接对每位学生进行管理吗?并不会,如果校长针对每位学生都做管理的话,那么他将忙的不可开交。那么校长是如何管理好一所学校的呢?他只需要将他需要管理的方式下达给主任,然后主任在将需要管理的方式在下达到辅导员,每个辅导员又会将信息转达到班长上,最后每位学生都会接收到对应的要求。之后辅导员会将每位学生的被管理的信息汇总起来,传达给主任,然后各位主任会将所有学生的信息都交到校长手上。至此,校长就对学校内的所有学生信息进行处理,将每个学生描述成一个对象,将这些对象用链表组织连接起来形成一个表格,对整个表格的学生信息进行增删查改,从而起到管理者管理被管理者的效果。
操作系统的管理也是如此,对软硬件做管理时,OS并不会直接对这些软硬件进行管理做处理,会先将这些软硬件先用struct
结构体进行属性封装描述成一个个对象,然后对这些结构体对象用链表或更高效的数据结构组织起来进行管理。
可以说操作系统中管理的本质就是:先描述,再组织。
-
系统调用:在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分
由操作系统提供的接口叫做系统调用。 -
库函数:开发者对部分系统调用进行适度封装,从而形成库
有了上述知识铺垫那么就很容易理解进程了。
进程概念
可执行文件的本质其实是一个普通的二进制文件。文件存储在磁盘当中(文件 = 文件内容 + 文件属性)。
当在Windows下对一个可执行文件进行双击,又或者在Linux命令行下进行 ./
执行可执行文件时,其实就是将文件的文件内容加载到内存中。
现在来分析一句话:将磁盘中的可执行文件内容加载到内存,这个可执行文件的就是被操作系统管理的进程。这句话准确吗?
诸如,一个外国人进入不属于他的国家中,那么就可以说这个人就是这个国家的公民吗?
这个说法是不对的。一个合法的公民需要有这个国家的公民身份证,接受这个国家的法律约束等等。满足这个国家对这个人的管束才能说这个人是属于这个国家的公民。
进程也是如此,当一个可执行文件的代码被加载到内存中时,需要被操作系统管理起来,才能被称为进程。
当然这样说进程概念还是不够具体的。
加载到内存中的可执行文件并不是只有一个,会有许许多多的二进制文件被加载到内存中。面对内存中如此多且杂乱的程序,操作系统该如何去管理呢?
对于进程的管理,OS当然是进行:先描述,再组织。
- 描述:在每个可执行程序加载到内存时,在OS内核里面,会对这些程序的数据或代码创建一个数据结构对象
PCB
,PCB
在Linux操作系统中的名称为:task_struct
。这些PCB
对象中会提取进程中的所有属性,在PCB
的结构体中保存有指向该进程的地址,方便找到进程在内存中的位置。每个进程都有属于自己的PCB
,这就是OS对进程描述的过程。
- 组织:每个
PCB
都有指向下一个PCB
的结构体指针,诸多PCB
之间就可以通过链表的方式组织起来。OS通过管理这些PCB
从而达到间接管理到各个进程的效果。结束一个进程就好比,在PCB
链表中找到对应PCB
进程属性节点,然后对该节点进行释放删除的操作,对每个进程的管理就变成了对PCB
链表的 增删查改。
因此进程概念应该是这样的:
- 进程:内核关于进程的数据结构加上当前进程的代码和数据
进程的查看
ps 命令
ps ajx #查看当前机器下所有正在运行的进程
提示:在使用 ps
查看进程命令时应该和 grep
过滤行代码进行配合,方便我们查找进程的信息。
在这里简单介绍一下:pid
和 ppid
pid:当前进程的进程编号
ppid:当前进程的父进程编号
获取进程 pid
在C语言中,可以通过 getpid()
来获取 pid
编号;通过 getppid()
来获取 ppid
编号。
在使用这些函数前需要包含库函数:#include <unistd.h>
下面来举个例子:
编译运行:
运用 ps 命令来查看一下对应的进程状态:
ps ajx | head -1 && ps ajx | grep 2964 | grep -v grep
上述命令行中,grep也是一个进程,在同时输入ps命令和grep命令时会将grep进程也显示到终端,因此在这里需要将grep进程过滤掉:
仔细观察上述代码,在创建的./myporcess
程序中,出现一个 ppid
编号为:29043 的父进程( bash 进程)。
为什么会出现 bash 进程呢?
bash
是一个命令处理器,能够处理用户输入命令的进程。每当用户输入一个命令时,都是在 bash
进程中创建一个子进程
这也是为什么新创建程序的中会存在 bash 进程的原因。
文件内查看进程
在根目录下存在一个叫 proc 的目录,该目录是保存进程相关属性的目录,属于内存级别的目录,只有当OS启动的时候该目录才会存在。
ll /proc
查看 proc 目录,可以看到很多蓝色数字编号的目录,这些编号正是操作系统中正在运行的进程编号 pid。
还是上述代码为例子,当新运行起来一个程序时在 proc目录下就会创建一个进程的目录:
查看 proc 目录下的 编号为32217的pid目录:
在编号为32217的pid这个文件下就是该进程的所有属性内容了。
当我们终止 pid为32217这个进程后,再次查看 proc 目录下的文件就会发现编号为 32217 的目录不存在了:
终止进程的方式
kill命令
使用 kill 命令不仅可以杀死前台进程,后台进程也可以被杀掉
- 方法一:利用
kill
命令,使用终止信号-9
kill -9 进程编号
- 方法二:
killall
killall 进程名称
快捷键
- 利用快捷键方式:
ctrl + c
进程的创建 fork
通常情况下,在window下双击一个可执行文件 或者 在Linux下 ./
执行一个可执行文件,都是将二进制文件加载到内存,然后被操作系统管理起来,变成一个进程。
有没有一种方式就是手动创建进程呢?答案是可以的。
在Linux下可以通过调用 fork
函数来创建子进程,使用该函数时需要包含 #include<sys/types.h>
头文件。
下面来举个例子,编写这样一串代码:
编译后结果如下:
结果很奇怪,为什么会打印出两行b的结果?
代码是不会骗人的,在代码执行过程中,代码执行过程是顺序执行的。
下面来查对应的 pid 和 ppid ,就可以一目了然:
运行:
第一行b中的 pid:742 指代是 myporcess 进程的编号,可以由打印a的pid推断出来
第二行b中的 pid:743 指代是 myporcess 进程创建的子进程编号,可以由第二行 b 的 ppid :742 推断出来
fork 单词含义为分支的意思,上述代码中在打印a和b之间调用了 fork 函数,此时代码就不会按照原来的线性规则执行,会在调用 fork 函数之后创建一个新的执行流,原来的执行流会继续执行后续代码,新的执行流也会在 fork函数调用完后,继续执行后续代码。这也是终端为什么会打印出两行b的原因。
上述代码只是举例,当然正常情况下 fork 不会去这样使用,会根据 fork 函数的返回值判断进行使用。
下面来介绍一下 fork 函数返回值:
#include <unistd.h>
pid_t fork(void);
当父进程创建成功了后,会将子进程的 pid 返回给父进程,子进程的返回值为0,失败返回-1
还是上述代码,但是进行稍微修改,将fork返回值、地址进行打印显示:
结果如下:
父子进程执行先后顺序是不一定的,不同机器下会有不同结果,这个完全取决于调度器的调度管理。
上面的进程创建是成功的,因此可以看到的结果是:
在第一行b的打印结果中,父进程在fork函数执行后, ret 接收到了子进程的pid 1436
在第二行b的打印结果中,子进程在fork函数执行后, ret 接收到了返回值为:0
但是为什么 ret 的地址会一样呢?甚至奇怪一个函数会有两个返回值。
地址一样是关乎到进程地址空间的话题再后续会讲到这里先不提。不过可以确定的是这里的地址并不是物理上的地址,而是虚拟地址。
首先来简单使用一下 fork 函数的使用场景,再来回答为什么一个函数会有两个返回值的问题。
根据fork 的返回值进行 if 条件判断,限定父子进程执行代码:
父进程执行一次,停顿两秒,子进程执行一次,停顿一秒。运行结果如下:
前面提到过执行流问题问题,在 fork 函数之后会出现两个执行流。
上面代码的也演示了,可以通过 if-else 分支语句来控制执行流。
正常的C语言,是不能同时满足 if 和 else,而且还是不能同时执行两个 while,在这里为什么又可以了呢?
因为,在 fork函数执行后,所创建的子进程会共享同一份代码。
创建进程时,是将磁盘中的文件和数据加载到内存,然后OS通过创建PCB结构体对象来管理这些加载到内存中的数据和代码。PCB结构体中存储的是加载到内存中的数据和代码的属性,记住了是属性。当我们在执行 fork 函数时,就会创建子进程,创建方式就是:将父进程的PCB拷贝一份下来(当然子进程的PCB也有属于自己的私有属性),父进程的PCB指向内存中对应进程的数据和代码,子进程的PCB也是如此,这也是为什么fork 函数之后会公用同一份代码的原因了。
这里的子进程PCB的创建方式只能说是比较笼统介绍一下,具体会更加复杂一些。
每个进程之间是独立的,一个进程的运行是不会影响到另一个进程的运行。
举例:将上面的代码跑起来
此时,再将2066杀掉
可以看到,2065 进程不受影响。
加载到内存中的文件内容是:代码 和 数据。代码是写死的,没有人就是在代码运行时会将代码更改掉,因此每次运行代码时是只读的。那么加载到内存中的数据呢?
下面再来看个问题:同时共享同一份代码,一个进程在运行时修改数据内容时,另一个进程会被影响吗?
在 fork 函数之前创建一个x变量,在父进程打印一次内容后将x值进行修改,运行看看结果:
fork 函数值后,代码是公用的。当有一个执行流尝试更改数据内容时,OS会自动触发一种机制,这个机制叫做写实拷贝。写实拷贝会将原有的数据内容拷贝到父进程或是子进程当中,这样就可以保证各个进程之间的独立性。
fork 返回值问题
一个函数最多一个返回值,为什么 fork 函数可以出现两个返回值?
当一个函数执行到 return 语句的时候可以说该函数的主体功能是已经完成的了。
fork 是一个系统调用的函数,功能是创建一个子进程,将父进程的PCB进行拷贝,然后形成子进程的PCB,当fork函数执行到 的return语句时,说明上述功能就是已经执行完的了。那么就形成了两个执行流:一个是父进程的执行流,另一个是子进程的执行流。但是 fork函数还没有完,还需要执行完最后一句return语句,因此就形成了一个函数会出翔两个return返回值的现象。
进程状态
在讲进程状态前,先来提两个概念:阻塞 和 挂起
- 阻塞:进程因为等待某种条件就绪,从而导致一种不推进的状态(进程等待某种资源就绪的过程)
进程阻塞在用户角度看了就是运行的程序卡住了,进程阻塞就是在等待某种资源就绪。这些资源包括有 硬件资源、系统调用、内存资源、I/O操作等等。资源是有限的,当进程发生阻塞时就是有其他进程正在使用这些资源,只有等资源被其他进程用完后才能轮到自己。
举例:当我们下载一个比较大的文件时,突然有一段时间网断了。此时,CUP会将该进程停止运行,等什么时候网络资源空闲了再来运行该进程。下载的进程就被中断了被称为该进程阻塞,这个阻塞过程就是等待网络资源的过程。
OS是一款搞管理的软件,不仅仅是进程会被管理,硬件也会被管理起来。OS搞管理的本质就是先描述,再组织。
OS为了管理好硬件,会将每一个硬件都创建对应的结构体对象,然后通过数据结构的模型将这些结构体对象链接起来。
每一个硬件对应就是一个结构体对象节点。
在CPU运行的进程不只有一个,当某个进程缺少资源时,CPU会要求该进程先获取对应的资源,此时的OS会将当前进程的PCB链接到对应资源尾部,等待该资源能被自己利用。当资源获取到后,OS会将该进程的PCB链接回CPU继续运行起来。
PCB可以被维护到不同的结构体对象的队列中!!!
- 挂起:操作系统在管理内存时,会将闲置的某个进程的数据和代码暂时性的存放到磁盘中(闲置:表示进程再等待某种资源的过程)。内存中只保存该进程的PCB结构体对象,这个状态叫做挂起状态。
当该进程获取到资源时,操作系统再将该进程的数据和代码加载到内存中,进而可以达到减少内存资源开销的作用。
挂起状态严格意义上被称为阻塞挂起
下面以Linux操作系统进程状态展开分析:
在Linux中,task_struct 表示进程的PCB。task_struct 是一个结构体内部会包含进程的诸多属性,其中就包含有状态属性
struct task_struct
{int status; //状态属性//... 其他属性
}
其中,不同的状态会被定义成不同的宏。例如:
#define R 0
#define S 1
#define D 2
#define T 4
#define t 8
#define X 16
#define Z 32
下面来简单介绍不同的进程状态:
运行状态 :R
进程处于 R 状态时,并不是就在 CPU 正在运行。在 CPU 上运行的进程并不是就只有一个,会有很多进程。
为了调度运行这些进程,操作系统会维护一个叫进程的运行队列 (简化伪代码:struct task_struct* runqueuq )CPU只需要调用该队列中的进程即可。进程在该队列中,那么该进程就被称为运行状态。
所以,看一个进程处于什么状态时,就看这个进程在哪里排队。
举例看一下正在运行的进程的状态:
直接一个死循环,运行查看对应进程状态:
休眠状态:S (可中断)和 D(不可中断)
那么,下面将代码更改一小部分,添加一行打印代码:
运行后,查看一下对应进程状态:
为什么会这样?进程明明就是正在死循环打印。
死循环是没有问题,当我们在往显示屏打印内容的时候就是在访问硬件资源,访问期间该进程就是在等待显示器的资源。OS会将该进程的PCB就会被链接到硬件资源的队列中进行排队,CPU计算速度是很快的,所以该进程的大部分时间都是在等待资源的过程,只有那么一瞬间是被执行的
可以说在上万次查看该进程的状态时,只有那么一瞬间是运行状态。大部分时间都是在等待资源的过程,所以查看进程时的状态会被显示为S+状态。
除了上面例子,最典型的就是使用 scanf 函数时,该进程在为了获取键盘输入资源时,将会卡在终端,等待用户的输入。
所以可以说:休眠状态本质就是一种阻塞状态
- 进程D状态:不可中断的休眠状态
这种状态通常发生在等待磁盘I/O操作完成的进程上,因为进程正在与硬件交互,且这个交互过程不允许被其他进程或中断打断。在此状态下,进程无法被信号中断,很多时候甚至连关机都很难关机,若要强行中断只能是拔电源,但是会造成磁盘数据丢失。
一般 D 状态出现的概率非常非常小,若是电脑出现了D的进程状态,说明该电脑快宕机了,内存吃紧,OS腾不开空间。
暂停状态:T 和 t(追踪式暂停)
-
T状态:将正在运行的进程设置为暂停状态,一般为用户需求。
-
使用 kill 命令,向指定的进程发送 -19 信号(19 号信号为 SIGSTOP 意思表示为暂停一个进程)。
kill -19 进程编号
以代码为例:
编译运行:
当前进程处于 S+ 状态,上面提到过,这里不在赘述。下面将该进程置于暂停状态:
如果要将该进程恢复,那么就可以使用 18 号信号(SIGCONT)让当前进程继续运行
kill -18 进程编号
注意:使用该信号后,会将进程处于后台运行(后台运行的进程会显示为 S
状态,没有+
的符号 )。在没有 +
号状态下的进程我们使用快捷键 Ctrl+c
是杀不了后台进程的,此时只能使用 kill 的 9 号信号才能杀掉。前台运行的进程,都可以使用快捷键 Ctrl + c
杀掉
- t 状态:追踪式暂停状态
最典型的例子就是:在调试代码时,只要在某行代码处设置了断点,那么调试中就不会将所有代码一次性跑完,会卡在断点处。这个暂停方式就是追踪式暂停。
以上面代码为例子,用gdb进入调试状态:
在第 10 行代码打个断点,然后输入 r 命令将代码调试起来:
此时再来查看当前进程状态:
死亡状态:X
进程处于终止状态表示死亡状态,这个状态过程是一瞬间,在进程中很难查看到,终止的一瞬间就结束了。
僵尸状态:Z
- 进程退出码:一个用于表示进程终止原因的数字代码
创建进程是为了计算机能够帮助我们去办事,办事的结果无疑就是两个:完成和未完成。
对于一些进程,我们会寻求他们执行后的结果,也就是事办的怎么样了?
我们编写的代码都是以 main函数为入口,然后完成程序的主体功能,再到 return 语句结束,不过一般我们自己写的main函数的返回值都会设置成返回 0。
其实 main 函数的返回值是进程的退出码。
查看当前进程退出码的方式:
echo $?
在Linux输入的代码也是进程,我们可以尝试输入一条命令后查看进程退出码:
如果我们输入命令时带入错误选项,结果会报错,此时再来查看一下进程的退出码:
进程退出码:2 表示 ls 命令执行后未能执行正常的工作。
进程退出码是为了获取进程工作的结果。如果一个进程退出后直接就是 X 死亡状态,那么父进程或者就是OS能够获得进程退出码吗?
答案是不行的。
- 僵尸状态:在Linux中,一般进程退出后并不会即可退出,它会维持一个状态,这个状态被称为僵尸状态(状态的维持是为了OS或者是父进程去获取当前退出进程的退出码)
举个简单的例子:当一个人正常非正常死亡,警察为了调查。不会立即将尸体交还给家属,会将尸体交给法医去调查死因结果,等到结果出来后才会交给家属处理。
下面用代码来看看进程的僵尸状态,创建子进程:
运行,查看进程状态:
此时将子进程杀掉:
僵尸状态的维持是需要消耗内存的,就好比动态开辟的内存空间,如果使用了内存空间不去释放就会导致内存泄漏。
操作系统为了避免内存泄漏问题,会将僵尸进程进行回收处理,当然我们也可以自己手动回收,回收方式会在后续博客中提及。
孤儿进程
- 孤儿进程:父进程创建子进程,当父进程被提前终止运行,这个子进程就被称为孤儿进程
上面提到过,一个进程的终止并不会直接就被干掉,会将进程结束的退出码返回给父进程。
如果父进程被干掉了,bash进程会来接收父进程的退出码。问题来了,子进程的退出码谁来接收?
下面来举个例子:
父子进程同时运行,当5秒后父进程结束运行,再来查看结果:
下面是进程在第五秒前和第五秒后的进程状态:
可以看到子进程(11673),在父进程结束运行后 ,子进程的 ppid 直接变成了 1 ,进程状态也由 S+ 变成了 S。
进程 1 代表的是 Init 进程,当一个进程结束运行后会进入僵尸状态,这是为了让父进程获取退出码。上面的父进程结束后,子进程变成了孤儿进程,为了能够让子进程在结束后的僵尸状态得到回收,Init (1号)进程就充当了领养孤儿的角色。在子进程退出后,子进程的退出码得到处理,子进程的僵尸状态会被Init 进程回收,从而避免进程僵尸没有及时回收造成的危害。
这篇博客内容就介绍到这里,感谢观看!
相关文章:

Linux:进程
进程 知识铺垫冯诺依曼体系结构操作系统(OS) 进程概念进程的查看ps 命令获取进程 pid文件内查看进程终止进程的方式kill命令快捷键 进程的创建 forkfork 返回值问题 进程状态运行状态 :R休眠状态:S (可中断)…...
【Vue3】defineExpose 实践
【Vue3】defineExpose 实践 defineExpose 是 Vue 3 的 <script setup> 语法糖中提供的一个函数,用于显式地暴露组件的属性、方法或其他响应式状态给其父组件或外部使用。这是在使用 <script setup> 语法时,控制组件公开哪些内部状态和方法的…...

centos7.9安装nacos
centos7.9安装nacos2.3.1 在centos x86_64环境安装nacos2.31环境准备 jdk1.8 、 mysql、 nacos 在window11环境安装nacos2.31 在centos x86_64环境安装nacos2.31 环境准备 jdk1.8 、 mysql、 nacos Nacos 依赖 Java 环境来运行。我们通过下载编译后压缩包方式安装。 重点踩坑…...
ARM/Linux嵌入式面经(四):浙江大华
大华一面 嵌入式 主要是问的项目相关 标准的十五分钟 电话面 这个面试官主要问项目,我同门面的全问八股,可能面试官不一样吧 文章目录 UART串口通信的波特率,常用波特率有哪些串口通信校验方式是什么,有什么区别方便简单的奇偶校验偶校验(even parity)累加和校验CRC循环冗…...

ubuntu 18.04安装教程(详细有效)
文章目录 一、下载ubuntu 18.04镜像二、安装ubuntu1. 点击下载好的Vmware Workstation,点击新建虚拟机,选择 “自定义(高级)”,之后下一步。2. 默认配置,不需要更改,点击下一步。3. 选择 “安装程序光盘映像文件(iso)(…...

第二十一天-NumPy
目录 什么是NumPy NumPy使用 1.数组的创建 2.类型转换 3.赠删改查 4.数组运算 5.矩阵运算 什么是NumPy 1.NumPy操作的是多维数组,什么是纬度? NumPy使用 1. 安装 pip install numpy import numpy as np 2.官网: 中文官网:…...

Vue:自动按需导入element-plus图标
自动导入使用 unplugin-icons 和 unplugin-auto-import 从 iconify 中自动导入任何图标集。 完整vite.config.js参考模板 https://download.csdn.net/download/ruancexiaoming/88928539 动态导入图标参考 https://blog.csdn.net/ruancexiaoming/article/details/136568219 导入…...

魔法之线:探索string类的神秘世界
🎉个人名片: 🐼作者简介:一名乐于分享在学习道路上收获的大二在校生 🙈个人主页🎉:GOTXX 🐼个人WeChat:ILXOXVJE 🐼本文由GOTXX原创,首发CSDN&…...

使用gnvm下载nodejs和npm
目录 前言 一、下载gnvm 二、利用gnvm下载nodejs 三、下载对应版本的npm 四、gnvm常用的命令 总结 前言 由于之前下载的版本过低,需要升级版本。但在使用gnvm升级node版本时遇到了一系列的问题,索性就把nodejs全部删除,重新用gnvm在下…...

C语言——简易版扫雷
目录 前言 编辑 游戏规则 游戏结构的分析 游戏的设计 使用多文件的好处有以下几点: 游戏代码实现 框架(test.c) game函数(test.c) InitBoard初始化(game.c) Print打印棋盘(g…...
L3自动驾驶的“双保险”:冗余EPS关键技术解析
摘要: 本文主要介绍冗余EPS的发展路径和关键技术。 引言 在乘用车领域,电动助力转向系统(Electric Power Steering,EPS)相比传统的液压助力转向系统(Hydraulic Power Steering,HPS)具有结构简单、响应迅速、能耗低等优点,因此应用很广。随着智能驾驶的发展,作为底层…...
java.net.UnknownHostException
目录 报错信息 报错分析 UnknownHostException 分析 尝试解决 域名 报错可能 网络请求: 数据库连接: Socket通信: 总结: 报错信息 java.net.UnknownHostException Caused by: java.net.UnknownHostException:at java.…...

比派电器T6白色系高速吹风机,高品质保证下,追求极致性价比
广东比派电器科技有限公司于2020年成立于东莞市松山湖高新技术企业园区融易大厦,公司聚焦于小家电的研发,生产,销售。专注在小家电的PCBA研发,产品设计,成品生产。提供小家电产品一站式解决方案,致力于成为…...
每天学习一个Linux命令之ss
每天学习一个Linux命令之ss 在Linux中,网络管理是非常重要的一项工作。ss(Socket Statistics)是一个强大的命令行工具,用于获取各种网络套接字统计信息。它可以帮助我们查看网络连接、监听套接字、路由表、组播成员等信息。本文将…...
Qt的信号槽机制
1. 什么是元对象编译器和元对象系统? 在开始讲信号槽之前,我们先了解下Qt的框架的核心组成部分,Qt的元对象编译器(MOC)和元对象系统是Qt框架的核心组成部分,它们使得Qt拥有了信号与槽机制、反射(…...
跨域问题总结
文章目录 概要web应用整体请求流程技术名词解释跨域问题产生的原理解决方案前端代码角度前端服务器角度后端代码角度后端服务器角度 小结 概要 在不成熟的前后端开发过程中,经常遇到跨域问题; 在前后端分离的模式下的开发过程中,经常遇到跨域…...

K8s-MySQL主从集群
K8s-MySQL主从集群 引言 该案例代码均可从https://github.com/WeiXiao-Hyy/k8s_example 获取,欢迎Star! 需求 一个“主从复制”的MySQL集群有一个主节点Master有多个从节点Slave从节点需要能水平扩展所以写操作只能在主节点上执行读操作可以在所有节点…...

seo js转码工具
js转码工具作用 用于把js加密 如果不想让别人看到自己的js 代码就可以使用这个方法 js工具网址 https://tool.chinaz.com/js.aspx 效果...

【SQL】601. 体育馆的人流量(with as 临时表;id减去row_number()思路)
前述 知识点学习: with as 和临时表的使用12、关于临时表和with as子查询部分 题目描述 leetcode题目:601. 体育馆的人流量 思路 关键:如何确定id是连续的三行或更多行记录 方法一: 多次连表,筛选查询方法二&…...

java上传本地文件到服务器共享
在Windows系统中,将本地文件夹中的某个文件上传到另一台Windows服务器电脑上,前提:两台电脑网络互通,要接收文件的Windows服务器文件夹开启了共享,可以被本机用如下方式进行写入和读取: 如何配置服务器共享请自行百度查找。 所需要的maven依赖如下: <dependency>…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...
【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验
Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...
React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构
React 实战项目:微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇!在前 29 篇文章中,我们从 React 的基础概念逐步深入到高级技巧,涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...
13.10 LangGraph多轮对话系统实战:Ollama私有部署+情感识别优化全解析
LangGraph多轮对话系统实战:Ollama私有部署+情感识别优化全解析 LanguageMentor 对话式训练系统架构与实现 关键词:多轮对话系统设计、场景化提示工程、情感识别优化、LangGraph 状态管理、Ollama 私有化部署 1. 对话训练系统技术架构 采用四层架构实现高扩展性的对话训练…...

react更新页面数据,操作页面,双向数据绑定
// 路由不是组件的直接跳转use client,useEffect,useRouter,需3个结合, use client表示客户端 use client; import { Button,Card, Space,Tag,Table,message,Input } from antd; import { useEffect,useState } from react; impor…...