【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。大家自己好好消化!今天就分享到這! 感謝各位的耐心垂閱!咱們下期見!拜拜~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/80937.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/80937.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/80937.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【辦公類-89-03】20250429AI寫的研討記錄,清除格式,統一格式,名字替換。部分加粗,添加頁眉

背景需求: 檢查自即,需要AI一下院內的五次科研培訓記錄。 本次用了豆包 豆包寫的不錯,也是“水字數”的高手 把每次培訓內容貼到WORD里 把AI資料貼到WORD里,發現問題: 1、字體、段落什么都是不統一的,需要統一改成宋體小四,1.5倍行距 2、十個研討人也要改成真人。就找…

unity Orbbec Femto Bolt接入unity流程記錄 AzureKinectExamples 插件 使用記錄

奧比中光的深度相機Orbbec Femto Bolt是Microsoft的Azure Kinect DK的升級版&#xff0c;根據官網的文檔配置環境遇到了一些問題&#xff0c;記錄一下。 注意&#xff1a; 官網文檔鏈接&#xff1a;Femto Bolt文檔 1、首先連接相機到電腦USB3.0&#xff0c;接通電源&#xf…

聊天室系統:多任務版TCP服務端程序開發詳細代碼解釋

1. 需求 目前我們開發的TCP服務端程序只能服務于一個客戶端&#xff0c;如何開發一個多任務版的TCP服務端程序能夠服務于多個客戶端呢? 完成多任務&#xff0c;可以使用線程&#xff0c;比進程更加節省內存資源。 2. 具體實現步驟 編寫一個TCP服務端程序&#xff0c;循環等…

Python3:裝飾器、生成器與迭代器

Python3&#xff1a;裝飾器、生成器與迭代器 一、&#x1f3ad; 裝飾器&#xff1a;給函數穿上"魔法外衣"裝飾器基本概念為裝飾器添加參數傳遞功能帶參數的裝飾器functools.wraps&#xff1a;保留原函數的元信息實用裝飾器示例1. 計時器裝飾器2. 緩存裝飾器(Memoizat…

SQL命令一:SQL 基礎操作與建表約束

目錄 引言 一、SQL 基礎命令 &#xff08;一&#xff09;數據庫相關操作 &#xff08;二&#xff09;表格相關操作 &#xff08;三&#xff09;MySQL 常用數據類型 二、增刪改查&#xff08;CRUD&#xff09;操作 &#xff08;一&#xff09;增加數據 &#xff08;二&a…

Windows 桌面個性高效組件工具

軟件介紹 Widgets 這款基于 Vue3 構建的開源 Windows 桌面小部件工具超實用。 其多樣化組件庫涵蓋超 20 種&#xff0c;從倒計時、打工進度等實用工具&#xff0c;到抖音熱榜等實時資訊組件應有盡有&#xff0c;各組件獨立運行&#xff0c;滿足多場景需求。 高度自定義布局支持…

PCB入門指南:從電阻到常見電路的全解析

知識點1【電阻】 常見的是 色環電阻和貼片電阻 1、色環電阻 色環電阻&#xff0c;早期是碳膜電阻&#xff0c;精度不是很高&#xff0c;一般是4個色環&#xff0c;紅 橙 黃 綠 藍 紫 灰 白 黑&#xff0c;每個顏色代表一個阻值 后期是金屬膜電阻&#xff0c;5個色環&#x…

論文閱讀的三個步驟

論文閱讀的三個步驟 方法說明鏈接&#xff1a;https://www.academia.edu/4907403/How_to_Read_a_Paper 方法框架如下

Centos 7 ssh連接速度慢(耗時20秒+)

作系統&#xff1a;centos7.9 現象突然間通過 ssh、xshell等客戶端工具連接時&#xff0c;連接速度緩慢&#xff0c;需要耗時20秒左右才能連接上 排查思路&#xff1a; 1. 查看操作系統日志 從系統日志里看到較多的錯誤&#xff0c;這個可能是一個bug &#xff08;現象類似&a…

通過腳本自動檢查項目中全局的中文

現在越來越多的公司在做出海項目&#xff0c;出海項目首先要解決的就是語言國際化的問題&#xff0c;有很多如l18n、l10n的工具可以用&#xff0c;這些工具可以提供解決方案&#xff0c;但是不能約束開發者的開發行為。開發者仍然可能在代碼中存留沒有做過國際化處理的部分&…

軟件分析師-第三遍-章節導圖-13/14

系統設計&#xff0c;按步驟分&#xff1a;概要設計、詳細設計。另一種分類方式&#xff1a;按設計內容和設計方法分。設計內容&#xff1a;處理流程、輸入/輸出原型、人機交互。設計方法&#xff1a;結構化、面向對象、設計模式。 軟件實現&測試&#xff0c;分為實現、測…

通過全局交叉注意力機制和距離感知訓練從多模態數據中識別橋本氏甲狀腺炎|文獻速遞-深度學習醫療AI最新文獻

Title 題目 Hashimoto’s thyroiditis recognition from multi-modal data via globalcross-attention and distance-aware training 通過全局交叉注意力機制和距離感知訓練從多模態數據中識別橋本氏甲狀腺炎 01 文獻速遞介紹 橋本氏甲狀腺炎&#xff08;HT&#xff09;&a…

刀客doc:小紅書商業技術負責人蒼響離職

根據大廠日爆的爆料&#xff0c;小紅書商業化再度迎來高層人事變動&#xff0c;原商業平臺技術負責人蒼響&#xff08;薯名&#xff09;&#xff0c;職級L2&#xff0c;已于本月正式離職&#xff0c;其下屬團隊現由電商業務負責人接管。 根據刀客doc獲得的資料&#xff0c;蒼響…

Manus AI多語言手寫識別技術全解析:從模型架構到實戰部署

簡介 Manus AI作為當前多語言手寫識別領域的領軍技術&#xff0c;其核心創新在于融合三維卷積網絡、動態特征融合引擎和混合解碼系統&#xff0c;實現了對112種語言的98.7%識別準確率和8ms延遲的實時處理能力。本文將深入探討Manus AI的架構設計、特征提取方法、數據預處理策略…

華為云Astro大屏從iotda影子設備抽取數據做設備運行狀態的大屏實施步驟

目錄 背景與意義 1. 準備階段 2. IoTDA 開放影子查詢API 3. Astro輕應用創建連接器 4. Astro大屏設計界面 5. 數據綁定與交互邏輯 6. 發布與測試 小結&#xff08;流程復盤&#xff09; 背景與意義 隨著物聯網技術的快速發展&#xff0c;越來越多的設備接入云端&#x…

為什么要學習《易經》?

《易經》精華解讀&#xff1a;變易之道與人生智慧 《易經》&#xff08;《周易》&#xff09;是中國最古老的經典之一&#xff0c;被譽為“群經之首&#xff0c;大道之源”。它不僅是占卜之書&#xff0c;更是一部哲學經典&#xff0c;揭示了宇宙運行的規律和人生處世的智慧。…

逆傳播AIGEO營銷:破局生成式搜索時代,讓AI成為品牌代言人!

當GS(Generative Search生成式搜索)成為用戶的新“搜索入口”&#xff0c;你的品牌還在進行傳統軟文發布嗎? Gartner分析師預測"到2026年70%企業將把生成式AI整合進核心營銷系統"&#xff0c;傳統SEO的正被AI搜索徹底重構。用戶的搜索行為發生史詩級轉變&#xff0…

WPF(Windows Presentation Foundation)的內容模型

WPF&#xff08;Windows Presentation Foundation&#xff09;的內容模型&#xff08;Content Model&#xff09;是其核心架構之一&#xff0c;定義了UI元素如何組織和呈現內容。以下是WPF內容模型的系統化解析&#xff1a; 1. 內容模型基礎概念 WPF通過邏輯樹和可視化樹管理內…

52.[前端開發-JS實戰框架應用]Day03-AJAX-插件開發-備課項目實戰-Lodash

常用JavaScript庫 1 認識前端工具庫 前端工具類庫 2 Lodash vs underscore underscore庫 VS Lodash庫 Lodash庫 的安裝 手寫精簡版的Lodash ;(function(g) {function Lodash() {}// 添加類方法Lodash.VERSION 1.0.0Lodash.join function(arr, separater) {// todo ......…

前端Ui設計工具

PS 稿、藍湖、Sketch 和 Figma 前端 UI 設計工具的對比分析 PS 稿&#xff08;Adobe Photoshop&#xff09; 提供精準設計細節&#xff1a;PS 稿能讓前端更精準地理解頁面布局、元素尺寸、顏色等&#xff0c;通過精確測量和查看信息面板&#xff0c;把握設計元素的空間關系、…