Linux(进程控制)
进程控制
- 进程创建
- fork函数初识
- fork函数返回值
- 写时拷贝
- fork常规用法
- fork调用失败的原因
- 进程终止
- 进程退出码
- 进程常见退出方法
- 进程等待
- 进程等待必要性
- 获取子进程status
- 进程等待的方法
- 阻塞等待与非阻塞等待
- 阻塞等待
- 非阻塞等待
- 进程替换
- 替换原理
- 替换函数
- 函数解释
- 命名理解
- 做一个简易的shell
进程创建
fork函数初识
在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
返回值:子进程中返回0,父进程返回子进程id,出错返回-1。


进程调用fork,当控制转移到内核中的fork代码后,内核做:
- 分配新的内存块与内存数据给子进程;
- 将父进程的部分数据内容拷贝进子进程;
- 添加子进程到系统列表当中;
- fork返回,开始调度器调度。
当一个进程调用fork之后,就有两个二进制代码相同的进程。而且它们都运行到相同的地方。但每个进程都将可以开始它们自己的旅程:


我们会发现,fork之前,父进程是单独执行的,fork以后父进程和子进程就分流进行执行了,但是要注意的是父子代码共享是fork以后共享,并不是程序所有的都共享,而且fork以后谁先执行是由调度器决定的。
fork函数返回值
那么为什么要给父进程返回子进程PID呢?
一个子进程只能拥有一个父进程,但是一个父进程可以拥有多个子进程,父进程创建子进程是为了给子进程指派任务,返回子进程的PID就可以很好的对诸多子进程进行管理。
为什么fork以后就会有两个返回值呢?
父进程在调用fork函数以后,fork函数就会进行一系列操作,创建子进程PCB,创建子进程虚拟地址空间,创建页表…,也就是说,在return之前,子进程就已经创建完成了,return就需要父进程子进程都执行,而return的本质就是对id的写入,父进程返回一个id,子进程返回一个id,对于父子进程返回的id程序都需要进行执行,所以此时就会有两个返回值。

写时拷贝
父进程创建子进程,并不会对所有代码和数据都进行拷贝,因为有些东西子进程只需要进行读取,并不需要修改,与父进程共享即可,我们只有在需要修改的时候,对数据进行拷贝即可,这种延时拷贝策略,极大的提升了效率。

fork常规用法
- 一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。
- 一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数。
fork调用失败的原因
- 系统中有太多的进程。
- 实际用户的进程数超过了限制。
进程终止
进程退出场景:
- 代码运行完毕,结果正确。
- 代码运行完毕,结果不正确。
- 代码异常终止。
进程退出码
我们平时写代码过程中,一直是return 0,这是为什么呢?main函数也是个函数,系统要调用他,就需要有返回值,而return 0就表示代码执行成功,结果正确,我们一般用非0表示结果不正确,原因在于成功了就成功了,只有一种可能,但是失败确有多种原因。
我们可以使用echo $?命令查看最近一次进程退出的退出码信息:


C语言当中的strerror函数可以通过错误码,获取该错误码在C语言当中对应的错误信息:


实际上我们Linux中各种指令也是可执行程序,我们也可以看见相应的退出码:

进程常见退出方法
正常退出
- return退出
在main函数中使用return终止是我们最常见的方式。


- 调用exit
(1)执行用户通过 at;
(2) 关闭所有打开的;
(3) 调用_exit;
我们要注意,return只能在main函数中退出,在其他位置都是返回值,而exit可以再任意位置退出,包括调用的函数内部。


3. 调用_exit


我们会发现exit与_exit的区别就是_exit会直接终止进程,不做任何后续处理,而exit会刷新缓冲区。

我们需要知道的是,保存数据的缓冲器并不是操作系统再给我们维护,因为_exit之后,并没有刷新缓冲区,而是C标准库给我们维护的。
异常退出
ctrl + c,信号终止
在进程运行过程中向进程发生kill -9信号使得进程异常退出,或是使用Ctrl+C使得进程异常退出等。
进程等待
进程等待必要性
- 子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。
- 进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。
- 父进程派给子进程的任务完成的如何,我们需要知道。子进程运行完成,结果对还是不对, 或者是否正常退出。
- 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。
获取子进程status
- wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
- 如果传递NULL,表示不关心子进程的退出状态信息。 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
- status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位)

在status的低16比特位当中,高8位表示进程的退出状态,即退出码。进程若是被信号所杀,则低7位表示终止信号,而第8位比特位是core dump标志。
进程退出码:(status >> 8) & 0xFF
进程退出信号:status & 0x7F
进程等待的方法
1. wait方法
pid_t wait(int*status);
//返回值:成功返回被等待进程pid,失败返回-1。
//参数:输出型参数,获取子进程退出状态,不关心则可以设置成为NULL


此时程序处于僵尸状态,父进程并没有对子进程进行回收,当我们使用wait以后:
1 #include<stdio.h>2 #include<unistd.h>3 #include<stdlib.h>4 #include<sys/wait.h>5 #include<sys/types.h>6 7 int main()8 {9 pid_t id = fork();10 if(id == -1)11 {12 perror("fork()");13 return 1;14 }15 else if(id == 0)16 {17 int cnt = 5;18 while(cnt)19 {20 printf("I am chlid: cnt:%d, pid:%d, ppid:%d\n", cnt, getpid(), getppid());21 sleep(1);22 cnt--;23 }24 exit(1);25 }26 else27 {28 pid_t ret = wait(NULL); 29 if(ret > 0)30 {31 printf("wait child sucess: ret:%d\n", ret);32 }33 while(1)34 {35 printf("I am father: pid:%d, ppid:%d\n", getpid(), getppid());36 sleep(1);37 }38 }39 return 0;40 }


父进程一直在等待当子进程运行完成,子进程运行以后,父进程对子进程进行了回收,此时程序中就只剩下父进程,子进程已经成功被回收了。
2. waitpid方法
pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
pid:
Pid= -1,等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
创建子进程后,父进程可使用waitpid函数一直等待子进程(此时将waitpid的第三个参数设置为0),直到子进程退出后读取子进程的退出信息。
1 #include<stdio.h>2 #include<unistd.h>3 #include<sys/wait.h>4 #include<stdlib.h>5 int main()6 {7 pid_t id = fork();8 if(id < 0)9 {10 perror("fork()");11 }12 else if(id == 0)13 {14 int cnt = 5;15 while(cnt)16 {17 printf("I am child: pid:%d, ppid:%d\n", getpid(), getppid());18 sleep(1);19 cnt--;20 }21 exit(1);22 }23 else24 {25 int status = 0;26 pid_t result = waitpid(id, &status, 0);27 if(result > 0)28 {29 printf("wait child sucess: result:%d\n", result); 30 if(WIFEXITED(status))31 {32 printf("子进程退出码:%d\n",WEXITSTATUS(status));33 }34 else35 {36 printf("子进程收到的退出信号:%d\n", status & 0x7F);37 }38 }39 }40 return 0;41 }
当子进程正常退出时,父进程等待子进程成功:

我们可以尝试使用kill -9命令将子进程杀死,这时父进程也能等待子进程成功,但是子进程属于异常退出。

注意: 被信号杀死而退出的进程,其退出码将没有意义。
阻塞等待与非阻塞等待
阻塞等待
上述例子中,我们可以发现,当子进程未退出时,父进程一直就在等待子进程,其他什么事都没有做,此时进程父进程就会进入阻塞状态,当子进程运行完毕,父进程立马被唤醒,接收子进程pid,此时状态就叫做阻塞状态。
非阻塞等待
父进程调用waitpid函数来进行等待,如果子进程没有退出,waitpid这个系统调用立马返回,父进程可以干其他事情,而且父进程会不间断的调用waitpid函数来获取子进程的运行情况,一旦子进程运行结束,父进程就立马接收到,这就叫做非阻塞等待。
做法很简单,向waitpid函数的第三个参数potions传入WNOHANG,这样一来,等待的子进程若是没有结束,那么waitpid函数将直接返回0,不予以等待。而等待的子进程若是正常结束,则返回该子进程的pid。下面以一段伪代码展示一下:

1 #include<iostream>2 #include<vector>3 #include<stdio.h>4 #include<unistd.h>5 #include<sys/wait.h>6 #include<stdlib.h>7 8 typedef void (*hander_t)();9 std::vector<hander_t> handers;10 void fun_one()11 {12 printf("这是一个临时任务1\n");13 }14 void fun_two()15 {16 printf("这是一个临时任务2\n");17 }18 void Load()19 {20 handers.push_back(fun_one);21 handers.push_back(fun_two);22 }23 int main()24 {25 pid_t id = fork();26 if(id == 0)27 {28 int cnt = 5;29 while(cnt)30 {31 printf("I am child: %d\n", cnt);32 sleep(1);33 cnt--;34 }35 exit(1);36 }37 else38 {39 int quit = 0;40 while(!quit)41 {42 int status = 0;43 pid_t ret = waitpid(-1, &status, WNOHANG);44 if(ret > 0)45 {46 printf("wait child sucess: exit code:%d\n", WIFEXITED(status));47 break;48 } 49 else if(ret == 0)50 {51 printf("The child process is still running, the parent processcan handle other things!!\n");52 if(handers.empty())53 {54 Load();55 }56 for(auto e : handers)57 {58 e();59 }60 }61 else62 {63 printf("wait failed\n");64 break;65 }66 sleep(1);67 }68 }69 }
此刻在运行程序我们可以发现,在等待期间,父进程也可以处理其他事情:

进程替换
替换原理
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

当程序替换之后,有没有创建新的进程?
程序替换只是将物理内存空间的代码以及数据进行了替换,程序的PCB,虚拟地址空间以及页表并没发生改变,只是改变当前页表的映射关系,所以并没有创建新进程。
子进程进行替换后,会影响父进程的代码和数据吗?
子进程刚被创建时,与父进程共享代码和数据,此时子进程需要被替换,就需要将父子进程共享的代码和数据进行写时拷贝,父子进程的代码和数据就发生了分离,所以对子进程替换是不会影响父进程的代码和数据的。
替换函数
其实有六种以exec开头的函数,统称exec函数:
1.int execl(const char* path, const char* arg, ...);
第一个参数为需要执行程序的路径,第二个参数为可变参数列表,表示你要如何执行这个程序,并以NULL结尾。


调用execl函数以后,当前进程的所有数据和代码都会被进行替换,包括已经执行的和未执行的,上述程序中printf已经被执行完毕,打印出来了,所以会显示出来,本质上其实他已经被替换了。
2. int execv(const char* path, char* const argv[]);
第一个参数表示可执行程序的路径,第二个参数是一个指针数组,存放的是你要如何执行这个可执行程序,数组以NULL结尾。


3. int execlp(const char* file, const char* arg, ...);
第一个参数是要执行程序的名字,第二个参数是可变参数列表,表示你要如何执行这个程序,并以NULL结尾。
4. int execvp(const char* file, const char* const argv[]);
第一个参数表示可执行程序的路径,第二个参数是一个指针数组,存放的是你要如何执行这个可执行程序,数组以NULL结尾。


5. int execle(const char *path, const char *arg, ...,char *const envp[]);
第一个参数是要执行程序的路径,第二个参数是可变参数列表,表示你要如何执行这个程序,并以NULL结尾,第三个参数是你自己设置的环境变量。


6. int execve(const char *path, char *const argv[], char *const envp[]);
第一个参数是要执行程序的路径,第二个参数是一个指针数组,数组当中的内容表示你要如何执行这个程序,数组以NULL结尾,第三个参数是你自己设置的环境变量。


下面我们可以看见子进程进行替换是的状态:


函数解释
- 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
- 如果调用出错则返回-1
- 所以exec函数只有出错的返回值而没有成功的返回值。
命名理解
- l(list) : 表示参数采用列表 ;
- v(vector) : 参数用数组 ;
- p(path) : 有p自动搜索环境变量 PATH;
- e(env) 表示自己维护环境变量;
| 函数名 | 参数格式 | 是否带路径 | 是否使用当前环境变量 |
|---|---|---|---|
| execl | 列表 | 不是 | 是 |
| execlp | 列表 | 是 | 是 |
| execle | 列表 | 不是 | 不是,必须自己组装环境变量 |
| execv | 数组 | 不是 | 是 |
| execvp | 数组 | 是 | 不是,必须自己组装环境变量 |
| execve | 数组 | 不是 | 是 |
事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册 第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示:

做一个简易的shell
shell建立一个新的进程,然后在那个进程中运行ls程序并等待那个进程结
束,然后shell读取新的一行输入,建立一个新的进程,在这个进程中运行程序 并等待这个进程结束。
所以要写一个shell,需要循环以下过程:
- 获取命令行
- 解析命令行
- 建立一个子进程(fork)
- 替换子进程(execvp)
- 父进程等待子进程退出(wait)

1 #include<stdio.h>2 #include<stdlib.h>3 #include<string.h>4 #include<unistd.h>5 #include<sys/wait.h>6 7 #define NUM 10248 #define SIZE 329 #define SEP " "10 //保存完整的字符11 char cmd_line[NUM];12 //保存打散之后的字符串13 char* g_argv[SIZE];14 15 int main()16 {17 while(1)18 {19 //1.打印出提示信息:"[root@localhost myshell]# "20 printf("[root@localhost myshell]# ");21 fflush(stdout);22 memset(cmd_line, '\0', sizeof cmd_line);23 //2.获取用户输入的各种键盘指令:"ls -a -l -i"24 if(fgets(cmd_line, sizeof cmd_line, stdin) == NULL)25 {26 continue;27 }28 cmd_line[strlen(cmd_line) - 1] = '\0';29 //3.命令行字符串解析:"la -a -l -i" -> "ls" "-a" "-l" "-i"30 g_argv[0] = strtok(cmd_line, SEP);31 int index = 1;32 if(strcmp(g_argv[0], "ls") == 0)33 {
W> 34 g_argv[index++] = "--color=auto";35 }36 if(strcmp(g_argv[0], "ll") == 0)37 {
W> 38 g_argv[0] = "ls";
W> 39 g_argv[index++] = "-l";
W> 40 g_argv[index++] = "--color=auto";41 }
W> 42 while(g_argv[index++] = strtok(NULL, SEP));//第二次解析原始字符串,出入NULL43 //4.让父进程自己执行命令44 if(strcmp(g_argv[0], "cd") == 0)\45 {46 if(g_argv[1] != NULL)47 {48 chdir(g_argv[1]);49 }50 continue;51 }52 //5. fork()53 pid_t id = fork();54 if(id < 0)55 {56 perror("fork()");57 return 1;58 }59 else if(id == 0)60 {61 printf("下面程序是由子进程运行的\n");62 execvp(g_argv[0], g_argv);63 exit(1);64 } 65 else66 {67 int status = 0;68 pid_t ret = waitpid(-1, &status, 0);69 if(ret > 0)70 {71 printf("wait sucesss: exit code:%d\n", WEXITSTATUS(status));72 }73 }74 75 }76 return 0;77 }
结果演示:

相关文章:
Linux(进程控制)
进程控制 进程创建fork函数初识fork函数返回值写时拷贝fork常规用法fork调用失败的原因 进程终止进程退出码进程常见退出方法 进程等待进程等待必要性获取子进程status进程等待的方法 阻塞等待与非阻塞等待阻塞等待非阻塞等待 进程替换替换原理替换函数函数解释命名理解 做一个…...
Java学习笔记——(18)进制介绍
对于整数,有四种表示方式: 二进制:0,1 ,满 2 进 1.以 0b 或 0B 开头。(注:书写二进制时需要按四位数字一组的方式书写,缺的前面补0)十进制:0-9 ,满 10 进 1。…...
【数学建模】--灰色关联分析
系统分析: 一般的抽象系统,如社会系统,经济系统,农业系统,生态系统,教育系统等都包含有许多种因素,多种因素共同作用的结果决定了该系统的发展态势。人们常常希望知道在众多的因素中,哪些是主要…...
图像像素梯度
梯度 在高数中,梯度是一个向量,是有方向有大小。假设一二元函数f(x,y),在某点的梯度有: 结果为: 即方向导数。梯度的方向是函数变化最快的方向,沿着梯度的方向容易找到最大值。 图像梯度 在一幅模糊图…...
[论文笔记]Batch Normalization
引言 本文是论文神作Batch Normalization的阅读笔记,这篇论文引用量现在快50K了。 由于上一层参数的变化,导致每层输入的分布会在训练期间发生变化,让训练深层神经网络很复杂。这会拖慢训练速度,因为需要更低的学习率并小心地进行参数初始化,使得很难训练这种具有非线性…...
SpringCloud教程(中)
目录 八、Hystrix(服务降级) 8.1、Hystrix基本概念 8.1.1、分布式系统面临的问题 8.1.2、Hystrix是什么? 8.1.3、服务降级 概念 哪些情况会触发降级 8.1.4、服务熔断 8.1.5、服务限流 8.2、Hystrix案例 8.2.1、Hystrix支付微服务构…...
蓝帽杯2022
计算机取证 1 内存取证获取开机密码 现对一个windows计算机进行取证,请您对以下问题进行分析解答。 从内存镜像中获得taqi7的开机密码是多少?(答案参考格式:abcABC123) 首先我们直接对 1.dmp 使用 vol查看 py -2 v…...
vue + el-table 表格数据导出为excel表格
下载依赖 npm install --save xlsx file-saver引入插件 import * as XLSX from xlsx; import FileSaver from "file-saver";完整代码 <template><div class"administrativeCase-container"><div class"content-box"><di…...
ClickHouse(二十):Clickhouse SQL DDL操作-2-分区表DDL操作
进入正文前,感谢宝子们订阅专题、点赞、评论、收藏!关注IT贫道,获取高质量博客内容! 🏡个人主页:含各种IT体系技术,IT贫道_Apache Doris,大数据OLAP体系技术栈,Kerberos安全认证-CSDN博客 &…...
Springboot 在 redis 中使用 Guava 布隆过滤器机制
一、导入SpringBoot依赖 在pom.xml文件中,引入Spring Boot和Redis相关依赖 <!-- Google Guava 使用google的guava布隆过滤器实现--><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><vers…...
Docker本地镜像发布到阿里云
1. 本地镜像发布到阿里云 2. 镜像的生成方法 OPTIONS说明: -a :提交的镜像作者; -m :提交时的说明文字; 本次案例centosubuntu两个,当堂讲解一个,家庭作业一个,请大家务必动手,亲自实操。 docke…...
Postgresql源码(112)plpgsql执行sql时变量何时替换为值
相关 《Postgresql源码(41)plpgsql函数编译执行流程分析》 《Postgresql源码(46)plpgsql中的变量类型及对应关系》 《Postgresql源码(49)plpgsql函数编译执行流程分析总结》 《Postgresql源码(5…...
OhemCrossEntropyLoss
1. Ohem Cross Entropy Loss 的定义 OhemCrossEntropyLoss 是一种用于深度学习中目标检测任务的损失函数,它是针对不平衡数据分布和困难样本训练的一种改进版本的交叉熵损失函数。Ohem 表示 “Online Hard Example Mining”,意为在线困难样本挖掘。在目…...
prometheusalert区分告警到不同钉钉群
方法一 修改告警规则 - alert: cpu使用率大于88%expr: instance:node_cpu_utilization:ratio * 100 > 88for: 5mlabels:severity: criticallevel: 3kind: CpuUsageannotations:summary: "cpu使用率大于85%"description: "主机 {{ $labels.hostname }} 的cp…...
AUTOSAR规范与ECU软件开发(实践篇)3.2 ETAS AUTOSAR系统解决方案介绍(上)
1、ETAS AUTOSAR系统解决方案介绍 博世集团ETAS公司基于其强大的研发实力为用户提供了一套高效、 可靠的AUTOSAR系统解决方案, 该方案覆盖了软件架构设计、 应用层模型设计、 基础软件开发、 软件虚拟验证等各个方面, 如图3.5所示, 其中深色…...
【leetcode】第三章 哈希表part02
454.四数相加II public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {HashMap<Integer,Integer> map new HashMap<>();// 统计频率for (int i 0; i < nums1.length; i) {for (int j 0; j < nums2.length; j) {int num nums1…...
【C语言】memset()函数
一.memset()函数简介 我们先来看一下cplusplus.com - The C Resources Network网站上memset()函数的基本信息: 1.函数功能 memset()函数的功能是:将一块内存空间的每个字节都设置为指定的值。 这个函数通常用于初始化一个内存空间,或者清空一个内存空间…...
C++中重载(overload)、重写(override,也叫做“覆盖”)和重定义(redefine,也叫作“隐藏”)的区别?
在C中,允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。 重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。 …...
将非受信数据作为参数传入,可能引起xml 注入,引起数据覆盖,这个问题咋解决
目录 1 解决 1 解决 当将非受信数据作为参数传入时,确实存在XML注入(XML Injection)的风险,攻击者可以通过构造恶意的XML数据来修改XML文档结构或执行意外的操作。为了解决这个问题,你可以采取以下措施: 输…...
设计模式-简单工厂模式
简单工厂模式又称为静态工厂模式,其实就是根据传入参数创建对应具体类的实例并返回实例对象,这些类通常继承至同一个父类,该模式专门定义了一个类来负责创建其他类的实例。 using System.Collections; using System.Collections.Generic; us…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...
0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化
是不是受够了安装了oracle database之后sqlplus的简陋,无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话,配置.bahs_profile后也能解决上下翻页这些,但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可,…...


