进程控制(详解)
上篇文章介绍了进程的相关概念,形如进程的内核数据结构task_struct 、进程是如何被操作系统管理的、进程的查看、进程标识符、进程状态、进程优先级、已经环境变量和进程地址空间等知识点;
本篇文章接着上篇文章继续对进程的控制进行展开,主要包括进程的创建fork,进程的退出和终止、写时拷贝、进程等待(防止僵尸进程的产生使得内存泄漏),进程替换的相关知识!
文章目录
- 进程创建
- fork()的使用场景
- fork()调用失败的原因
- 子进程的数据和代码默认是与父进程共享的
- 创建子进程的操作系统需要干什么
- 父子进程是具有独立性的
- 父子进程既然共享代码和数据如何实现独立性
- 写时拷贝详解
- 进程终止
- 进程退出的场景有哪几种?
- 进程退出的方式
- exit()和_exit()的区别
- 如何获取一个进程的退出码或者退出信号
- 方法一:通过位运算来获取
- 方法二:通过宏来获取
- 进程等待
- 进程等待的用处是什么?
- 进程等待的方式
- wait()函数
- waitpid()函数
- 阻塞等待
- 非阻塞等待
- 进程替换
- 为什么要有程序替换呢?
- 原理是什么?
- 六种程序替换函数
- execl()
- execlp
- execle
- execv()
- execvp
- execvpe
- execve
- exec系列函数用法总结:
进程创建
进程的创建在上篇文章中也有介绍过,进程创建的方式有两种,一种是我们将一个程序跑起来它就会变成一个进程,还有一种就是通过fork()函数创建子进程,主要的创建方式就是通过fork函数,所以我们再来回顾一下fork()函数吧.
fork()函数是一个系统调用接口
//fork()
pid_t fork(void);
返回值: pid_t 类型(实际上是无符号整数) 如果创建子进程成功,返回新创建的子进程的pid(大于0的)给它的父进程,返回0给它自己,如果创建失败就返回-1
注意:fork()成功创建了子进程后就会有父子两个进程,那么也就是说会有两个执行流,fork()会返回两次,分别对父进程和创建出来的子进程进行返回,给父进程返回子进程的pid,因为父进程和子进程的关系是一对多的,所以父进程需要去唯一标识子进程,而进程的pid是天然的标识一个进程的标志,所以fork()返回给父进程的是新创建出来的子进程的pid!而子进程他自己是被创建的时候就知道了自己的pid和其父进程的pid的,所以它不需要fork()函数对他返回任何值,所以fork()就默认给它返回0意思意思一下。上面是创建成功的情况,如果创建失败就会返回-1给当前进程!!!

fork()的使用场景
- 一个进程希望将自己分身,可以一个人做多份工作,那么一个进程就可以通过创建子进程的方式来实现这个目的,可以将自己要干的事分担给子进程,让子进程去帮自己完成。
- 一个进程需要执行别的可执行程序,就可以通过创建子进程的方式,让子进程通过程序替换去帮自己完成程序的执行,我们使用的shell就是这样的,我们执行命令(命令也是可执行程序)的时候,bash就会创建子进程,然后通过程序替换去执行可执行程序。
fork()调用失败的原因
原因非常简单,类比你的手机下来太多东西,容量不够了就不能在下载东西了!
所以fork()创建子进程失败的原因无非就是操作系统中存在太多的进程,内存不够了。
子进程的数据和代码默认是与父进程共享的
我们学习语言的时候了解过继承,子类会继承父类的成员变量,这个理念在进程中同样被使用!
一个进程如果通过fork()函数创建子进程成功,那么它的子进程就会以它的父进程为模板,拷贝它的代码和数据。
创建子进程的操作系统需要干什么
fork()是系统调用,那么就必须要由操作系统来完成子进程的创建工作,那么os会做什么事情呢?
-
根据上篇文章的知识,进程是被操作系统管理起来的,一个进程就是一个task_struct结构体,该结构体包括所有的进程的属性,比如进程的pid、进程的代码对应的地址,数据对应的地址,进程的退出状态,退出信号,进程地址空间等等;
-
操作系统用task_struct 结构体 将进程描述好,再对这些结构体管理,实现对进程的管理工作;
-
知道了上面这些,那么我们就很容易猜到 创建一个进程操作系统就肯定会先创建一个新的task_struct 结构体,该结构体就是那个新新创建的子进程,创建好结构体还没完,还需要对其进行初始化,根据继承的理念,子进程会默认继承父进程的代码和数据,那么操作系统就会把父进程的task_struct中的代码和数据的地址拷贝给子进程;
父子进程是具有独立性的
进程之间是具有独立性的,即使是父子进程也是如此,各个进程之间都是独立运行的!
父子进程既然共享代码和数据如何实现独立性
既然子进程创建出来后是和父进程共享的代码和数据,那么子进程对继承自父进程的数据或代码进行修改,岂不是会对父进程造成影响吗?那怎么还说进程是具有独立性的呢?
- 子进程是和父进程共享的代码和数据没错,进程间具有独立也没有错,错的是子进程修改和父进程共享的数据和代码是不可能会影响到父进程的!因为操作系统考虑到了这个问题,并且让父子进程之间具有写时拷贝的机制,使得在父子一方对共享的资源进行修改是就会将修改的资源分离使得父子各有一份,从而实现进程的独立性!!!
下面具体介绍写时拷贝~
写时拷贝详解
未发生写时拷贝:
未发生写时拷贝的时候,父子是共享这数据和代码的。 它们通过各自的页表将相同的虚拟地址映射到相同的物理地址。

发生写时拷贝后:
当子进程对父子共享的数据或者代码进行修改时,因为进程之间要有独立性,操作不可能直接让子进程将共享的数据给改掉,那样会直接影响到父进程!
所以操作系统会在子进程对父子共享的代码或者数据进行修改的时候,会开辟出一块新的物理空间,将要被修改的代码或者数据拷贝一份之后,让父子中先对共享资源修改的一方去修改新拷贝出来的数据,再让先修改共享资源的一方的页表去重新映射新开辟出来的物理空间实现父子进程的资源分离,互不影响!这就是写时拷贝的基本原理~ (当有一方要对共享资源修改时,为其开要修改的资源开辟新空间,再对该空间的值进行修改,使得二者都有对应的资源,但是被修改的资源不是同一块物理空间了!)

注:写时拷贝的相关操作是由操作系统的内存管理模块完成的!
为什么要有写时拷贝?直接在创建子进程的时候就为其数据和代码开辟新的空间,将其和父进程的数据和代码分离开不好么?
- 1.父子进程的数据,子进程不一定全用,即使使用,也不一定全都会写入(修改)----------存在空间浪费
- 2.最理想的情况是,只有会被父子修改的数据,才会进行分离拷贝,不会修改修改的共享即可 ------理论可以技术角度上无法实现,父子对数据的修改是不可提前预测的
- 3.如果fork的时候就无脑的将父进程的数据拷贝给子进程,就会增加fork的成本(内存和时间层面上)
所以既然写时拷贝存在就有它存在的原因,存在即合理!
写时拷贝是解决上述问题的较为合理的方法,所以才会被采用。写时拷贝是一种演示拷贝的策略,只有当你会对数据进行修改的时候才会给你开辟新的空间,将数据分离,当你不修改的时候,就不会给你新开辟空间,这样省下来的空间就可以被其他进程使用了!体现了os良好的内存管理方案
进程终止
进程退出的场景有哪几种?
- 第一种:代码运行完毕,结果正确
- 第二种:代码运行完毕,结果不正确
- 第三种:代码都没执行完,发生了异常,操作系统直接终止进程
进程退出的方式
- 第一种:通过main函数返回
- 第二种:通过调用exit()函数或系统调用_exit()退出进程
- 第三种:给进程发信号,将进程终止(kill)
上面提到了exit()和_exit(),它们的功能都是让进程退出,两者之间有什么区别呢?
exit()和_exit()的区别
exit()是封装_exit()的函数, _exit()是系统调用。exit() 最终也还是会去调用 _exit() , exit()在 _exit()的基础上增加了新的功能,就是:
- 执行用户通过atexit或on_exit定义的清理函数
- 关闭所有打开的流,所有的缓存数据都会被写入(刷新)

如何获取一个进程的退出码或者退出信号
-
exit(int status)和 _exit(int status)中的参数status就是进程的退出状态码
-
status只有低16位才有价值,它的0-7位是存储着进程的退出信号,8-15位是存的进程的退出码
-
进程正常终止时的退出信号是为0的,退出码为进程设置的退出码
-
进程被信号所终止时,退出码为0,退出信号为进程所收到的信号

方法一:通过位运算来获取
//获取退出码
status>>8&0xFF
//获取退出信号
status&0x7F
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
int main()
{pid_t id=fork();if(id==0){//childint n=10;while(n--){printf("i am child! i am running pid:%d ppid:%d\n",getpid(),getppid());sleep(1);}exit(110);}else if(id>0){sleep(10);int status=0;waitpid(id,&status,0);if(id>0){sleep(3);printf("等待子进程成功\n");printf("子进程退出码:%d 退出信号:%d 子进程pid:%d \n",status>>8&0xFF,status&0x7f,id);//位运算获取退出码和退出信号的方式}printf("父进程退出!\n");}else {printf("fork error!\n");}return 0;
}
运行结果:

方法二:通过宏来获取
操作系统提供对应的宏来协助我们获取对应的退出信息
| 宏 | 说明 |
|---|---|
| WIFEXITED(int status) | 子进程正常终止则返回真,可以通过WEXITSTATUS(int status)获取子进程退出码 |
| WIFSIGNALED(int status) | 子进程异常终止返回真,若为真可通过WTERMSIG(int status)获取子进程的终止信号 |
| WIFSTOPPED(int status) | 子进程若为暂停状态返回真 |
| WIFCONTINUED(int status) | 子进程被暂停后将其继续的状态,返回真 |
| WEXITSTATUS(int status) | 获取子进程退出码 status的次低8位 |
| WTERMSIG(int status) | 获取子进程终止信号 stauts的低7位 |
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<unistd.h>
int main()
{pid_t id=fork();if(id==0){while(1){printf("i am child pid:%d \n",getpid());sleep(1);//exit(110);}}else {int status=0;int ret=waitpid(id,&status,0);//阻塞式等待子进程if(ret>0){// if(WIFEXITED(status)||WIFSIGNALED(status))if(WIFEXITED(status)){printf("子进程退出! 退出码:%d\n",WEXITSTATUS(status));}if(WIFSIGNALED(status)){printf("子进程退出!退出信号:%d\n",WTERMSIG(status));}}}return 0;
}

进程等待
进程等待的用处是什么?
我们知道子进程退出时如果父进程未读取其相关的退出信息那么该子进程就会变成僵尸进程,僵尸进程会有内存泄漏浪费系统资源,且操作系统无法回收,就算是kill 也不能将它怎么样,因为它已经僵尸了,kill可以杀死在运行的进程,但是它做不到杀死一个已经死去的进程!
- 所以进程等待就是防止产生僵尸进程的处理方式。通过让父进程等待子进程退出,然后再读取它的退出信息,那么子进程就不再僵尸,会变成终止状态,随时等待被系统回收资源!
- 创建子进程一般都是让子进程去完成父进程给它分配的任务,那么父进程就需要知道子进程最终完成的如何,所以父进程有必要等待子进程退出,读取它的退出状态!
进程等待的方式
wait()函数
pid_t wait(int *stauts)wait会等待任意一个子进程
参数:
status是输出型参数,可以通过status获取退出子进程的退出状态(退出码或终止信号)
返回值:
成功返回对应子进程pid,失败返回-1
waitpid()函数
pid_t waitpid(pid_t pid,int* status,int options)waitpid相对wait的可选性更多
参数:
pid : 为-1时,代表着等待任意一个子进程;大于0时,代表等待指定pid的子进程
status:输出型参数,可以通过它获取子进程的退出状态(退出码或终止信号)
options:等待方式,当options为0 时代表父进程阻塞式的等待子进程退出;当options为WNHANG时代表着非阻塞等待,当waitpid返回值为0时,说明子进程还未退出,父进程不会一直在那等待,而是会去干别的事,父进程会以轮询的方式来获取子进程是否退出。
返回值:等待成功返回等待的子进程的pid 失败返回-1
阻塞等待
笼统的理解
所谓的阻塞式等待就是将waitpid中的参数options设置为0,那么父进程就会一直停留在waitpid()这条语句这里,什么也不干就是干等着子进程退出,之后再往下执行后续代码。
系统的理解
父进程阻塞式等待子进程就是当子进程未退出时,操作系统会将父进程的PCB(task_sttuct)放到子进程的等待队列当中,根据前面的进程状态知识可以知道,一个进程等待着某种资源就绪的状态叫做阻塞状态,子进程在等待着父进程退出就是父进程等待着资源就绪,只要子进程不退出,那么父进程就会一直再其等待队列中,直到子进程退出,操作系统才会将父进程继续放回运行队列往下运行后续代码!!!

非阻塞等待
设置非阻塞等待的方式就是将waitpid()中的options设置为WNOHANG即可,那么父进程就会去询问子进程是否退出,如果退出了就返回子进程的pid,未退出返回0,出错返回-1;
通过这种返回就可以让父进程去以轮询的方式去询问子进程是否退出,如果退出就将其回收,否则父进程就可以去干其他事情,而不是一直在原地阻塞着硬等着子进程退出,非阻塞等待的方式可以解放父进程的时间!!!
进程替换
为什么要有程序替换呢?
子进程被创建出来是共享着父进程的代码的,并且会从fork()之后的代码处开始往后执行,那么执行的是和父进程一样的代码,这是没有什么意义的! 我们通常是创建子进程去让子进程去完成其他的工作,比如让子进程去执行其他的可执行程序,那么这里就需要用到进程的替换。
原理是什么?
进程替换的原理就是当进程调用exec系列的进程替换函数后,当前进程的用户空间的代码和数据就会全部被新的程序所替换,接下来就会执行的是新的程序的代码!
注意:进程替换只是将一个进程的用户空间代码和数据用新的程序的代码及数据来替换,整个过程中是没有创建新的进程的,原进程的pid是不变的,变的只有代码和数据!

六种程序替换函数

execl()
int execl(const char *path, const char *arg, ...);函数名中的l代表list,指代传执行程序的方式是按列表的方式传参的
参数列表
path:代表着要执行的程序的绝对路径
arg:执行该程序的方式,这里的arg是一个可变参数列表。(执行指令方式的多个字符串都可以被arg接收),但是传给arg的最后一个字符串必须是NULL
例如:执行ls 命令时 我们敲的是 ls -a -l
那么让ls去替换子进程时,首先path 就是传的ls的绝对路径(/usr/bin/ls),剩下的就是我们的执行方式 ls -a -l 那么arg就得是“ls" “-a” “-l”,当然最后得串一个空代表执行命令结束了,所以传给arg的是"ls",“-a”,“-l”,NULL
执行结果:子进程执行了ls(ls是一个命令 ,也是一个程序!)
execlp
int execlp(const char *file, const char *arg, ...);解释
函数名中的p代表着PATH的意思,带p的替换函数就会自动去搜索环境变量PATH;
所以在带p的替换函数中执行某个程序的时候就可以不用传某个程序的绝对路径,只需传其程序名即可,程序替换函数会自己去环境变量中去找这个程序。
参数列表
第一个就是替换的程序路径(不用写全),第二个就是可变参数列表,接收的是执行的方式,同execl.
execle
int execle(const char *path, const char *arg,..., char * const envp[]);解释
函数名带l,说明传参方式是列表;
函数名中的e代表着environment的意思,也就是环境变量了。execle函数可以自己组装环境变量。
execv()
int execv(const char *path, char *const argv[]);解释
path代表着执行的程序的绝对路径;
argv代表着执行替换程序的方式,用数组的方式传参。
execvp
解释
execvp和execlp只相差了一个字母,一个是l,一个是v,l代表list(列表),v代表vecor(数组)
也就是带l的执行程序的方式是以列表的方式传递的,带v的则以数组的方式传递!
execvpe
解释
类比execle和execlp,带v可知execvp的程序的执行方式以数组形式传递,带p可知其会自己搜索环境变量PATH,带e可知该函数可以自己组装环境变量。
execve
上面的六个函数都是语言封装的execve函数 execve是系统调用
注意:
- exec系列的函数的参数args,是接收的程序执行的方式,其必须以NULL结尾!
exec系列函数用法总结:
| 函数名 | args(执行方式)的传参形式 | 是否带路径(PATH) | 是否使用当前环境变量 |
|---|---|---|---|
| execl | (list)列表 | 否 | 是 |
| execlp | 列表 | 是 | 是 |
| execle | 列表 | 否 | 自己组装环境变量 |
| execv | (vector)数组 | 否 | 是 |
| execvp | 数组 | 是 | 是 |
| execvpe | 数组 | 是 | 自己组装环境变量 |
| execve | 数组 | 否 | 自己组装环境变量 |
相关文章:
进程控制(详解)
进程控制上篇文章介绍了进程的相关概念,形如进程的内核数据结构task_struct 、进程是如何被操作系统管理的、进程的查看、进程标识符、进程状态、进程优先级、已经环境变量和进程地址空间等知识点; 本篇文章接着上篇文章继续对进程的控制进行展开&#x…...
瓜子大王稳住基本盘,洽洽食品做对了什么?
2月24日,洽洽食品披露2022年业绩快报,公司预计实现营收总收入68.82亿元,同比增长14.98%, 实现归母净利润9.77 亿元,同比增长5.21%,业绩基本符合市场预期。来源:洽洽食品2022年度业绩快报2022年,瓜子大王洽洽…...
【音视频安卓开发 (十一)】jni基础
要使用jni开发需要包含jni.h头文件JNIEXPORT JNI : 是一个关键字,不能少(编译能通过),标记为该方法可以被外部调用jstring : 代表java中的stringJNICALL: 也是一个关键字,可以少的jni callJNIENV : 这是c和java相互调用…...
通过FRP搭建内网穿透,实现域名访问局域网本机
1我的目标:实现通过域名访问局域网tomcat服务,域名访问方便本地微信调试2思路:(1)用服务器搭建内网穿透利用FRP实现穿透,frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、…...
Pytorch 代码复现终极指南【收藏】
修改自:https://zhuanlan.zhihu.com/p/532511514我在刚接触pytorch的时候搜到了这个大佬的文章,当时最后天坑部分没有看的太明白,直到今天我也遇到的相同的问题,特来做一点点补充,方便大家理解。上述大佬文章的简版内容…...
iOS 后台运行
背景:app有在后台运行的需求 常用的后台常驻实现方法 1.Audio(音乐播放软件) 应用在后台播放无声的音频文件,只要播放不断,后台可一直活着,苹果审核人员如果发现,会被拒,有可能会…...
软设下午题第一大题--数据流图(做题)
软设下午第一大题一、数据流图(Data Flow Diagram,简称DFD)1、基本图形元素元素(1)外部实体(External Agent)(2)数据存储(Data Store)(3ÿ…...
springboot内嵌Tomcat 安全漏洞修复
漏洞扫描提示的是tomcat-embed-core[CVE-2020-1938],解决方式是升级tomcat的版本。 该漏洞影响的版本: Apache Tomcat 9.x < 9.0.31 Apache Tomcat 8.x < 8.5.51 Apache Tomcat 7.x < 7.0.100 Apache Tomcat 6.x 其余的安全漏洞也可以通过…...
Android OTA 相关工具(三) A/B 系统之 bootctl 工具
文章目录1. bootctl 的编译2. bootctl 的帮助信息3. bootctl 的用法1. hal-info2. get-number-slots3. get-current-slot4. mark-boot-successful5. set-active-boot-slot6. set-slot-as-unbootable7. is-slot-bootable8. is-slot-marked-successful9. get-suffix10. set-snaps…...
【Flink】org.apache.flink.table.api.ValidationException: SQL validation failed
遇到问题如下 Exception in thread "main" org.apache.flink.table.api.ValidationException: SQL validation failed. From line 1, column 15 to line 1, column 17: Object aaa not found at org.apache.flink.table.planner.calcite.FlinkPlannerImpl.orgat org.…...
宏基因组鉴定病毒流程中需要的生物信息工具
谷禾健康 许多流行病的爆发都是病毒引起的,面对新的传染性基因组出现的最佳策略是及时识别,以便于在感染开始时立即实施相应措施。 目前可用的诊断测试仅限于检测新的病理因子。适用于同时检测存在的任何病原体的高通量方法可能比使用基于当前方法的大量…...
Doris入门篇-分区分桶实验
简介 测试分区分桶效果。 分区的基本操作 添加分区 ALTER TABLE v2x_olap_database.government_car ADD PARTITION p20221203 VALUES LESS THAN ("2022-12-04");动态分区表不能添加分区,需要转为手动分区表。 查看分区 show partitions from <表…...
Spring-Xml配置
一、Spring 简介 1.简介 文档下载地址:Index of /spring-framework/docs 1.简介 Spring framework 是 Spring 基础框架 学习Spring 家族产品 Spring framework SpringBoot SpringCloud Spring 能用来做什么 开发 WEB 项目 微服务 分布式系统 Spring framew…...
设计模式-工作线程 创建多少线程池合适
1、定义 让有限的工作线程(Worker Thread)来轮流异步处理无限多的任务。也可以将其归类为分工模式,它的典型实现就是线程池,也体现了经典设计模式中的享元模式(重用对象)。 例如,海底捞的服务员(线程),轮流…...
【算法基础】深度优先搜索(DFS) 广度优先搜索(BFS)
一、DFS & BFS 1. 深度优先搜索DFS 深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。 2. 广度优先搜索BFS 广度优先搜索较之深度优先搜索之不同在于,深度…...
【分布式】ProtocolBuffer平滑升级原则
原文链接:https://blog.csdn.net/nash_cyk/article/details/99549719 关于Protocol Buffer优势这里就不详细介绍了,如便于不同开发语言的交互通信,便于服务器上线的平滑升级等。 但Protocol Buffer的Message协议升级是需要注意一些细节&…...
第四阶段17-关于Redis中的list类型,缓存预热,关于Mybatis中的`#{}`和`${}`这2种格式的占位符
关于Redis中的list类型 Redis中的list是一种先进后出、后进先出的栈结构的数据。 在使用Redis时,应该将list想像为以上图例中翻转了90度的样子,例如: 在Redis中的list数据,不仅可以从左侧压入,也可以选择从右侧压入…...
stringstream用法
stringstream是 C++ 提供的另一个字串型的串流(stream)物件,和之前学过的iostream、fstream有类似的操作方式。包含在头文件sstream中(#include <sstream>)。 实例: 1、C++标准库中的<sstream>提供了比ANSI C的<stdio.h>更高级的一些功能,即单纯性、类…...
2022年下半年系统集成项目管理工程师综合知识真题及答案解析
2022年下半年系统集成项目管理工程师综合知识真题及答案解析 1、()不属于“提升云计算自主创新能力”的工作内容。A.加强云计算相关基础研究、应用研究、技术研发、市场培育和产业政策密衔接与统筹协调B.引导大型云计算中心优先在能源充足、气候适宜、自然灾害较少的地区部…...
【洛谷 P2089】烤鸡(搜索)
烤鸡 题目背景 猪猪 Hanke 得到了一只鸡。 题目描述 猪猪 Hanke 特别喜欢吃烤鸡(本是同畜牲,相煎何太急!)Hanke 吃鸡很特别,为什么特别呢?因为他有 101010 种配料(芥末、孜然等)…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...
论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving
地址:LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂,正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
spring Security对RBAC及其ABAC的支持使用
RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型,它将权限分配给角色,再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...







