【Linux系统】从零开始构建简易 Shell:从输入处理到命令执行的深度剖析
文章目录
- 前言
- 一、打印命令行提示符
- 代码功能概述
- 二、读取键盘输入的指令
- 2.1 为什么不继续使用`scanf()`而换成了`fgets()`?
- 2.2 调试输出的意义
- 2.3 为什么需要去掉换行符?
- 三、指令切割
- 补充知识: `strtok` 的函数原型
- 四、普通命令的执行
- 代码功能概述
- 五、内建指令执行
- 代码功能概述
- 结语
前言
在操作系统的世界里,Shell 作为用户与系统交互的桥梁,扮演着至关重要的角色。无论是资深的开发者,还是对系统运维感兴趣的新手,了解 Shell 的工作原理和实现机制都能极大地加深对操作系统底层运行逻辑的理解。
本文将带领大家深入探索简易 Shell 的实现过程,从最基础的打印命令行提示符开始,逐步实现读取用户输入、切割指令、执行普通命令以及处理内建指令等核心功能。每一步都配有详细的代码解析和关键知识点说明,不仅让你知其然,更能知其所以然。通过阅读本文,你将掌握 Shell 实现的关键技术,理解其中涉及的编程技巧和系统调用原理,同时也能体会到从无到有构建一个实用工具的乐趣与成就感。
为了方便理解我把源代码发给大家。(单击蓝色字体)
一、打印命令行提示符
#define MARK ":"
#define LABLE "$"
#define LINE_SIZE 1024char commandline[LINE_SIZE]; // 用于存储输入的命令行
char pwd[LINE_SIZE]; // 当前工作目录// 获取用户名
const char* getusername()
{return getenv("USER");
}// 获取主机名
const char* gethostname()
{return getenv("HOSTNAME");
}// 获取当前工作目录并进行格式化
void getpwd()
{getcwd(pwd, sizeof(pwd)); // 获取当前工作目录const char* home = getenv("HOME"); // 获取用户主目录if (home != NULL && strcmp(pwd, home) == 0){// 如果当前目录等于用户主目录,则返回 "~"strcpy(pwd, "~");}else if (home != NULL && strncmp(pwd, home, strlen(home)) == 0){// 如果当前目录是用户主目录的子目录,则替换主目录部分为 "~"static char relative_path[LINE_SIZE];snprintf(relative_path, sizeof(relative_path), "~%s", pwd + strlen(home));strcpy(pwd, relative_path);}
}int main()
{getpwd();printf("%s@%s"MARK"%s"LABLE" ", getusername(), gethostname(), pwd);scanf("%s", commandline); return 0;
}
代码功能概述
-
定义宏和变量:
- 宏
MARK
和LABLE
定义了提示符中的分隔符:
和$
,用于提示符的格式化输出。 - 宏
LINE_SIZE
设置了缓冲区大小(1024
),用于存储命令行输入和当前工作目录。 - 全局变量
commandline
和pwd
:commandline
: 存储从用户输入读取的命令行。pwd
: 存储当前工作目录。
- 宏
-
获取环境变量:
- 函数
getusername()
和gethostname()
分别通过调用getenv()
获取环境变量USER
和HOSTNAME
,用于表示用户名和主机名。
- 函数
-
格式化当前工作目录:
-
函数
getpwd()
获取当前工作目录并将其格式化:
- 如果当前目录是用户的主目录(
HOME
),用~
替代完整路径。 - 如果当前目录是主目录的子目录,则用
~
替代主目录部分。 - 否则显示完整路径。
- 如果当前目录是用户的主目录(
-
-
打印命令行提示符:
-
在
main()
函数中调用getpwd()
获取当前工作目录,然后通过printf()
按以下格式打印提示符:
-
-
等待用户输入:
- 程序通过
scanf()
等待用户输入命令,将输入存储到commandline
中。
- 程序通过
二、读取键盘输入的指令
// 交互式模式,显示提示符并读取用户输入
void interact(char* cline, int size)
{getpwd(); // 获取当前工作目录printf("%s@%s"MARK"%s"LABLE" ", getusername(), gethostname(), pwd); // 显示提示符char* s = fgets(cline, size, stdin); // 读取用户输入assert(s); // 确保输入不为空printf("echo: %s\n", s); // 测试能否读取成功cline[strlen(cline)-1] = '\0'; // 去掉末尾的换行符
}int main()
{while(1) // 循环运行直到用户退出{// 1. 获取用户输入的命令行interact(commandline, sizeof(commandline));}return 0;
}
2.1 为什么不继续使用scanf()
而换成了fgets()
?
scanf
通常用于格式化输入,但它在处理用户输入时存在一些显著的缺点:
scanf
默认以空格、制表符或换行符作为输入的分隔符,因此只能读取一个单词(或无空格的字符串)。- 在交互式 Shell 中,用户输入的命令往往由多个部分组成(如命令和参数),
scanf
无法正确读取整行命令。 scanf
不会自动限制输入长度,如果用户输入超出缓冲区大小,就会导致缓冲区溢出,进而引发未定义行为甚至安全漏洞。
2.2 调试输出的意义
加调试输出 printf("echo: %s\n", s)
是为了测试程序的输入功能是否正常运行,具体验证以下几点:
- 提示符显示后,用户是否能输入数据:
- 如果
s == NULL
,则说明fgets()
未能成功读取输入(可能是因为输入错误或用户直接按下Ctrl+D
)。
- 如果
- 输入是否被正确存储到缓冲区
cline
:- 打印输入内容以确保其正确性。
- 缓冲区大小是否足够:
- 如果输入过长,可能会导致缓冲区溢出或截断。
调试结果:
2.3 为什么需要去掉换行符?
-
用户通过键盘输入时,输入内容会带有换行符(
\n
),这是因为按下回车键会在输入的末尾自动添加一个换行符。 -
例如,用户输入
ls -l
后,缓冲区中的数据实际是:ls -l\n\0
\n
是换行符。\0
是字符串的终止符。
-
在处理命令时,换行符通常是多余的:
- 它会影响字符串的比较。例如,
strcmp(command, "exit")
会返回不匹配,因为字符串实际是"exit\n"
。 - 如果直接打印字符串,换行符会造成多余的空行。
- 某些函数(如文件名或路径相关函数)可能会因为换行符导致逻辑错误。
- 它会影响字符串的比较。例如,
三、指令切割
#define DELIM " " // 分隔符
#define ARGC_SIZE 32char *argv[ARGC_SIZE]; // 用于存储命令行参数// 将输入的命令行分割为参数数组
int splitstring(char* cline, char* _argv[])
{int i = 0;argv[i++] = strtok(cline, DELIM); // 分割第一个参数while(_argv[i++] = strtok(NULL, DELIM)); // 分割剩余的参数return i - 1; // 返回参数个数
}int main()
{while(1) // 循环运行直到用户退出{// 1. 获取用户输入的命令行interact(commandline, sizeof(commandline));// 2. 分割命令行字符串为指令和参数int argc = splitstring(commandline, argv);// 调试代码for(int i = 0; argv[i]; i++){printf("[%d]->%s\n", i, argv[i]);}// 3. 如果没有输入指令(空行),跳过本次循环if(argc == 0) continue;}return 0;
}
补充知识: strtok
的函数原型
char *strtok(char *str, const char *delim);
- 参数:
char *str
:- 第一次调用时传入需要分割的字符串。
- 后续调用传入
NULL
表示继续上一次的分割。
const char *delim
:- 一个以
\0
结尾的字符串,表示分割的分隔符集合(例如,空格、逗号等)。
- 一个以
- 返回值:
- 成功:返回一个指向分割后的子字符串(token)的指针。
- 失败:如果没有更多的子字符串可以返回,则返回
NULL
。
注意点:循环结束时,i
的值比实际的参数个数多 1
(因为最后一次分割返回 NULL
)。
因此,用 i - 1
表示参数的实际个数。
调试结果:
四、普通命令的执行
#define EXIT_CODE 44extern char **environ; // 环境变量
int lastcode = 0; // 上次命令的退出状态
int quit = 0; // 是否退出// 执行外部命令
void normalExcute(char* _argv[])
{pid_t id = fork(); // 创建子进程if(id < 0){perror("fork"); // 创建失败,打印错误信息return;}if(id == 0) // 子进程{execvpe(_argv[0], _argv, environ);exit(EXIT_CODE);}else // 父进程{int status = 0;waitpid(id, &status, 0); // 等待子进程完成if (WIFEXITED(status)) // 检查子进程是否正常退出{lastcode = WEXITSTATUS(status); // 获取子进程的退出状态}}
}int main()
{while(!quit) // 循环运行直到用户退出{// 1. 获取用户输入的命令行interact(commandline, sizeof(commandline));// 2. 分割命令行字符串为指令和参数int argc = splitstring(commandline, argv);// 3. 如果没有输入指令(空行),跳过本次循环if(argc == 0) continue;// 4.执行普通外部指令normalExcute(argv);}return 0;
}
代码功能概述
功能:执行外部命令并处理子进程的退出状态。
关键点:
fork
创建子进程。execvpe
替换子进程执行映像。waitpid
等待子进程结束并处理退出状态。
结果:命令执行结果的退出码存储在 lastcode
中供后续使用。
调试结果:
五、内建指令执行
char myenv[LINE_SIZE]; // 用于存储导出的环境变量// 构建内置命令
int buildCommand(char* _argv[], int _argc)
{// 内置命令:cdif(_argc == 2 && strcmp(_argv[0], "cd") == 0){getpwd();chdir(_argv[1]); // 改变工作目录sprintf(getenv("PWD"), "%s", pwd); // 更新环境变量PWDreturn 1; // 返回已处理标志}// 内置命令:exportelse if(_argc == 2 && strcmp(_argv[0], "export") == 0){strcpy(myenv, _argv[1]); // 保存环境变量putenv(myenv); // 设置环境变量return 1; // 返回已处理标志}// 内置命令:echoelse if(_argc == 2 && strcmp(_argv[0], "echo") == 0){if(strcmp(_argv[1], "$?") == 0) // 显示上一个命令的退出状态{printf("%d\n", lastcode);lastcode = 0; // 重置退出状态}else if(*_argv[1] == '$') // 显示环境变量的值{char* val = getenv(_argv[1] + 1);if(val) printf("%s\n", val);}else printf("%s\n", _argv[1]); // 直接打印参数return 1; // 返回已处理标志}// 自动为 ls 添加 --color 参数if(strcmp(_argv[0], "ls") == 0){_argv[_argc++] = "--color";_argv[_argc] = NULL; // 确保参数数组以 NULL 结束}return 0; // 返回未处理标志
}int main()
{while(!quit) // 循环运行直到用户退出{// 1. 获取用户输入的命令行interact(commandline, sizeof(commandline));// 2. 分割命令行字符串为指令和参数int argc = splitstring(commandline, argv);// 3. 如果没有输入指令(空行),跳过本次循环if(argc == 0) continue;// 4. 尝试执行内置命令int n = buildCommand(argv, argc);// 5. 如果不是内置命令,执行普通外部指令if(!n) normalExcute(argv);}return 0;
}
代码功能概述
该函数 buildCommand
的功能是处理内置命令,包括 cd
、export
和 echo
,并对特定外部命令(如 ls
)添加额外参数。如果输入的命令属于内置命令范围,函数会执行相应逻辑并返回已处理标志;否则返回未处理标志,交由其他部分(如外部命令执行器)处理。
-
处理内置命令:
cd
if (_argc == 2 && strcmp(_argv[0], "cd") == 0)
- 检查用户输入的命令是否是
cd
,且参数个数为 2(命令名 + 目标目录)。 - 功能:
- 使用
chdir
改变当前工作目录为用户指定的路径(_argv[1]
)。 - 更新环境变量
PWD
,同步当前工作目录的变化。
- 使用
- 实现思路:要注意
cd
命令是由bash
本身去做,而不是创建一个子进程去做,故而需要改变的是当前可执行程序的工作目录,并且需要将环境变量中的PWD
改变。 - 测试:
- 检查用户输入的命令是否是
-
处理内置命令:
export
else if (_argc == 2 && strcmp(_argv[0], "export") == 0)
- 检查用户输入的命令是否是
export
,且参数个数为 2。 - 功能:
- 设置环境变量,将用户输入的
变量=值
格式字符串存储到全局变量myenv
。 - 调用
putenv
将该变量添加到环境变量中。
- 设置环境变量,将用户输入的
- 实现思路:我们输入的环境变量实际上是保存在
commandline
当中,只要当下一次输入指令,上一次定义的环境变量就会被清空。putenv
添加环境变量,并不是把对应的字符串深拷贝到系统的环境变量表当中,而是把该字符串的地址保存在系统的环境变量表中(浅拷贝)。因此我们要确保保存环境变量字符串的那个地址里的环境变量不会被修改,所以我们需要为用户输入的环境变量,也就是那一串字符串单独开辟一块空间进行存储,保证在内次重新输入指令的时候,不会影响到之前用户添加的环境变量。所以我们需要定义一个二维数组用于存储导出的环境变量(这里只简单地分配了一维数组)。 - 测试:
注意看,连续两次的写入导致第一次的定义的环境变量被覆盖了。
- 检查用户输入的命令是否是
-
处理内置命令:
echo
else if (_argc == 2 && strcmp(_argv[0], "echo") == 0)
- 检查用户输入的命令是否是
echo
,且参数个数为 2。 - 功能:
- 如果参数为
"$?"
,显示上一个命令的退出状态(从全局变量lastcode
获取)。 - 如果参数以
$
开头,显示对应环境变量的值。 - 否则,直接打印参数内容。
- 如果参数为
- 测试:
故意写成ll
(没有定义的),导致子进程退出,退出码刚好是44。
- 检查用户输入的命令是否是
-
处理外部命令:自动为
ls
添加--color
参数if (strcmp(_argv[0], "ls") == 0)c
- 检查用户输入的命令是否是
ls
。 - 功能:
- 自动为命令添加
--color
参数,用于增强可读性(适用于 Linux 的ls
命令)。 - 确保参数数组以
NULL
结束。
- 自动为命令添加
- 测试:
总结:说了这么久的环境变量,那么请问我们登录的时候,系统中的 shell 的环境变量又是从哪里来的呢?答案是 Bash。那么 Bash 的环境变量又是从何而来?当然是系统自带的目录文件中写入的。
- 检查用户输入的命令是否是
结语
通过以上对简易 Shell 实现过程的详细讲解,相信大家对 Shell 的工作流程和实现细节已经有了较为全面的认识。从命令行提示符的设计,到输入指令的处理,再到不同类型命令的执行,每一个环节都凝聚着操作系统与编程的智慧。
虽然本文实现的 Shell 只是一个简化版本,但其中涉及的技术和思想为进一步探索更复杂、功能更强大的 Shell,乃至深入理解操作系统的运行机制奠定了坚实的基础。希望大家能将所学应用到实际开发或探索中,不断挖掘操作系统的奥秘。如果在阅读过程中有任何疑问或想法,欢迎在评论区交流分享,也别忘了点赞、收藏并持续关注后续更多精彩的技术内容!
今
天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,17的主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是17前进的动力!
相关文章:
【Linux系统】从零开始构建简易 Shell:从输入处理到命令执行的深度剖析
文章目录 前言一、打印命令行提示符代码功能概述 二、读取键盘输入的指令2.1 为什么不继续使用scanf()而换成了fgets()?2.2 调试输出的意义2.3 为什么需要去掉换行符? 三、指令切割补充知识: strtok 的函数原型 四、普通命令的执行代码功能概…...
SSRF服务端请求伪造
SSRF:服务端请求伪造 危害:任意文件读取、任意服务探测(通过端口来探测) 例:探测3306端口,看mysql服务是否开启,再通过文件读取,获得mysql配置文件 例:当我们点击链接…...
LVGL的三层屏幕结构
文章目录 🌟 LVGL 的三层屏幕架构1. **Top Layer(顶层)**2. **System Layer(系统层)**3. **Active Screen(当前屏幕层)** 🧠 总结对比🔍 整体作用✅ 普通屏幕层对象&…...
使用互斥锁保护临界
Linux线程互斥及相关概念解析 1. 临界资源(Critical Resource) 定义:被多个线程共享的资源(如变量、文件、内存区域等),需通过互斥访问确保数据一致性。特点: 共享性:多个线程可能…...
5.8线性动态规划2
P1004 [NOIP 2000 提高组] 方格取数 做法1:DFS剪枝 #include<bits/stdc.h> using namespace std; int n, a[10][10], maxs, minx 11, miny 11, maxx, maxy; void dfs(int x, int y, int s, int type){if(type 1 && x minx && y miny){…...
linux系统Ubuntn界面更改为中文显示,配置流程
Linux 系统是一种开源的、多用户的、多任务的操作系统,具有高度的稳定性、安全性和灵活性,被广泛应用于服务器、嵌入式系统、科研、教育以及个人电脑等领域。以下是关于 Linux 系统的一些基本信息: 发展历程:Linux 的发展始于 19…...
Looper死循环阻塞为什么没有ANR
Looper 死循环阻塞没有 ANR 的原因在于 ANR (Application Not Responding) 的检测机制依赖于特定线程的事件处理超时。以下是详细解释: 1. ANR 的触发机制: 主线程 (UI 线程) 阻塞: ANR 最常见的情况是主线程阻塞。Android 系统会监控主线程…...
数字孪生陆上风电场可视化管理系统
图扑软件搭建陆上风电场数字孪生平台,通过高精度建模与实时数据采集,1:1 还原风机设备、输电网络及场区环境。动态展示风机运行参数、发电量、设备健康状态等信息,实现风电场运维管理的智能化、可视化与高效化。...
图像处理篇---MJPEG视频流处理
文章目录 前言一、MJPEG流基础概念MJPEG流特点格式简单无压缩时序HTTP协议传输边界标记 常见应用场景IP摄像头视频流嵌入式设备(如ESP32)视频输出简单视频监控系统 二、基础处理方法方法1:使用OpenCV直接读取优点缺点 方法2:手动解…...
ensp的华为小实验
1.先进行子网划分 2.进行接口的IP地址配置和ospf的简易配置,先做到全网小通 3.进行ospf优化 对区域所有区域域间路由器进行一个汇总 对区域1进行优化 对区域2.3进行nssa设置 4.对ISP的路由进行协议配置 最后ping通5.5.5.5...
webpack和vite区别
webpack将文件视为模块打包 ,从入口文件递归解析依赖,生成依赖图,使用loader处理非JS模块,最终输出到dist目录 因为要解析所有依赖,所以他启动慢 vite利用浏览器对于es模块的原生支持,利用ESM能力&#x…...
从父类到子类:C++ 继承的奇妙旅程(2)
前言: 各位代码航海家,欢迎回到C继承宇宙!上回我们解锁了继承的「基础装备包」,成功驯服了public、protected和花式成员隐藏术。但—— ⚠️前方高能预警: 继承世界的暗流涌动远不止于此!今天我们将勇闯三大…...
ScaleTransition 是 Flutter 中的一个动画组件,用于实现缩放动画效果。
ScaleTransition 是 Flutter 中的一个动画组件,用于实现缩放动画效果。它允许你对子组件进行动态的缩放变换,从而实现平滑的动画效果。ScaleTransition 通常与 AnimationController 和 Tween 一起使用,以控制动画的开始、结束和过渡效果。 基…...
部署RocketMQ
部署环境:jdk8以上,Linux系统 下载和安装指令: wget https://archive.apache.org/dist/rocketmq/4.9.4/rocketmq-all-4.9.4-bin-release.zip 显示下载成功: --2025-05-10 11:34:46-- https://archive.apache.org/dist/rocketm…...
从爬虫到网络---<基石9> 在VPS上没搞好Docker项目,把他卸载干净
1.停止并删除所有正在运行的容器 docker ps -a # 查看所有容器 docker stop $(docker ps -aq) # 停止所有容器 docker rm $(docker ps -aq) # 删除所有容器如果提示没有找到容器,可以忽略这些提示。 2.删除所有镜像 docker images # 查看所有镜像 dock…...
每日c/c++题 备战蓝桥杯(P2241 统计方形(数据加强版))
洛谷P2241 统计方形(数据加强版)题解 题目描述 给定一个 n m n \times m nm 的方格棋盘,要求统计其中包含的正方形数量和长方形数量(不包含正方形)。输入为两个正整数 n n n 和 m m m,输出两个整数分…...
LLaVA:开源多模态大语言模型深度解析
一、基本介绍 1.1 项目背景与定位 LLaVA(Large Language and Vision Assistant)是由Haotian Liu等人开发的开源多模态大语言模型,旨在实现GPT-4级别的视觉-语言交互能力。该项目通过视觉指令微调技术,将预训练的视觉编码器与语言模型深度融合,在多个多模态基准测试中达到…...
基于Spring Boot + Vue的母婴商城系统( 前后端分离)
一、项目背景介绍 随着母婴行业在互联网平台的快速发展,越来越多的家庭倾向于在线选购母婴产品。为了提高商品管理效率和用户购物体验,本项目开发了一个基于 Spring Boot Vue 技术栈的母婴商城系统,实现了商品分类、商品浏览、资讯展示、评…...
HNUST湖南科技大学-软件测试期中复习考点(保命版)
使用说明:本复习考点仅用于及格保命。软件测试和其他专业课不太一样,记忆的太多了,只能说考试的时候,想到啥就写啥,多写一点!多写一点!多写一点!(重要事情说三遍…...
【AI智能推荐系统】第七篇:跨领域推荐系统的技术突破与应用场景
第七篇:跨领域推荐系统的技术突破与应用场景 提示语:🔥 “打破数据孤岛,实现1+1>2的推荐效果!深度解析美团、亚马逊如何用跨领域推荐技术实现业务协同,知识迁移核心技术全公开!” 目录 跨领域推荐的商业价值跨领域推荐技术体系 2.1 基于共享表征的学习2.2 迁移学习…...
【现代深度学习技术】注意力机制04:Bahdanau注意力
【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈PyTorch深度学习 ⌋ ⌋ ⌋ 深度学习 (DL, Deep Learning) 特指基于深层神经网络模型和方法的机器学习。它是在统计机器学习、人工神经网络等算法模型基础上,结合当代大数据和大算力的发展而发展出来的。深度学习最重…...
使用FastAPI和React以及MongoDB构建全栈Web应用01 概述
Are you ready to craft digital experiences that captivate and convert? 您准备好打造令人着迷并能带来转变的数字体验了吗? In a world driven by innovation, the demand for robust and scalable web applications has never been higher. Whether you’re…...
Flutter - UIKit开发相关指南 - 概览
环境 Flutter 3.29 macOS Sequoia 15.4.1 Xcode 16.3 概览 UIView与Widgets的比较 在UIKit使用UIView类的对象进行页面开发,布局也是UIView类的对象,在Flutter中使用的是Widget,在概念上Widget可以理解成UIView。 差异: 有效期: Widgets是不可变的,它的生存期只…...
扩容 QCOW2 磁盘镜像文件
🌈 个人主页:Zfox_ 目录 ✅ 一、扩展 QCOW2 文件大小✅ 二、启动虚拟机后扩展分区和文件系统方式一:如果使用的是标准分区(如 /dev/vda1)方式二:使用 gparted(图形工具) ✅ 总结 &am…...
【ts】for in对象时,ts如何正确获取对应的属性值
第一种:for…in keyof:适合需要遍历对象属性键并动态访问值的场景。 keyof typeof obj是ts的类型操作符,用于获取对象obj的所有属性键的联合类型(“name” | “age” | “city”)通过obj[key keyof typeof obj]&…...
软考 系统架构设计师系列知识点之杂项集萃(55)
接前一篇文章:软考 系统架构设计师系列知识点之杂项集萃(54) 第89题 某软件公司欲开发一个Windows平台上的公告板系统。在明确用户需求后,该公司的架构师决定采用Command模式实现该系统的界面显示部分,并设计UML类图如…...
绑定 SSH key(macos)
在 macOS 上绑定 Gitee 或 GitHub 的 SSH Key,通常分为以下几步操作,包括生成 SSH key、添加到 ssh-agent,并配置到 Gitee 或 GitHub 平台。 1. 检查是否已有 SSH Key ls -al ~/.ssh 看看是否已有 id_rsa 或 id_ed25519 等文件。如果没有就…...
PyTorch API 6 - 编译、fft、fx、函数转换、调试、符号追踪
文章目录 torch.compiler延伸阅读 torch.fft快速傅里叶变换辅助函数 torch.func什么是可组合的函数变换?为什么需要可组合的函数变换?延伸阅读 torch.futurestorch.fx概述编写转换函数图结构快速入门图操作直接操作计算图使用 replace_pattern() 进行子图…...
Unreal 从入门到精通之VR常用操作
文章目录 前言1.如何设置VRPawn视角的位置。2.如何播放视频3.如何播放VR全景视频。4.如何打开和关闭VR模式。前言 我们使用Unreal5 开发VR 项目的时候,会遇到很多常见问题。 比如: 1.如何设置VRPawn视角的位置。 2.如何播放视频。 3.如何播放VR全景视频。 4.如何打开和关闭V…...
Dify使用总结
最近完成了一个Dify的项目简单进行总结下搭建服务按照官方文档操作就行就不写了。 进入首页之后由以下组成: 探索、工作室、知识库、工具 探索: 可以展示自己创建的所有应用,一个应用就是一个APP,可以进行测试使用 工作室包含…...
事务连接池
一、事务概述 (一)事务的定义 事务是数据库提供的一种特性,用于确保数据操作的完整性和一致性。事务将多个数据操作组合成一个逻辑单元,这些操作要么全部成功,要么全部失败。 (二)事务的特性…...
如何用AWS Lambda构建无服务器解决方案:实战经验与场景解析
一、为什么开发者都在关注Serverless? 一、为什么开发者都在关注Serverless? 在云计算高速发展的今天,“无服务器架构”正成为技术新宠。根据Gartner预测,到2025年全球将有50%企业采用Serverless技术。而作为无服务器领域的领头…...
Android Compose 框架物理动画之捕捉动画深入剖析(29)
Android Compose 框架物理动画之捕捉动画深入剖析 一、引言 在 Android 应用开发中,动画是提升用户体验的关键元素之一。它能够让界面更加生动、交互更加自然。Android Compose 作为新一代的声明式 UI 框架,为开发者提供了强大且灵活的动画能力。其中&…...
Jmeter中的Json提取器如何使用?
在JMeter中使用JSON提取器可以方便地从JSON格式的响应数据中提取特定字段的值。以下是详细步骤和示例: 1. 添加JSON提取器 右击目标HTTP请求 -> 选择 添加 -> 后置处理器 -> JSON提取器。 2. 配置JSON提取器参数 变量名称(Names of created…...
STM32中断
STM32 GPIO外部中断简图 中断向量表 定义一块固定的内存,以4字节对齐,存放各个中断服务函数程序的首地址 中断向量表定义在启动文件,当发生中断,CPU会自动执行对应的中断服务函数 中断向量表以及中断函数 NVIC嵌套向量中断控制…...
navicat 如何导出数据库表 的这些信息 字段名 类型 描述
navicat 如何导出数据库表 的这些信息 字段名 类型 描述 数据库名字 springbootmt74k 表名字 address SELECT COLUMN_NAME AS 字段名,COLUMN_TYPE AS 类型,COLUMN_COMMENT AS 描述 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA springbootmt74k AND TABLE_NAME a…...
LangGraph(三)——添加记忆
目录 1. 创建MemorySaver检查指针2. 构建并编译Graph3. 与聊天机器人互动4. 问一个后续问题5. 检查State参考 1. 创建MemorySaver检查指针 创建MemorySaver检查指针: from langgraph.checkpoint.memory import MemorySavermemory MemorySaver()这是位于内存中的检…...
数仓-可累计,半累加,不可累加指标,是什么,举例说明及解决方案
目录 1. 可累计指标定义:举例:解决方案: 2. 半累加指标定义:举例:解决方案: 3. 不可累加指标定义:举例:解决方案: 4. 总结对比5. 实际场景中的注意事项 这是数据仓库设计…...
Java ClassLoader双亲委派机制
Java ClassLoader双亲委派机制 1 什么是双亲委派模型 “类加载体系”及ClassLoader双亲委派机制。java程序中的 .java文件编译完会生成 .class文件,而 .class文件就是通过被称为类加载器的ClassLoader加载的,而ClassLoder在加载过程中会使用“双亲委派…...
upload-labs靶场通关详解:第四关
一、分析源代码 可以看出这一关仍然是黑名单验证,但是它禁止了更多的后缀。像php3,php4这类后缀也被加入了黑名单,第三关的方法在这里显然就失效了。那么我们想一想,既然配置文件中存在将php3当作php来执行的功能,那么…...
Webug4.0通关笔记25- 第30关SSRF
目录 一、SSRF简介 1.SSRF原理 2.渗透方法 二、第30关SSRF渗透实战 1.打开靶场 2.渗透实战 (1)Windows靶场修复 (2)Docker靶场修复 (3)获取敏感文件信息 (4)内网端口与服务…...
【 Redis | 实战篇 缓存 】
目录 前言: 1.认识缓存 2.添加Redis缓存 2.1.根据id查询商铺缓存 2.2.优化根据id查询商铺缓存 3.缓存更新策略 3.1.三种策略 3.2.策略选择 3.3.主动更新的方案 3.4. Cache Aside的模式选择 3.5.最佳实践方案 4.缓存三大问题 4.1.缓存穿透 4.1.1.介绍 …...
数字果园管理系统的设计与实现(Tensorflow的害虫识别结合高德API的害虫定位与Websocket的在线聊天室)
文章目录 技术栈主要功能害虫识别与定位害虫识别的实现训练与测试评估代码模型转化为TFLite预测脚本PredictController预测控制器害虫识别过程展示 害虫定位实现害虫定位代码害虫定位过程展示 专家咨询功能在线咨询聊天室主要前端代码如下主要后端代码如下 技术栈 Spring Boot…...
信息检索(包含源码)
实验目的 掌握逻辑回归模型在二分类问题中的应用方法熟悉机器学习模型评估指标PR曲线(精确率-召回率曲线)和ROC曲线(受试者工作特征曲线)的绘制与分析学习使用Python的scikit-learn库进行数据预处理、模型训练与评估理解特征选择…...
【金仓数据库征文】金仓数据库KingbaseES: 技术优势与实践指南(包含安装)
目录 前言 引言 一 : 关于KingbaseES,他有那些优势呢? 核心特性 典型应用场景 政务信息化 金融核心系统: 能源通信行业: 企业级信息系统: 二: 下载安装KingbaseES 三:目录一览表: 四:常用SQL语句 创建表: 修改表结构…...
Java数据结构——二叉树
二叉树 树的概念二叉树满二叉树和完全二叉树二叉树的性质二叉树的遍历 题目练习前序遍历中序遍历后序遍历 前言 已经知道了数据结构中的线性结构,那有没有非线性结构呢? 当然有就像我们文件夹,一个文件夹中有有另一个文件夹,这就是…...
用go从零构建写一个RPC(仿gRPC,tRPC)--- 版本2
在版本1中,虽然系统能够满足基本需求,但随着连接数的增加和处理请求的复杂度上升,性能瓶颈逐渐显现。为了进一步提升系统的稳定性、并发处理能力以及资源的高效利用,版本2引入了三个重要功能:客户端连接池、服务器长连…...
drf 使用jwt
安装jwt pip install pyJwt 添加登录url path("jwt/login",views.JwtLoginView.as_view(),namejwt-login),path("jwt/order",views.JwtOrderView.as_view(),namejwt-order), 创建视图 from django.contrib.auth import authenticateimport jwt from jw…...
202536 | KafKa生产者分区写入策略+消费者分区分配策略
KafKa生产者分区写入策略 1. 轮询分区策略(Round-Robin Partitioning) 轮询分区策略 是 Kafka 默认的分配策略,当消息没有指定 key 时,Kafka 会采用轮询的方式将消息均匀地分配到各个分区。 工作原理: 每次生产者发…...
《自动驾驶封闭测试场地建设技术要求》 GB/T 43119-2023——解读
目录 一、标准框架与核心内容 二、重点技术要求 三、实施要点与建议 四、实施时间与参考依据 原文链接:国家标准|GB/T 43119-2023 (发布:2023-09-07;实施:2024-01-01) 一、标准框架与核心内容 适用范围…...