当前位置: 首页 > news >正文

【Linux学习笔记】进程替换和自定义shell

【Linux学习笔记】进程替换和自定义shell

🔥个人主页大白的编程日记

🔥专栏Linux学习笔记


文章目录

  • 【Linux学习笔记】进程替换和自定义shell
    • 前言
    • 一.进程程序替换
      • 1.1 替换原理
      • 1.2 替换函数
      • 1.2.1函数解释
      • 1.2.2命名理解
    • 二.自主Shell命令行解释器
      • 2.1 模块框架图
      • 2.2 目标
      • 2.3 实现原理
      • 2.4 全局变量
      • 2.5 环境变量函数
      • 2.6 初始化环境变量表函数
      • 2.7 输出命令行提示符模块
      • 2.8 提取命令输入模块
      • 2.9 填充命令行参数表模块
      • 2.10 检测并处理内建命令模块
      • 2.11 执行命令模块
      • 2.12 源码
    • 三 总结
    • 后言

前言

哈喽,各位小伙伴大家好!上期我们讲了进程地址空间 今天我们讲的是进程替换和自定义shell。话不多说,我们进入正题!向大厂冲锋!
在这里插入图片描述

一.进程程序替换

fork()之后,父子各自执行父进程代码的一部分如果子进程就想执行一个全新的程序呢?进程的程序替换来完成这个功能!程序替换是通过特定的接口,加载磁盘上的一个全新的程序(代码和数据),加载到调用进程的地址空间中!

1.1 替换原理

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


1.2 替换函数

其实有六种以exec开头的函数,统称exec函数:

 #include <unistd.h>int execl(const char *path, const char *arg, ...);int execlp(const char *file, const char *arg, ...);int execle(const char *path, const char *arg, ...,char *const envp[]);int execv(const char *path, char *const argv[]);int execvp(const char *file, char *const argv[]);int execve(const char *path, char *const argv[], char *const envp[]);

1.2.1函数解释

  1. 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
  2. 如果调用出错则返回-1
  3. 所以exec函数只有出错的返回值而没有成功的返回值。
    在这里插入图片描述

1.2.2命名理解

这些函数原型看起来很容易混,但只要掌握了规律就很好记。

  • I(list):表示参数采用列表
  • v(vector):参数用数组
  • p(path):有p自动搜索环境变量PATH
  • e(env):表示自己维护环境变量
函数名参数格式是否带路径是否使用当前环境变量
execl列表不是
execlp列表
execle列表不是不是,须自己组装环境变量
execv数组不是
execvp数组
execve数组不是不是,须自己组装环境变量

exec调用举例如下:

#include <unistd.h>int main()
{char *const argv[] = {"ps", "-ef", NULL};char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};execl("/bin/ps", "ps", "-ef", NULL);// 带p的,可以使用环境变量PATH,无需写全路径execlp("ps", "ps", "-ef", NULL);// 带e的,需要自己组装环境变量execle("ps", "ps", "-ef", NULL, envp);execv("/bin/ps", argv);// 带p的,可以使用环境变量PATH,无需写全路径execvp("ps", argv);// 带e的,需要自己组装环境变量execve("/bin/ps", argv, envp);exit(0);
}

事实上,只有execve是真正的系统调用,其它五个函数最终都调用execve,所以execve在man手册第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示。下图exec函数簇一个完整的例子:


在这里插入图片描述

二.自主Shell命令行解释器

2.1 模块框架图

2.2 目标

  • 要能处理普通命令
  • 要能处理内建命令
  • 要能帮助我们理解内建命令/本地变量/环境变量这些概念
  • 要能帮助我们理解shell的允许原理

2.3 实现原理

考虑下面这个与shell典型的互动:

[root@localhost epoll]# lsclient.cpp  readme.md  server.cpp  utility.h[root@localhost epoll]# psPID TTY          
TIME CMD3451 pts/0    
3514 pts/0    
00:00:00 bash00:00:00 ps

然后shell读取新的一行输入,建立一个新的进程,在这个进程中运行程序并等待这个进程结束。 所以要写一个shell,需要循环以下过程:

  1. 获取命令行
  2. 解析命令行
  3. 建立一个子进程(fork)
  4. 替换子进程(execvp)
  5. 父进程等待子进程退出(wait) 根据这些思路,和我们前面的学的技术,就可以自己来实现一个shell了。

2.4 全局变量

  • 我们的shell内部有两张表命令行参数表和环境变量表
  • 同时我们还要定义一张哈希表方便处理别名
  • 定义两个数组用来方便处理记录路径
  • lastcode记录上一次的进程退出码、
  • 宏定义大小方便开辟数组 以及命令行输出格式
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "// 下面是shell定义的全局数据// 1. 命令行参数表
#define MAXARGC 128
char* g_argv[MAXARGC];
int g_argc = 0;// 2. 环境变量表
#define MAX_ENVS 100
char* g_env[MAX_ENVS];
int g_envs = 0;// 3. 别名映射表
std::unordered_map<std::string, std::string> alias_list;// for test
char cwd[1024];
char cwdenv[1024];// last exit code
int lastcode = 0;

2.5 环境变量函数

环境标量函数直接调用getenv获取在返回即可
注意GetPWD需要ssnprintf格式化写入即可
DirName直接从后面查找分割符\ 然后返回之后的字符串即可

const char* GetUserName()
{const char* name = getenv("USER");return name == NULL ? "None" : name;
}const char* GetHostName()
{const char* hostname = getenv("HOSTNAME");return hostname == NULL ? "None" : hostname;
}const char* GetPwd()
{//const char *pwd = getenv("PWD");const char* pwd = getcwd(cwd, sizeof(cwd));if (pwd != NULL){snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);putenv(cwdenv);}return pwd == NULL ? "None" : pwd;
}const char* GetHome()
{const char* home = getenv("HOME");return home == NULL ? "" : home;
}
string DirName(const char* pwd)
{
#define SEP "/"string ret = pwd;int index = ret.rfind(SEP);if (ret == SEP){return SEP;}if (index == string::npos){return "BUG?";}string t = ret.substr(index+1);return t;
}

2.6 初始化环境变量表函数

直接把enriron指向的环境变量表拷贝到我们的环境变量表里面 为了区分我们的shell和系统的我们在末尾添加上haha区分
在让environ指向我们的环境变量表即可

void InitEnv()
{extern char** environ;memset(g_env, 0, sizeof(g_env));g_envs = 0;for (int i = 0; environ[i]; i++){g_env[i] = (char*)malloc(strlen(environ[i]) + 1);strcpy(g_env[i], environ[i]);g_envs++;}g_env[g_envs++] = "haha";g_env[g_envs] = NULL;for (int i = 0; g_env[i]; i++){putenv(g_env[i]);cout << g_env[i] << endl;}environ = g_env;
}

2.7 输出命令行提示符模块

先定义一个字符输出存储命令行提示符
然后snprintf格式化写入字符数组中 在输出即可

void PrintCommandPrompt()
{char cmd_prompt[COMMAND_SIZE];MakeCommandLine(cmd_prompt, sizeof(cmd_prompt));std::cout << cmd_prompt;
}
void MakeCommandLine(char cmd_prompt[], int size)
{snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), DirName(GetPwd()).c_str());
}
const char* GetUserName()
{const char* name = getenv("USER");return name == NULL ? "None" : name;
}const char* GetHostName()
{const char* hostname = getenv("HOSTNAME");return hostname == NULL ? "None" : hostname;
}const char* GetPwd()
{//const char *pwd = getenv("PWD");const char* pwd = getcwd(cwd, sizeof(cwd));if (pwd != NULL){snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);putenv(cwdenv);}return pwd == NULL ? "None" : pwd;
}const char* GetHome()
{const char* home = getenv("HOME");return home == NULL ? "" : home;
}
string DirName(const char* pwd)
{
#define SEP "/"string ret = pwd;int index = ret.rfind(SEP);if (ret == SEP){return SEP;}if (index == string::npos){return "BUG?";}string t = ret.substr(index+1);return t;
}

2.8 提取命令输入模块

这里先fgets获取标准输入到字符数组中
然后构造字符串删除erase\0 然后去map中判断是否为别名
如果是直接把value的值拷贝到数组中即可 然后return ture退出
不是别名则把用户回车的\n字符消除 同时如果用户只回车此时n==0
不做处理返回false 其他返回true;

bool GetCommandLine(char* commandline, int size)
{const char* ret=fgets(commandline, size, stdin);if (ret == NULL){return false;}std::string a = ret;a.erase(a.size() - 1, 1);if (alias_list.count(a)){strcpy(commandline, alias_list[a].c_str());return true;}int n = strlen(commandline);commandline[n - 1] = 0;if (n == 0){return false;}return true;
}

2.9 填充命令行参数表模块

先strtok获取指向第一个空格字符串 while的那个ret不为空时填充g_argv参数表 继续分割填充 直到找不到空格 说明字符串分割完毕
最后填充在g_argv表最后填充NULL即可
根据g_argv大小判断是否填充成功 成功返回ture 反之返回flase

bool CommandParse(char* commandline)
{
#define SEP " "g_argc = 0;char* ret = strtok(commandline, SEP);if (ret == NULL){return false;}while (ret){g_argv[g_argc++] = ret;ret = strtok(nullptr, SEP);}g_argv[g_argc] = ret;for (int i = 0; g_argv[i]; i++){cout << g_argv[i] << " ";}cout << endl;return g_argc > 0 ? true : false;
}

2.10 检测并处理内建命令模块

先根据g_argv表的第一个命令 判断分流检测处理

  • cd命令如果只有cd 那就直接获取家目录的环境变量字符串

  • chdir修改当前命令为家目录即可

  • 否则直接chdir修改当前目录的路径为g_argv[1]

  • echo命令判断分流

  • echo $?直接返回lastcode退出码 再设置wield0即可

  • echo $xxx 直接获取xxx的环境变量 再输出即可

  • echo xxx 直接打印xxx字符串g_argv[1]即可

  • export命令先判断是否填写了要导入的环境变量

  • 没有直接返回ture不做处理 否则直接putenv导入g_argv[1]环境变量即可
    alias命令 这里只处理不带命令选项的替换
    strtok分割=前后字符串然后 存储到map中即可

bool CheckAndExecBuiltion()
{std::string t = g_argv[0];if (t == "cd"){return Cd();}else if (t == "echo"){return Echo();}else if (t == "export"){return Export();}else if (t == "alias"){cout << "开始替换" << endl;return Alias();}else{return false;}
}
bool Cd()
{std::string t;if (g_argc == 1){t = GetHome();if (t == ""){return true;}chdir(t.c_str());}else{t =g_argv[1];chdir(t.c_str());}return true;
}
bool Echo()
{std::string t = g_argv[1];if (t=="$?"){std::cout << lastcode << std::endl;lastcode = 0;}else if (t[0] == '$'){char* ret = getenv(t.substr(1).c_str());if (ret){cout << ret << endl;}}else{cout << t << endl;}return true;
}
bool Export()
{if (g_argc != 2){return true;}putenv(g_argv[1]);return true;
}
bool Alias()
{
#define SEP "="char* t = g_argv[1];char* ret = strtok(t, SEP);if (ret == NULL){return false;}string a=std::string(ret),b = std::string(strtok(NULL, SEP));alias_list[a] = b;cout << alias_list[a] <<" "<<alias_list.count(a)<<endl;cout << a<< "->" << b << endl;return true;
}

2.11 执行命令模块

直接创建子进程 紫金陈通过execvp程序替换执行命令 执行完后exit退出 然后父进程waitpid等待子进程 同时把lastcode更新即可。

int Execute()
{pid_t id = fork();if (id == 0){execvp(g_argv[0], g_argv);exit(1);}int status = 0;pid_t rid=waitpid(id, &status, 0);if (rid > 0){lastcode = WEXITSTATUS(status);}return 0;
}
  • 效果演示:



在这里插入图片描述

2.12 源码

#include<iostream>
#include<cstdio>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string> 
#include<cstring>
#include<unordered_map>
using namespace std;
#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "// 下面是shell定义的全局数据// 1. 命令行参数表
#define MAXARGC 128
char* g_argv[MAXARGC];
int g_argc = 0;// 2. 环境变量表
#define MAX_ENVS 100
char* g_env[MAX_ENVS];
int g_envs = 0;// 3. 别名映射表
std::unordered_map<std::string, std::string> alias_list;// for test
char cwd[1024];
char cwdenv[1024];// last exit code
int lastcode = 0;
const char* GetUserName()
{const char* name = getenv("USER");return name == NULL ? "None" : name;
}const char* GetHostName()
{const char* hostname = getenv("HOSTNAME");return hostname == NULL ? "None" : hostname;
}const char* GetPwd()
{//const char *pwd = getenv("PWD");const char* pwd = getcwd(cwd, sizeof(cwd));if (pwd != NULL){snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);putenv(cwdenv);}return pwd == NULL ? "None" : pwd;
}const char* GetHome()
{const char* home = getenv("HOME");return home == NULL ? "" : home;
}
void InitEnv()
{extern char** environ;memset(g_env, 0, sizeof(g_env));g_envs = 0;for (int i = 0; environ[i]; i++){g_env[i] = (char*)malloc(strlen(environ[i]) + 1);strcpy(g_env[i], environ[i]);g_envs++;}g_env[g_envs++] = "haha";g_env[g_envs] = NULL;for (int i = 0; g_env[i]; i++){putenv(g_env[i]);cout << g_env[i] << endl;}environ = g_env;
}
bool GetCommandLine(char* commandline, int size)
{const char* ret=fgets(commandline, size, stdin);if (ret == NULL){return false;}std::string a = ret;a.erase(a.size() - 1, 1);if (alias_list.count(a)){strcpy(commandline, alias_list[a].c_str());return true;}int n = strlen(commandline);commandline[n - 1] = 0;if (n == 0){return false;}return true;
}
string DirName(const char* pwd)
{
#define SEP "/"string ret = pwd;int index = ret.rfind(SEP);if (ret == SEP){return SEP;}if (index == string::npos){return "BUG?";}string t = ret.substr(index+1);return t;
}
void MakeCommandLine(char cmd_prompt[], int size)
{snprintf(cmd_prompt, size, FORMAT, GetUserName(), "@hcss-ecs-8ddb:", DirName(GetPwd()).c_str());
}
void PrintCommandPrompt()
{char cmd_prompt[COMMAND_SIZE];MakeCommandLine(cmd_prompt, sizeof(cmd_prompt));std::cout << cmd_prompt;
}
bool CommandParse(char* commandline)
{
#define SEP " "g_argc = 0;char* ret = strtok(commandline, SEP);if (ret == NULL){return false;}while (ret){g_argv[g_argc++] = ret;ret = strtok(nullptr, SEP);}g_argv[g_argc] = ret;for (int i = 0; g_argv[i]; i++){cout << g_argv[i] << "#";}cout << endl;return g_argc > 0 ? true : false;
}
bool Cd()
{std::string t;if (g_argc == 1){t = GetHome();if (t == ""){return true;}chdir(t.c_str());}else{t =g_argv[1];chdir(t.c_str());}return true;
}
bool Echo()
{if (g_argc != 2){return true;}std::string t = g_argv[1];if (t=="$?"){std::cout << lastcode << std::endl;lastcode = 0;}else if (t[0] == '$'){char* ret = getenv(t.substr(1).c_str());if (ret){cout << ret << endl;}}else{cout << t << endl;}return true;
}
bool Export()
{if (g_argc != 2){return false;}putenv(g_argv[1]);
}
bool Alias()
{
#define SEP "="char* t = g_argv[1];char* ret = strtok(t, SEP);if (ret == NULL){return false;}string a=std::string(ret),b = std::string(strtok(NULL, SEP));alias_list[a] = b;cout << alias_list[a] <<" "<<alias_list.count(a)<<endl;cout << a<< "->" << b << endl;return true;
}
bool CheckAndExecBuiltion()
{std::string t = g_argv[0];if (t == "cd"){return Cd();}else if (t == "echo"){return Echo();}else if (t == "export"){return Export();}else if (t == "alias"){cout << "开始替换" << endl;return Alias();}else{return false;}
}
int Execute()
{pid_t id = fork();if (id == 0){execvp(g_argv[0], g_argv);exit(1);}int status = 0;pid_t rid=waitpid(id, &status, 0);if (rid > 0){lastcode = WEXITSTATUS(status);}return 0;
}
int main()
{//初始化环境变量表InitEnv();while (1){//打印命令行提示符PrintCommandPrompt();//获取命令行输入char commandline[COMMAND_SIZE];if (!GetCommandLine(commandline, sizeof(commandline))){continue;}//填充命令行参数表if (!CommandParse(commandline)){continue;}//处理内建命令if (CheckAndExecBuiltion()){continue;}//执行命令Execute();}return  0;
}

三 总结

在继续学习新知识前,我们来思考函数和进程之间的相似性 exec/exit就像call/return
一个C程序有很多函数组成。一个函数可以调用另外一个函数,同时传递给它一些参数。被调用的函数执行一定的操作,然后返回一个值。每个函数都有他的局部变量,不同的函数通过cal/return系统进行通信。

后言

这就是=进程替换和自定义shell。大家自己好好消化!今天就分享到这! 感谢各位的耐心垂阅!咱们下期见!拜拜~

相关文章:

【Linux学习笔记】进程替换和自定义shell

【Linux学习笔记】进程替换和自定义shell &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;Linux学习笔记 文章目录 【Linux学习笔记】进程替换和自定义shell前言一.进程程序替换1.1 替换原理1.2 替换函数1.2.1函数解释1.2.2命名理解 二.自主…...

HarmonyOS运动开发:如何监听用户运动步数数据

前言 在开发运动类应用时,准确地监听和记录用户的运动步数是一项关键功能。HarmonyOS 提供了强大的传感器框架,使得开发者能够轻松地获取设备的运动数据。本文将深入探讨如何在 HarmonyOS 应用中实现步数监听功能,同时分享一些开发过程中的经验和技巧,帮助你更好地理解和实…...

Vuex状态管理及其持久化使用指南

1. 安装 Vuex Vue 2 或 Vue 3 项目&#xff1a; npm install vuex4 # Vuex 4 兼容 Vue 2 和 Vue 3 2. 创建 Store 新建 store/index.js&#xff1a; import { createStore } from vuex; import createPersistedState from vuex-persistedstate; // 持久化插件// 定义子模…...

k8s学习笔记

1查看默认命名空间pod kubectl get nodes 查看所有名空间的pod kubectl get pods -A 查看所有节点node kubectl get nodes 2根据配置文件,给集群创建资源 kubectl apply -f xxxx.yaml 3查看集群部署了哪些应用? kubectl get pods -A 4生成、删除、获取所…...

HarmonyOS ArkUI安全控件开发指南:粘贴、保存与位置控件的实现与隐私保护实践

目录 安全控件1. 粘贴控件1.1 约束与限制1.2 开发步骤 2. 保存控件2.1 约束与限制2.2 开发步骤 3. 位置控件3.1 约束与限制3.2 开发步骤 安全控件 安全控件是系统提供的一组系统实现的ArkUI组件&#xff0c;其中保存控件在用户首次使用时&#xff0c;会弹出通知弹窗&#xff0…...

前端在平常的开发中高度还原ui图的思考规范

问题原因 我们明明按照ui测量 为什么还会出现这么多样式偏差 根据我多次的验收反馈总结如下 使用组件库 不可能所以样式覆盖复制粘贴以前的代码 样式产生偏差有些样式是继承所得 难免疏漏图片等背景问题 或者切图问题padding等结尾问题 解决方案 组件库穿透 主要样式复制粘…...

Python 数据智能实战 (2):LLM 工具箱搭建 - API/库实操与高效 Prompt 设计入门

写在前面 —— 工欲善其事,必先利其器:连接你的 Python 与 LLM 大脑,掌握“对话”的艺术 本篇博客,我们将聚焦于“工欲善其事,必先利其器”这一核心环节,带你完成以下关键任务: 选择你的 LLM “接入点”: 了解访问 LLM 的主要方式 (云 API vs. 本地模型),并为入门选…...

即时设计笔记

1. 编辑形状 1.1 连接两个锚点 如图&#xff0c;形状编辑模式下&#xff0c;想将下面图形变成闭合形状 使用【钢笔工具】选中其中一锚点&#xff0c;会自动伸出一条线&#xff0c;然后再点击另一个锚点即可 【注意】&#xff0c;形状闭合后&#xff0c;会发现无法填充颜色&am…...

“云大物智移”背景下,五年制高职计算机类高水平专业群建设方案

在信息技术革命的浪潮中&#xff0c;云计算、大数据、物联网、人工智能和移动互联网&#xff08;简称“云大物智移”&#xff09;已成为推动全球经济和社会数字化转型的关键力量。这些技术不仅重塑了产业格局&#xff0c;也给职业教育带来了前所未有的挑战与机遇。随着“云大物…...

docker desktop汉化

关闭Docker Desktop 在Docker安装目录找到app.asar文件并将其备份&#xff0c;防止出现意外。 Windows下默认为C:\Program Files\Docker\Docker\frontend\resourcesMacos下默认为/Applications/Docker.app/Contents/MacOS/Docker Desktop.app/Contents/ResourcesUbuntu/Debian…...

KUKA机器人快速启动设置

KUKA机器人在首次开机启动时&#xff0c;有时在示教器上需要进行投入运行等相关的设置。如以下相关的信息需要处理&#xff1a; 1、机器人系统开机后&#xff0c;选择T1运行模式&#xff1b;2、显示提示信息&#xff1a;“RDC 存储器和控制系统不一致什么被更换了”时&#xf…...

【Java学习】Java的CGLIB动态代理:通俗解释与使用指南

应友友的要求&#xff0c;本章是对于Java的CGLIB动态代理进行一个通俗的解释&#xff0c;希望能够帮助到奋斗的你&#xff0c;一起加油&#xff01;&#xff01;&#xff01; &#x1f31f; 通俗理解CGLIB动态代理 想象你有一个玩具机器人&#xff08;原始类&#xff09;&…...

Python3 基础语法

编码 默认情况下&#xff0c;Python3 源码文件以 UTF-8 编码&#xff0c;所有字符串都是 unicode 字符串。 当然你也可以为源码文件指定不同的编码&#xff1a; # -*- coding: cp-1252 -*- 上述定义允许在源文件中使用 Windows-1252 字符集中的字符编码&#xff0c;对应适合…...

基于Lucene的多场景检索系统开发指南

基于Lucene的多场景检索系统开发指南 官网 https://lucene.apache.org/ 一、项目构建配置 (pom.xml) <dependencies><!-- Lucene核心库 --><dependency><groupId>org.apache.lucene</groupId><artifactId>lucene-core</artifactId&g…...

SDC命令详解:使用get_nets命令进行查询

相关阅读 SDC命令详解https://blog.csdn.net/weixin_45791458/category_12931432.html?spm1001.2014.3001.5482 get_nets命令用于创建一个线网对象集合&#xff0c;关于设计对象和集合的更详细介绍&#xff0c;可以参考下面的博客。需要注意的是&#xff0c;在有些工具中还存…...

MATLAB长方体磁体3D磁力线生成

代码说明&#xff1a; 磁体参数设置&#xff1a;对磁体的尺寸、磁化强度以及真空磁导率进行定义。计算网格生成&#xff1a;构建一个 3D 网格&#xff0c;同时排除磁体内部的点。磁场计算&#xff1a;采用等效面磁荷法来计算每个网格点的磁场。可视化&#xff1a; 绘制磁体的框…...

东土科技NewPre系列智能控制器的创新之旅

物流行业&#xff0c;作为支撑现代供应链的核心环节&#xff0c;正在面临效率瓶颈、人工成本上升、设备协同难等多重挑战。如何实现从自动化向智能化的转型&#xff0c;成为行业亟须解决的关键问题。东土科技基于NewPre3102智能控制器打造的工业AI智能机器人控制系统&#xff0…...

2025年燃气从业人员考试题库及答案

一、单选题 83.城镇燃气输配管道严密性试验稳压的持续时间应为24h&#xff0c;每小时记录不应少于1次&#xff0c;当修正压力降小于_____Pa为合格。&#xff08;&#xff09; A.100 B.133 C.150 D.200 答案&#xff1a;B 84.室外钢管敷设描述错误的是_____。&#xff08;…...

小结: 接口类型和路由优先级

网络接口类型 1. Bridge-if&#xff08;桥接接口&#xff09; 作用&#xff1a;用于桥接网络&#xff0c;将多个接口或VLAN连接为一个广播域&#xff0c;实现二层数据转发。 常用指令&#xff1a; interface bridge-if <number> bridge <bridge-id> # 将接口加入…...

【XR空间传送】深入理解Unity中 XR Interaction Toolkit 的 MatchOrientation 用法与使用场景(空间传送、视角切换)

一、前言 在 Unity 进行 VR/AR 开发&#xff0c;尤其是在展示、环境体验、导览交互等场景中&#xff0c;“传送&#xff08;Teleport&#xff09;”是一项非常核心的操作。它不仅决定了玩家的位置&#xff0c;还直接影响玩家的站姿、视角与空间感知。 在 Unity 官方提供的 XR…...

PPT/WORD如何实现Shift键加鼠标滚轮实现左右滑动

方法&#xff1a;使用【AutoHotKey】软件 1. 什么是AutoHotKey&#xff1f; AutoHotKey (AHK)&#xff0c;是一款类似于按键精灵的软件&#xff0c;用于自动化控制键盘和鼠标&#xff0c;提高工作效率。 官网下载&#xff1a;https://www.autohotkey.com/ 2. 需求背景 在编…...

C++函数模板基础

1 函数模板 1.1 基础介绍 函数模板是一种特殊的函数定义,它允许你创建通用的函数,这些函数可以处理多种不同的数据类型,而不需要为每种数据类型都编写一个单独的函数。 在 C++ 里,函数模板的格式包含模板声明与函数定义两部分,其基本格式如下: template <typename…...

Java实现使用EasyExcel按模板导出文件

需求&#xff1a;导出excel文件要求头部标题内容过于复杂&#xff0c;在代码层面不好实现&#xff0c;直接提供模板&#xff0c;只需要将数据写入即可。 一、代码实现示例 实现数据写入模板代码如下&#xff1a; public void export(HttpServletResponse response) throws E…...

【AI】OrinNX上安装RIVA-2.19.0,实现文本转语音

1、注册NGC账户 1)登录官网注册, https://ngc.nvidia.com/signin 2)生成key 3)拷贝生成的key到文本中,下面会使用 2、安装NGC工具 目前最新版本为3.64.2 1)下载 wget --content-disposition https://api.ngc.nvidia.com/v2/resources/nvidia/ngc-apps/ngc_cli/vers…...

Ansible安装配置

一、前提 服务器操作系统均为centos7.9 主机ipmaster(Ansible管理端)172.25.192.2node1172.25.192.10node2172.25.192.3 更新/etc/hosts文件 二、安装 master节点&#xff1a; 1. 安装epel源 yum install -y epel-release 2. 安装Ansible yum install -y ansible A…...

认识Linux基本操作、命令

目录 一、了解一下用户目录、linux根目录 二、常见的linux命令 &#xff08;1&#xff09;目录切换 cd【与windows通用】 &#xff08;2&#xff09; 查看文件列 ls &#xff08;3&#xff09;打开文件权限-chmod ux *.sh &#xff08;4&#xff09;查看当前所在路径 pwd…...

Android学习总结之Bitmap篇

一、质量压缩&#xff08;面试高频&#xff1a;原理与适用场景&#xff09; 1. 核心原理&#xff08;面试官必问&#xff09; 质量压缩针对有损压缩格式&#xff08;如 JPEG&#xff09;&#xff0c;通过丢弃图像中人类视觉不敏感的高频信息&#xff08;如色彩过渡细节&#…...

MySQL--数据引擎详解

存储引擎 MySQL体系结构 连接层&#xff1a; 主要接收客户端的连接&#xff0c;然后完成一些链接的处理&#xff0c;以及认证授权的相关操作和安全方案&#xff0c;还要去检查是否超过最大连接数等等&#xff0c;比如在连接MySQL服务器时需要输入用户名&#xff0c;密码&#…...

高瓴资本张磊的顶级价值投资之道

高瓴资本张磊的顶级价值投资之道 一、核心理念&#xff1a;长期主义与动态价值创造 张磊的价值投资体系以“长期主义”为基石&#xff0c;结合中国产业升级特点&#xff0c;形成了独特的投资哲学&#xff1a; 时间维度重构 • 主张跨越经济周期&#xff0c;关注企业10年以上的价…...

JAVA使用Apache POI导出Word,支持向表格动态添加多行数据

JAVA使用Apache POI动态导出Word文档_java导出word文档-CSDN博客...

外部存储器接口:EMIF总线

作为嵌入式系统中的关键接口之一&#xff0c;EMIF&#xff08;External Memory Interface&#xff0c;外部存储器接口&#xff09; 是处理器与外部存储器或外设进行高速数据交换的核心总线。以下从工作原理、信号定义、配置方式到设计要点进行系统化解析&#xff1a; 一、EMIF的…...

四.割草机技术总结--4.基站发送给流动站的差分数据传输标准RTCM

四.割草机技术总结–4.基站发送给流动站的差分数据传输标准RTCM 文章目录 四.割草机技术总结--4.基站发送给流动站的差分数据传输标准RTCM4.0 RTCM差分标准4.1 **什么是RTCM?**4.1.1**>版本1**4.1.2**>版本2**4.1.3**>版本3**4.2**为什么RTK的固件升级很重要?**4.3参…...

openssl_error_string() 不要依赖错误信息作为逻辑判断

不应该将 OpenSSL 错误信息直接用于程序的逻辑控制&#xff0c;而是应该将其作为调试和记录的辅助手段。这是因为 OpenSSL 错误信息的格式和内容可能会因 OpenSSL 版本、操作系统环境等因素而发生变化&#xff0c;直接依赖这些信息可能会导致代码的不稳定和难以维护。 为什么不…...

AbortController 取消请求

‌AbortController‌是JavaScript中的一个全局类&#xff0c;主要用于中止各种异步操作&#xff0c;包括网络请求、事件监听器、可写流和数据库事务等。通过提供一个AbortSignal对象&#xff0c;AbortController允许开发者在需要时自由地终止这些操作&#xff0c;避免不必要的开…...

【论文阅读】PEEKABOO: Interactive Video Generation via Masked-Diffusion

PEEKABOO: Interactive Video Generation via Masked-Diffusion 原文摘要 研究背景与问题 现状&#xff1a;现代视频生成模型&#xff08;如 Sora&#xff09;已能生成高质量视频&#xff0c;但缺乏用户交互控制能力。 问题&#xff1a;交互控制是未来应用和创意表达的关键功能…...

Java 多线程基础:Thread 类详解

在 Java 中&#xff0c;多线程编程离不开 Thread 类。本文将介绍 Thread 的常用属性和方法&#xff0c;并结合实际使用场景进行讲解。 一、线程的创建与基本操作 Java 提供了两种主要方式来创建线程&#xff1a; 继承 Thread 类 class MyThread extends Thread {public void r…...

论文阅读:2024 arxiv FlipAttack: Jailbreak LLMs via Flipping

总目录 大模型安全相关研究&#xff1a;https://blog.csdn.net/WhiffeYF/article/details/142132328 https://www.doubao.com/chat/4001481281518594 FlipAttack: Jailbreak LLMs via Flipping https://arxiv.org/pdf/2410.02832 速览 这篇论文主要介绍了一种针对大语言模…...

《数据结构之美--二叉树oj题练习》

引言&#xff1a; 上次我们学习了二叉树这一数据结构&#xff0c;这次我们就针对二叉树来做一些oj题来巩固一下 1. 单值二叉树 题目&#xff1a; 思路分析&#xff1a; 代码实现&#xff1a; 题目传送门: 力扣–965.单值二叉树 2. 相同的树 题目&#xff1a; 思路分析&am…...

大模型——使用coze搭建基于DeepSeek大模型的智能体实现智能客服问答

大模型——使用coze搭建基于DeepSeek大模型的智能体实现智能客服问答 本章实验完全依托于coze在线平台,不需要本地部署任何应用。 实验介绍 1.coze介绍 扣子(coze)是新一代 AI 应用开发平台。无论你是否有编程基础,都可以在扣子上快速搭建基于大模型的各类 AI 应用,并…...

React Navigation 使用指南

React Navigation 使用指南 React Navigation 介绍 React Navigation 是 React Native 中推荐的路由管理方案。 1. 开箱即用&#xff0c;快速上手&#xff08;Easy to Use&#xff09; 提供 内置导航器&#xff08;Built-in Navigators&#xff09;&#xff0c;如栈导航&…...

双系统,bios默认设置启动ubuntu+ubuntu改启动grub设置

设置默认启动ubuntu 装windows后就默认启动windows了 按f6上移动 -------------底下不用看 设了没用 sudo update-grub...

Vue 中的过渡效果与响应式数据:transition、transitiongroup、reactive 和 ref 详解

在 Vue 开发过程中&#xff0c;为应用添加过渡效果和处理响应式数据是提升用户体验和实现动态交互的关键。 一、transition&#xff1a;元素的单元素过渡效果 transition是 Vue 提供的内置组件&#xff0c;专门用于为单个元素或组件添加过渡动画。它会在元素插入、更新或移除…...

Gitea windows服务注册,服务启动、停止、重启脚本

修改配置文件 查看COMPUTERNAME echo %COMPUTERNAME%进入配置文件D:\gitea\custom\conf\app.ini&#xff0c;将 Gitea 设置为以本地系统用户运行 如果结果是 USER-PC&#xff0c;那么 RUN_USER USER-PC$ RUN_USER COMPUTERNAME$SQLite3 PATH配置&#xff0c;更改为包含完整…...

SpringBoot、微服务与AI场景题深度解析

SpringBoot、微服务与AI场景题深度解析 在互联网大厂Java求职者的面试中&#xff0c;经常会被问到关于SpringBoot、微服务以及AI技术的应用场景和解决方案。本文通过一个故事场景来展示这些问题的实际解决方案。 第一轮提问 面试官&#xff1a;马架构&#xff0c;欢迎来到我…...

实验六 文件操作实验

一、实验目的 1、了解文件系统功能及实现原理。 2、掌握LINUX下文件操作的有关系统调用。 3、熟悉main函数带参数运行的有关操作过程。 4、通过模拟程序实现简单的一级文件系统或二级文件系统。 二、实验内容 1、编程显示文件自身。&#xff08;1分&#xff09; #includ…...

scratch代码——游戏开发 【弹簧与反弹】

添加角色——绘制 1&#xff0c;level 平台 2&#xff0c; ball 球 3&#xff0c;terk 坦克 开始编程——ball 函数 其余代码 开始编程——terk 代码 完结撒花 ——————————————————THE—END————————————————————...

C++入门(缺省参数/函数/引用)

C入门 一.前言二.缺省参数什么是缺省参数&#xff1f; 三.函数重载一.传参类型不同2. 传参个数不同3.传参顺序不同注意点 四.引用1.引⽤的概念和定义2.引⽤的特性3.引⽤的使⽤4. const引⽤5.指针和引⽤的关系 五.inline&#xff08;内联&#xff09;一、什么是inline函数&#…...

ACTF2025 - WEB Excellent-Site

#flask框架 #互联网 #利用CRLF进行头控制 #通过CRLF注入伪造 #利用sql注入进行回显控制 #Jinja2模板 #注意sql语句的相关转义可以控制查询结果 [[IMAP协议简介]] [[SMTP协议简介]] app.py import smtplib import imaplib import email import sqlite3 from urllib.pa…...

通过 Node.js 搭配 Nodemailer 实现邮箱验证码发送

目录 一、整体思路 二、环境准备 三、代码详细步骤 1. 引入模块并初始化 2. 配置 Nodemailer 邮件服务 3. 定义发送验证码接口 4. 定义校验验证码接口 5. 启动服务器 四、补充说明 一、整体思路 接收邮箱地址请求 → 用户在前端填写邮箱&#xff0c;发送到后端接口&…...

Java后端程序员学习前端之html

什么是html Hyper Text Markup Language(超文本标记语言) 超文本包括:文字、图片、音频、视频、动画等 组织&#xff1a;W3C World Wide Web Consortium(万维网联盟)。 成立子1994年&#xff0c;Web技术领域最权威和具影响力的国际中立性技术标准机构 http://www.w3.org! …...