Linux下的进程控制
目录
退出码
终止进程
进程等待
进程程序替换
自己实现简易shell命令行
内建命令
退出码
在编写代码时main函数内部我们通常都使用return 0;结尾,以此标识正常退出。这里的return 0就是所谓的退出码,Linux下也是一样:



看这个小程序,当Add函数返回值是5050时return 0正常退出,当返回值是其它值时return 1标志程序有错误异常退出。
Linux下可以通过 $? 获取退出码。echo $?得到退出码为1.
$?:记录最近一个进程的退出码(注意是进程)。所以第一次echo $?得到的是1,再 echo $? 得到的是0:

如果返回值为0标识正常退出没必要说明原因,但异常退出的话要说明原因哪里有错误。
return 0:正常; return 其它:异常。
退出码必须有对应的文字描述,可以是自定义的,也可以使用系统映射的(不太频繁)。
之前学习C语言的时候就有一个可以打印错误码的函数strerror。

我们来看看Linux系统映射的错误有哪些:



Linux下一共有133条错误返回信息。
终止进程
程序在系统中一般是3种状态:
1、代码运行完结果正确,return 0;
2、代码运行完结果错误,return !0;退出码在这个时候起效果。
3、代码没运行完程序异常,此时退出码无意义。
终止进程的3种方法:
1、main函数中return 终止进程。
2、exit() 可以在任意位置终止进程。
3、_exit(), 是库函数exit的底层。
方法一上面已经说过了,先来看一下exit():


exit() 参数是status状态。在Linux操作系统中以整数代表状态,对应终止进程信息。
exit是库函数,而_exit是系统调用,exit调用的就是底层的_exit,那么两者还有什么区别呢?
来看上面的程序,因为打印的时候没用\n,数据先加载到缓冲区,过了一秒后程序终止打印到显示器上。
如果换成_exit:

我们发现没有任何打印,所以得出结论:
exit函数终止进程,主动刷新缓冲区;_exit 函数终止进程,不会刷新缓冲区。
那么缓冲区在哪也可以大致明确了,既然exit 在上层,而_exit 在下层,并且前者可以刷新后者不能,那么缓冲区就介于两者之间。

进程等待
我们知道僵尸进程对操作系统是有危害的,会长时间占用内存。今天我们来解决一下这个问题,就是用到进程等待。
那么进程等待的原因是:回收子进程资源,获取子进程退出信息。
回收子进程资源
先来看一下操作系统是如何回收子进程资源的:
这里要介绍一个系统调用函数wait:

通过wait接口让父进程等待子进程就能回收子进程资源。

写完程序make编译,分屏,左侧运行可执行程序,右侧执行下面监督脚本:
while :; do ps ajx | head -1 && ps ajx | grep test | grep -v grep; sleep 1; done

在子进程运行期间,父子进程都处于S+状态。

子进程退出后,变为僵尸状态,此时由于父进程在休眠,还没有回收子进程。

然后wait 函数等待子进程,将其资源回收,子进程的僵尸状态就结束了,只剩下父进程在运行。
以上就是wait 的回收过程。
获取子进程退出信息
获取子进程退出信息也有对应的系统调用接口waitpid:

waitpid头文件和wait一样有两个:#include<sys/types.h> #include<sys/wait.h>
参数有3个,第一个是要获取的对应进程pid,你传谁那就等待获取谁,第二个是status,获取退出信息(重点谈论),第三个是option,现在我们默认设为0,表示阻塞等待。
返回值pid_t > 0 则返回的是等待的进程的pid,如果为-1则表示等待失败。
下面我们来看一下waitpid的实际应用,还是以上面的代码为例:


waitpid的返回值没问题,就是子进程pid,但是status的退出信息不对。
这是因为status不是被整体使用的,有自己的位图结构。听起来比较抽象,画个图理解一下:
上面说了,进程退出有3种情况:
1、代码跑完,结果正确;
2、代码跑完,结果错误;
3、代码没跑完,异常。
status就用来表示这三种状态,下图是status的二进制结构:

整数status有32个比特位,我们只关心它的低16个比特位。低位0~6表示进程终止的信号,如果信号是0就表示正常退出,非0就表示异常终止。kill -l 可以查看终止信号:

次低位8~15表示子进程退出状态。如果终止信号为0(正常退出)再对应到子进程退出状态.
下面我们再改一下刚刚写的程序,将进程终止信息和子进程退出状态打印出来。

status & ox7F: 拿到低7位(终止信号),(status >>8) &oxFF: 拿到次低位(退出信号)。

sign number终止信号是0,表示正常退出。child exit code退出信息号是10,错误信息是10(可以自己定义错误信息)。
下面再谈一下僵尸进程。一个子进程退出的时候变成僵尸状态,它的代码和数据是不会被操作系统保留的,但是它的PCB及退出信息会被保留下来,等待被父进程接收。

waitpid是系统调用,系统调用是操作系统级的接口,相当于使用操作系统的代码。status是一个整数,存放子进程的退出信息。父进程检测子进程的退出信息,子进程将保存在PCB中的exit code 和sign number 交给status,通过status拿回来放到&status中。
在获取子进程的退出结果过程中,使用status & ox7F 这样的位操作其实并不方便,操作系统给我们提供了一些宏,下面我们介绍其中两种:

WIFEXITED:当子进程正常退出返回真;
WEXITSTATUS:WIFEXITED返回真,则获取子进程退出码。
下面我们使用这种方式来获取子进程的退出码:


以上是阻塞等待的情况,子进程退出变为僵尸,父进程会一直等待子进程接收它的退出信息,期间不会干其他事情。
还有一种情况是非阻塞等待,父进程会对子进程的状态进行检测,如果子进程没有就绪立即返回不会等待,过一会再检测还是一样,每一次都是一次非阻塞等待,进行多次轮询,期间父进程可以执行其它操作。


非阻塞等待的好处就是不会占用父进程所有资源,期间它可以执行其它操作。
那是不是非阻塞等待比阻塞等待要好呢?————其实这种比较并没有意义,要看具体场景下使用哪一种了,如果父进程没有其它任务就用阻塞等待,现实中往往阻塞等待用的多一点。
进程程序替换
首先问个问题:为什么要创建子进程,有什么目的?
1、想让子进程执行父进程代码的一部分 ------> 执行父进程磁盘代码中的一部分。
2、想让子进程执行一个全新的程序 ------> 让子进程加载磁盘上指定的程序,执行新程序的代码和数据。
其实上述过程就是进程的程序替换。
要么进程的程序替换具体是怎么做到的,我们来看一下。
下面有6个常见函数,可以实现程序替换。

先看一下第一个函数execl,它可以将指定程序加载到内存,让指定进程来执行。
我们知道,要将程序加载到内存首先要找到该程序的位置,环境变量PATH就是帮助我们找到对应程序位置的,这里由函数的第一个参数path来做;
找到位置后执行,指令后带cmd选项,如ls -l,ls -a....这由第二个参数arg来做;
第三个参数 ... 是可变参数列表,在printf、scanf函数中见到过。它也是决定程序怎么执行的。
下面看段代码来了解一下该函数的具体用法:

一开始进程正在运行,execl函数将/usr/bin/ls 加载到内存,并附带-l 等选项执行。也就是将程序替换成了ls -l 执行。
再来看一下execl函数的参数,const char* path传的是对应要加载到内存的程序路径,const char* arg传的是程序执行方法,在命令行怎么执行这里就怎么传,后面还可以添加选项传进来,最后以NULL结尾,所有exec*系列函数都以NULL结尾表示传参结束。
运行结果:

可以看到确实替换了程序,而本来应该在最后打印的process has run done没有打印出来,这是为什么看了下面我们就知道了。
程序替换的原理

程序替换的本质,就是将指定程序的代码加载到指定的位置,覆盖自己原来的数据和代码。
所以现在就可以解释上面的问题了,最后的printf内容没有打印出来是因为程序替换后,原来的数据和代码被新的覆盖了,因此没有显示出来。
如果我们要替换的程序不存在呢?


要替换的程序不存在,execl函数执行失败,不会进程程序替换,还是执行原来的程序。
来看一下execl函数的返回值:
![]()
execl 只有在执行错误的时候才有返回值-1,成功的话没有返回值,因为不需要——成功执行会程序替换覆盖后面的代码,那返回值就没有意义了。所以execl函数只要返回了那一定是执行出错了,可以用perror("ececl") 获取错误信息:


当然,在实际操作中不会像上面那样写,我们一般是创建一个子进程来进行程序替换,因为进程是独立的,子进程不会影响父进程。
程序替换成功:


这时终止信号为0,正常退出,退出码为0,程序替换成功。
程序替换失败:


这时终止信号为0,正常退出,退出码为1,程序替换失败。
如何保证子进程程序替换不会影响父进程呢?上面说了是由于进程的独立性保证的,还记得之前说过一个写时拷贝的概念吗?这里是一个道理。

子进程的PCB、代码、数据都是根据父进程拷贝过来的,通过各自的页表映射到物理内存上,对应的数据和代码也都一样。当子进程要改变对应的数据时,操作系统会进行写时拷贝,将对应数据拷贝给另一块空间,让子进程页表指向新的空间,再更改数据,同理代码也是一样,可以写时拷贝,于是这样就保证了进程的独立性,不会互相影响。
其它exec*的函数

上面介绍了execl函数,下面的也类似。
先来看一下execlp函数:
exe:可执行程序; l:list列表,将参数一个一个传入exec*; p : path环境变量,带p字符的函数不需要输入程序的路径,只要传入程序是谁,就能自动在环境变量PATH中自动寻找。
演示:

execlv函数
![]()
最后一个v :vector的意思,该函数相较于上面两个,少了一个参数... 也就是可变参数列表,因为vector代替了它,我们可以将本来要传入可变参数列表的选项放进argv[ ]数组中。
演示:

上面char* const argv_[ ]中报了警告,可以强转成char*类型,也可以先不管。
execvp函数:
![]()
显然是上面两者结合,这里演示一种结合main函数的用法。


execvp可以直接使用main函数传入的参数,argv[0]就是当前程序,argv[1]就是当前程序的下一条要替换的程序。怎么执行?从argv[1]处开始执行。
./myexec ls -a -l:要执行后面的ls -a -l
execle函数:
![]()
最后一个e:env环境变量,该函数可以自己导入环境变量。
在演示execle之前先来看一下execl函数能不能在调用我们自己写的程序的时候导入自定义环境变量:

在mybin.c中打印3条语句,其中PWD, HOME是系统环境变量,MYENV是自定义环境变量。

可以看到系统环境变量被打印出来了,自定义环境变量显示为null,此时我们想导入自定义环境变量可以使用execle函数。
它的第4个参数envp[ ] 就是给我们传环境变量的。

在数组envp_中放入自定义环境变量,再在函数中传入数组名调用。

我们发现自定义环境变量确实被导入打印了,但是系统环境变量此时却没有导入。
所以这种传参方式只能导入自定义环境变量,对系统环境变量不适用。
那么如何既导入系统环境变量又导入自定义环境变量呢?
environ可以帮助我们得到系统环境变量:

记得外部声明extern一下,否则会报错。

此时系统环境变量是有了,但是自定义环境变量又没了。
我们刚刚不是用envp_[ ]数组导入了吗,那是因为自定义环境变量和系统环境变量没有关系,设置了自定义环境变量里面没有自带系统的,是自己搞出来的一套环境变量。
其实环境变量就算不传,默认子进程也能获取。现在我们既想导入自定义的也想导入系统的,可以使用putenv:



putenv就是将自定义环境变量导入到系统中,environ指向对应的环境变量表。
这样就导入了系统环境变量+自定义环境变量。
execve函数:

查看man手册发现,execve是2号手册,也就是系统调用接口,而刚刚上面讲的所有exec*函数都是3号手册,也就是库函数。实际上确实只有execve是系统调用,其它都是根据它封装而来的,便于用户选择对应函数使用。
这里再提一点:main函数和execl谁先执行的呢?
————有人说,肯定是main函数呀,它是程序执行的入口。其实不是!
我们的程序在执行前要先加载到内存,是如何加载到内存的呢?liinux下就是用到exec*系列的函数!我们 ./程序能够运行其实就是用到exec*函数帮助加载,也叫加载器。
所以显而易见,这里是execl先于main函数执行。main函数也是函数,也要被调用,也要传参。
传参传的是什么?

其实也就是exec*的参数传给main函数,就算我们不主动传环境变量env,main函数也会默认获取。
自己调用自己的程序
上面我们是在调用系统的指令(程序),下面我想调用一下自己写的程序。
首先,除了刚刚的那个程序(调用者)外还要再写一个小程序(被调用者),在makefile文件中要将依赖关系和方法明确一下,使得两个程序都能被编译运行:

make默认只会执行第一条依赖方法。所以为了让mybin.c和myexec.c都能编译我们在开头加一个依赖关系all,.PHONY特点是总是被执行,所以给出.PHONY:all,再让all依赖于mybin和myexec就能使得两者必定被编译。

在myexec.c中调用execl函数,执行当前路劲下的 mybin可执行程序:


这样就实现了自己调用自己的函数。而且我们不仅可以用execl调用C++写的程序,JAVA、Python也都可以调用。
总之,程序替换可以调用任何后端语言的可执行程序。
自己实现简易shell命令行
结合上面的内容,我们可以自己实现一下shell命令行替换程序的过程。
首先创建makefile和myshell.c文件并建立依赖关系等操作不说了。
vim myshell.c:

首先要打印命令行提示符,因为没有 \n不会立刻刷新缓冲区,所以fflush手动刷新一下。
如何接收输入的命令?————fgets函数:
#define NUM 1024char lineCommand[NUM];

从输入流输入到lineCommand数组中,大小比lineCommand小1,预留空间。获取输入流返回不为NULL,否则失败。
写一个打印查看是否能接收输入命令:
printf("test :%s\n",lineCommand);

可以接收输入命令,但是多了一条空行,原因是用户输入的时候会按 "回车",系统换行本来就有一个 \n了,这样就多了一个,所以这里我们去掉最后一个 \n:

命令读取后就是解析的过程了。而解析的过程中需要把一整串字符串命令分割,比如 "ls -l -a"需要分割成3个短字符串 "ls","-l","-a"。
这里要用到字符串分割函数strtok.
以空格为界,先分割第一部分字串,创建指针数组myargv,将对应字串放入指针数组:
myargv[0] = strtok(lineCommand," ");
如果没有字串了,strtok会返回NULL,而myargv[end]也是NULL,所以根据这点就可以用循环实现字符串整体分割:
myargv[0] = strtok(lineCommand," ");
int i = 0;
while(myargv[i++] = strtok(NULL," ");
然后我们测试一下,使用条件编译:
#ifdef DEBUGfor(int i = 0;myargv[i]; ++i)
{printf("myargv[%d]:%s\n",i,myargv[i]);
}
#endif

当不需要测试只保留条件编译时,在-DDEBUG前加 #
最后命令行解释器不会只跑一次,所以将其整个套在死循环中:
完成输入,分析指令后,接下来是执行指令。
这里执行的话用刚刚讲的6个函数进行子进程程序替换。
首先创建进程,如果是子进程就执行程序替换,用exec*系列的哪一个函数呢?
————execvp,该函数第一个参数是const char* file,也就是不需要路径直接说明指令是谁,第二个参数是argv[ ],和我们这里的myargv[ ]正好适配。

最后父进程等待回收子进程即可,并且不需要关心退出信号。
运行一下:

内建命令
在该环境下执行cd命令时,发现路径不会改变:

首先要了解一个东西叫当前路径和工作路径:
写一个简单的程序,运行起来查看它的信息:


ls -al /proc/7440:proc文件系统是动态从系统内核读出所需信息的,现在查看7440进程系统内存信息,有两个引人注目的文件:cwd 和 exe.
exe我们都知道,是可执行的意思。它告诉用户当前进程执行的是磁盘路径下的哪一个文件。
cwd则是当前工作目录,它就是当前路径。默认在哪个路径下执行某个进程,那它的工作目录就是当前所在路径。并且它可以被修改,用到chdir函数:

在刚刚写的小程序开头调用chmir函数,修改对应路径:

再次ls -al proc/10136:

我们发现工作目录确实改变了。
那这和我们简易实现的shell中cd有什么关系呢?
我们fork进程,子进程执行cd命令,子进程有自己的工作目录,cd更改的是子进程的目录,而子进程执行完了就被回收了,shell(父进程)继续运行下去,所以工作目录不会变。
现在我想在myshell.c程序中改变cd的路径:

如果输入不为空并且输入指令是cd,cd 目录也不为空,就改变工作目录,改完后继续执行当前程序。

像cd命令,不创建子进程,让shell自己执行对应的命令,本质就是执行系统接口。
像这种不需要我们自己来执行,而是让shell来执行的命令,叫做内建 / 内置命令。
也就是由 Bash 自身提供的命令,而不是文件系统中的某个可执行文件。
比如之前说的echo之所以能打印非系统环境变量,也是因为它是内建命令。
相关文章:
Linux下的进程控制
目录 退出码 终止进程 进程等待 进程程序替换 自己实现简易shell命令行 内建命令 退出码 在编写代码时main函数内部我们通常都使用return 0;结尾,以此标识正常退出。这里的return 0就是所谓的退出码,Linux下也是一样: 看这个小程序&…...
QT 文件监视系统QFileSystemWatcher监视目录的改变directoryChanged和监视文件的改变fileChanged
QT 文件监视系统QFileSystemWatcher监视目录的改变相关操作说明mainwindow.hmainwindow.cpp调试结果相关操作说明 添加头文件 Header: #include qmake: QT core bool QFileSystemWatcher::addPath(const QString &path)如果路径存在,则会向文件系统监视器添…...
Typescript基础知识(类型断言、类型别名、字符串字面量类型、枚举、交叉类型)
系列文章目录 引入一:Typescript基础引入(基础类型、元组、枚举) 引入二:Typescript面向对象引入(接口、类、多态、重写、抽象类、访问修饰符) 第一章:Typescript基础知识(Typescri…...
Windows系统扩充C盘空间系列方法总结
目录前言方法一 使用自带的Windows的DiskPart扩充C盘1. 打开cmd2.三步命令方法二:使用Windows系统内置磁盘管理扩展C盘方法三. 使用专业磁盘分区工具总结前言 本教程是总结Windows系统进行C盘(系统盘)扩充空间的系列方法,一般来讲…...
华为OD机试 - 跳格子(Python)
跳格子 题目 地上共有N个格子,你需要跳完地上所有的格子, 但是格子间是有强依赖关系的,跳完前一个格子后, 后续的格子才会被开启,格子间的依赖关系由多组steps数组给出, steps[0]表示前一个格子,steps[1]表示steps[0]可以开启的格子: 比如[0,1]表示从跳完第0个格子以后…...
Java配置文件的值注入
1.平常使用直接在变量头上加上Value就可以把配置文件的值注入进来 Value(“${environment.active}”) private String environment; 2.但是变量使用static修饰时,就不能注入进来了 Value(“${environment.active}”) private static String environment; 这是因…...
SAP 订单BOM与销售BOM的区别
订单BOM与销售BOM的区别 訂單BOM: 是實際生產時用的BOM, 在標準BOM和銷售BOM基礎上增減物料的BOM 銷售BOM: 是為特定客戶設定的BOM, 在主檔數據層次上的BOM, 在生產時是帶到訂單BOM中去的. 標準BOM: 是公司為標準生產的BOM, 在主檔數據層次上的BOM, 在生產時是帶到訂單BOM中去的…...
支付宝支付详细流程
1、二维码的生成二维码生成坐标 <!-- zxing生成二维码 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.3.3</version></dependency><dependency><groupId>co…...
TCP 的演化史-fast retransmit/recovery
工作原因要对一个 newreno 实现增加 sack 支持。尝试写了 3 天 C,同时一遍又一遍梳理 sack 标准演进。这些东西我早就了解,但涉及落地写实现,就得不断抠细节,试图写一个完备的实现。 这事有更简单的方法。根本没必要完全实现 RFC…...
CSS基础选择器,你认识多少?
前言在上一文初识CSS中,我们了解到了其格式:选择器{ }在初步尝试使用时,我们笼统的直接输入了p { }以选择p标签来对其操作,而这一章节里,我们再进一步探索有关基础选择器的相关内容,理解选择器的作用。选择…...
ChatGPT入门案例|商务智能对话客服(三)
本篇介绍智能客服的基本功能架构和基本概念,并利用对话流技术构建商务智能应用。 01、商务智能客服功能结构 互联网的发展已经深入到社会的各个方面,智能化发展已经成为社会发展的大趋势。在大数据和互联网时代,企业和组织愈加重视客户沟通…...
Matlab 最小二乘法拟合平面(SVD)
文章目录 一、简介1.1最小二乘法拟合平面1.2 SVD角度二、实现代码三、实现效果参考资料一、简介 1.1最小二乘法拟合平面 之前我们使用过最为经典的方式对平面进行了最小二乘拟合(点云最小二乘法拟合平面),其推导过程如下所示: 仔细观察一下可以发现...
AtCoder Regular Contest 126 D题题解
思路 首先我们看看假设选中 mmm 个数后的答案。 我们首先现将 mmm 个数移动到一起,在将他们重新排序。 我们知道,mmm 个数移在一起时,当位于中间的那个数不动时交换次数最少,于是可以列出式子(cic_ici 是点 iii 的…...
Android R WiFi热点流程浅析
Android R WiFi热点流程浅析 Android上的WiFi SoftAp功能是用户常用的功能之一,它能让我们分享手机的网络给其他设备使用。 那Android系统是如何实现SoftAp的呢,这里在FWK层面做一个简要的流程分析,供自己记录和大家参考。 以Android R版本为…...
【C++进阶】二、多态详解(总)
目录 一、多态的概念 二、多态的定义及实现 2.1 多态的构成条件 2.2 虚函数 2.3 虚函数的重写 2.4 虚函数重写的两个例外 2.4.1 协变 2.4.2 析构函数的重写 2.5 C11 override 和 final 2.5.1 final 2.5.2 override 2.6 重载、覆盖(重写)、隐藏(重定义)的对比 三、…...
node-sass@4.14.1 包含风险, 如何升级依赖至 dart-sass
文章目录需求我上网都查到了哪些信息在 github 看到了 node-sass 依赖的最新版本的列表:关于方案2的失败不同版本的 nodejs 和 node-sass依赖的**适配关系**从何得知替代方案——dart-sass如何安装 dart sass?需求 在做一个基于Node、React的前端项目&a…...
DataWhale 大数据处理技术组队学习task2
三、Hadoop分布式文件系统 1. 产生背景 数据量越来越大,一台独立的计算机已经无法存储所有的数据---->将大规模的数据存储到成百上千的计算机中------为了解决数据管理以及维护极其繁琐与低效------>分布式文件系统 分布式文件系统是管理网络中跨多台计算机…...
一文读懂select、poll、epoll的用法
select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,…...
《C陷阱与缺陷》----词法“陷阱”
导言: 由于一个程序错误可以从不同层面采用不同方式进行考察,而根据程序错误与考察程序的方式之间的相关性,可以将程序错误进行划分为各种陷阱与缺陷: ①.词法“陷阱” ②.语法“陷阱” ③.语义“陷阱” ④.连接问题 ⑤.库函数问…...
千锋教育+计算机四级网络-计算机网络学习-04
UDP概述 UDP协议 面向无连接的用户数据报协议,在传输数据前不需要先建立连接;目地主机的运输层收到UDP报文后,不需要给出任何确认 UDP特点 相比TCP速度稍快些简单的请求/应答应用程序可以使用UDP对于海量数据传输不应该使用UDP广播和多播应用…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
VSCode 使用CMake 构建 Qt 5 窗口程序
首先,目录结构如下图: 运行效果: cmake -B build cmake --build build 运行: windeployqt.exe F:\testQt5\build\Debug\app.exe main.cpp #include "mainwindow.h"#include <QAppli...
初探用uniapp写微信小程序遇到的问题及解决(vue3+ts)
零、关于开发思路 (一)拿到工作任务,先理清楚需求 1.逻辑部分 不放过原型里说的每一句话,有疑惑的部分该问产品/测试/之前的开发就问 2.页面部分(含国际化) 整体看过需要开发页面的原型后,分类一下哪些组件/样式可以复用,直接提取出来使用 (时间充分的前提下,不…...
aurora与pcie的数据高速传输
设备:zynq7100; 开发环境:window; vivado版本:2021.1; 引言 之前在前面两章已经介绍了aurora读写DDR,xdma读写ddr实验。这次我们做一个大工程,pc通过pcie传输给fpga,fpga再通过aur…...
dvwa11——XSS(Reflected)
LOW 分析源码:无过滤 和上一关一样,这一关在输入框内输入,成功回显 <script>alert(relee);</script> MEDIUM 分析源码,是把<script>替换成了空格,但没有禁用大写 改大写即可,注意函数…...
Vue.js教学第二十一章:vue实战项目二,个人博客搭建
基于 Vue 的个人博客网站搭建 摘要: 随着前端技术的不断发展,Vue 作为一种轻量级、高效的前端框架,为个人博客网站的搭建提供了极大的便利。本文详细介绍了基于 Vue 搭建个人博客网站的全过程,包括项目背景、技术选型、项目架构设计、功能模块实现、性能优化与测试等方面。…...
