〖 Linux 〗操作系統進程管理精講(2)

文章目錄

  • 1、環境變量
    • 基本概念
    • 常見環境變量
    • 查看環境變量方法
    • 測試 PATH
    • 測試 HOME
    • 和環境變量相關的命令
    • 環境變量的組織方式<p align="center">
    • main 函數的三個參數
    • 通過代碼獲得環境變量
    • 通過系統調用獲取環境變量
    • 環境變量通常是具有全局屬性的
  • 2、程序地址空間
    • 2.1 驗證程序地址空間的排布
    • 2.2 驗證堆和棧增長方向的問題
    • 2.3 如何理解 static 變量<p align="center">
    • 2.4 感知虛擬地址空間的存在
  • 3、進程地址空間
    • 分頁 & 虛擬地址空間
  • 4、Linux2.6 內核進程調度隊列 - (理解即可)<p align="center">
    • 一個 CPU 擁有一個 runqueue
    • 優先級
    • 活動隊列
    • 過期隊列
    • active 指針和 expired 指針

1、環境變量

??我們都清楚自己寫的一串代碼,經過編譯后生成可執行程序,我們用./即可運行,但是系統里有些命令也是64位的可執行程序:


在這里插入圖片描述

  • 既然都是程序,那就可以把你自己的寫的程序叫做指令,把系統的指令叫做命令程序 or 二進制文件。所以自己寫的程序和系統中的指令沒區別,均可以稱為指令、工具、可執行程序。

??但是系統里的命令(ls、pwd……)可以直接用,既然你自己寫的可執行程序myproc也是命令,那為什么不能像系統中的那樣直接使用呢?反而要加上./才能運行。

在這里插入圖片描述

  • 注意看這里的報錯:command not found,就是說執行一個可執行程序,前提是要先找到它,這也就說明了系統的命令能找到它,自己寫的程序卻找不到它。

??原因:linux系統中存在相關的環境變量,保留了程序的搜索路徑的! 所以出現上面的情況。下面就來具體講解環境變量。

基本概念

??環境變量 (environment variables) 一般是指在操作系統中用來指定操作系統運行環境的一些參數。

  • 如在編寫 C/C++ 代碼鏈接時,雖不知動態靜態庫位置,但能鏈接成功生成可執行程序,就是因為有相關環境變量幫助編譯器查找。

??環境變量通常具有某些特殊用途,且在系統當中通常具有全局特性


常見環境變量

輸入指令env能夠查看所有環境變量,常見的環境變量如下:


在這里插入圖片描述

不過常見的如下:

  • PATH:系統中搜索可執行程序(命令)的環境變量
  • HOME:指定用戶的家目錄 (即用戶登陸到 Linux 系統中時,默認的目錄)
  • SHELL:當前 Shell, 它的值通常是 /bin/bash。

如下查看系統中的PATH命令:


在這里插入圖片描述

查看環境變量方法

??通過echo命令來查看環境變量,指令格式為:

echo $NAME //NAME:你的環境變量名稱

??以查看具體的PATH環境變量為例:


在這里插入圖片描述

??注意這里的路徑分隔符是用:間隔的,當輸入ls指令時,系統會在這些路徑里面逐個尋找,找到后就執行特定路徑下的ls 。這也就解釋了自己寫的myproc程序不在此路徑中,所以不能直接使用的原因。


測試 PATH

??以mypro文件為例,自己寫的可執行程序myproc不能像系統的命令一樣直接使用,如果想要讓myproc像系統中的命令一樣使用,有如下兩種方法:


在這里插入圖片描述
??根據我們前面的分析得知,我們不能讓自己寫的可執行程序myproc像系統的命令一樣直接使用:


在這里插入圖片描述
??如果要讓自己寫的myproc像系統中的命令樣使用,有如下兩種方法:

  • 1、手動添加到系統路徑/usr/bin/里頭

    在這里插入圖片描述

??但是并不建議把你自己寫的可執行程序隨意添加到系統里頭(會污染),所以執行下面的命令刪除即可:

sudo rm /usr/bin/mypro

2、使用export命令把myproc當前所處的路徑也添加到PATH環境變量里,Linux命令行也是可以定義變量的,分為兩種:

??1.本地變量 (不加export定義的就是本地變量,可以通過set命令查看本地變量,也可以查看環境變量:)
??2.環境變量(全局屬性,我們使用export可以導出環境變量,使用env顯示環境變量:)


在這里插入圖片描述

??如果我們在變量前面加上export,這就是導出環境變量:
在這里插入圖片描述
下面演示把myproc的路徑導入PATH里頭,輸入下面的命令:

PATH=$PATH:/home/ruice/test

在這里插入圖片描述

該命令是把所有的PATH環境變量內容提取出來放到PATH里,并在后面追加mypro的當前路徑。添加后就可以像命令一樣直接使用myproc,
若想刪除該環境變量,執行unset命令。


在這里插入圖片描述


測試 HOME

任何一個用戶在運行系統登錄時都有自己的主工作目錄(家目錄),環境變量HOME保存的就是該用戶的主工作目錄。
普通用戶示例:


在這里插入圖片描述

root超級用戶示例:


在這里插入圖片描述


和環境變量相關的命令

  • 1、echo:顯示某個環境變量值
  • 2、export:設置一個新的環境變量
  • 3、env:顯示所有環境變量
  • 4、unset:清除環境變量
  • 5、set:顯示本地定義的 shell 變量和環境變量

環境變量的組織方式

在這里插入圖片描述

??每個程序都會收到一張環境表,環境表是一個字符指針數組,每個指針指向一個以’\0’結尾的環境字符串。

main 函數的三個參數

??main函數可以帶 3 個參數,其形式為:int main(int argc, char* argv[], char* envp[]) ,其中:

int main(int argc, char* argv[], char* envp[])
{return 0;
}

int argc : 指針數組中元素的個數,代表命令行參數的數量(包含可執行程序名)。
char* argv[]:指針數組
int argc:數組里的元素個數

通過以下代碼測試前兩個參數:

#include<stdio.h>
#include<unistd.h>int main(int argc, char* argv[])
{for (int i = 0; i < argc; i++){printf("argv[%d]: %s\n", i , argv[i]);}return 0;
}

??運行結果如下:


在這里插入圖片描述

??main函數的第二個參數一個字符指針數組,此時argv數組下標 0 存儲的是命令行的第一個位置(可執行程序),其余字符指針存儲的是命令行對應的選項,main函數的第一個參數argc存儲的是數組元素個數


在這里插入圖片描述

??總結:我們給main函數傳遞的argc,char* argv[ ]是命令行參數,傳遞的內容是命令行中輸入的程序名和選項,并且結尾以NULL結束!!!

問:main函數傳這些參數的意義是什么?

??假設我們現在要實現一個命令行計算器,如果輸出./myproc -a 10 20,那么就是加法10+20=30,如果輸出./myproc -s 10 20,那么就是減法10-20=-10……。代碼如下

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>int main(int argc, char* argv[])
{if (argc != 4) {printf("Usage: %s [-a|-s|-m|-d first_num second_num",argv[0]);return 0;}int x = atoi(argv[2]);int y = atoi(argv[3]);if (strcmp("-a", argv[1]) == 0){printf ("%d + %d = %d\n", x, y, x + y);}else if (strcmp("-s", argv[1]) == 0){printf ("%d - %d = %d\n", x, y, x - y);}else if (strcmp("-m", argv[1]) == 0){printf ("%d * %d = %d\n", x, y, x * y);}else if (strcmp("-d", argv[1]) == 0){printf ("%d / %d = %d\n", x, y, x / y);}else {printf("Usage: %s [-a|-s|-m|-d first_num second_num",argv[0]);}return 0;
}

此時我們就可以運行此程序并通過命令行參數來實現我們想要的計算方式:


在這里插入圖片描述

總結:

  • 同一個程序,通過傳遞不同的參數,讓同一個程序有不同的執行邏輯,執行結果。 Linux系統中,會根據不同的選項,讓不同的命令,可以有不同的表現,這就是指令中各個選項的由來和起作用的方式!!! 這也就是命令行參數的意義,同樣也就是main函數參數的意義。

下面來談下main函數的第三個參數

int main(int argc, char* argv[], char* envp[])
{return 0;
}

??char*envp就是環境變量,也是一個字符指針數組,argv指向命令行參數字符串,envp指向一個一個環境變量字符串,最后以NULL結尾。

在這里插入圖片描述

通過以下代碼測試第三個參數:

int main(int argc, char* argv[], char* env[])
{for (int i = 0; env[i]; i++){printf("env[%d]: %s\n", i, env[i]);}return 0;
}

在這里插入圖片描述
??總結:一個進程是會被傳入環境變量參數的。
補充:一個函數在聲明和定義時無參數,實際傳參時也可以傳參。


通過代碼獲得環境變量

??可以通過main函數的第三個參數獲得環境變量,也可以通過第三方變量environ獲取。

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>int main() 
{extern char** environ;for (int i = 0; environ[i]; i++) {printf("%d: %s\n", i, environ[i]);}return 0;
}

在這里插入圖片描述


通過系統調用獲取環境變量

??除了通過main函數的第三個參數和第三方變量environ外,還可通過系統調用getenv函數獲取環境變量,getenv能通過目標環境變量名查找,返回對應的字符指針,從而直接獲得環境變量內容。

#include <stdio.h>
#include <stdlib.h>
int main()
{char* val = getenv("PATH");printf("%s\n", val);return 0;
}

在這里插入圖片描述

問:我為什么要獲得環境變量

??例如,假設當前用戶USER為ruice,只允許自己使用,不允許rc訪問,可通過獲取環境變量實現訪問控制。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{char* id = getenv("USER");if (strcmp(id, "ruice") != 0){printf("權限拒絕!\n");return 0;}printf("成功執行!\n");return 0;
}

在這里插入圖片描述
綜上,環境變量一定在某些地方有特殊用途,上面粗略的展示了其中一個方面。


環境變量通常是具有全局屬性的

??回顧bash進程:

#include<stdio.h>
#include<unistd.h>
int main()
{while (1){printf("hello Linux!,pid: %d, ppid:%d\n", getpid(), getppid());sleep(1); }return 0;
}

在這里插入圖片描述

  • 子進程pid每次運行結果不斷變化(因進程每次運行都在重啟),但父進程不變(父進程就是bash,是系統創建的命令行解釋器)。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{while (1){printf("hello Linux!,pid: %d, ppid:%d, myenv=%s\n", getpid(), getppid(),getenv("key"));sleep(1); }return 0;
}

在這里插入圖片描述

  • 如果殺掉bash進程,輸入任何命令都無反應,命令行直接掛掉。正常使用命令行是因為命令本身由bash進程獲取,且命令行中啟動的進程,父進程全都是bash

下面來理解環境變量具有全局屬性:

看如下代碼:(在原有的pid和ppid基礎上添加了獲取環境變量)

在這里插入圖片描述

??通過代碼測試發現,進程剛開始不存在環境變量,若在bash進程中導出一個環境變量,子進程運行時就能獲取到該環境變量。

在這里插入圖片描述

??總結:

  • 環境變量會被子進程繼承,若在bash進程中創建export環境變量,該環境變量會從bash位置開始被所有子進程獲取,所以環境變量具有全局屬性
  • 本地變量在bash內部定義,不會被子進程繼承

??補充:


在這里插入圖片描述

??local_val是本地變量,Linux下大部分命令通過子進程方式執行,但還有部分命令由bash自己執行(調用對應函數完成特定功能),這類命令叫內建命令

2、程序地址空間

在學習 C 的過程中,常見如下程序地址空間布局圖:

在這里插入圖片描述

2.1 驗證程序地址空間的排布

  • 程序地址空間不是內存,通過以下代碼在linux操作系統中對該布局進行驗證:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int un_g_val;
int g_val = 100;
int main()
{printf("code addr         : %p\n", main); //代碼區printf("init global addr  : %p\n", &g_val);//已初始化全局數據區地址printf("uninit global addr: %p\n", &un_g_val);//未初始化全局數據區地址char* m1 = (char*)malloc(100);printf("heap addr         : %p\n", m1);//堆區printf("stack addr        : %p\n", &m1);//棧區for(int i = 0; environ[i]; i++){printf("env addr: %p\n",  environ[i]);}return 0;
}

在這里插入圖片描述

  • 運行結果顯示,從上到下地址逐漸增大,且棧區和堆區之間有一塊非常大的鏤空,證實了程序地址空間的布局符合常見布局圖。

2.2 驗證堆和棧增長方向的問題


通過代碼測試,結果表明堆區的確是向上增長。

	char* m1 = (char*)malloc(100);char* m2 = (char*)malloc(100);char* m3 = (char*)malloc(100);char* m4 = (char*)malloc(100);printf("heap addr      :%p\n", m1);// 堆區printf("heap addr      :%p\n", m2);// 堆區printf("heap addr      :%p\n", m3);// 堆區

在這里插入圖片描述


通過代碼測試,從結果可以看出棧區向上減少。

	char* m1 = (char*)malloc(100);char* m2 = (char*)malloc(100);char* m3 = (char*)malloc(100);char* m4 = (char*)malloc(100);printf("stack addr      :%p\n", &m1);//棧區printf("stack addr      :%p\n", &m2);//棧區printf("stack addr      :%p\n", &m3);//棧區printf("stack addr      :%p\n", &m4);//棧區

在這里插入圖片描述

總結:

  • 堆區向地址增大方向增長(箭頭向上)
  • 棧區向地址減少方向增長(箭頭向下)
  • 堆,棧相對而生
  • 在 C 函數中定義的變量,通常在棧上保存,先定義的變量地址比較高先定義先入棧,后定義后入棧)。

2.3 如何理解 static 變量

在這里插入圖片描述

正常定義的變量符合棧的地址分布規則,后定義的變量在地址較低處。而被static修飾的變量,盡管在函數內定義,但已不在棧上,而是變為全局變量,存儲在全局數據區,這就是其生命周期會隨程序一直存在的原因。


在這里插入圖片描述

在這里插入圖片描述

總結:函數內定義的變量被static修飾,本質是編譯器會把該變量編譯進全局數據區內。

2.4 感知虛擬地址空間的存在

通過父子進程對全局數據操作的代碼示例:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
int un_g_val;
int g_val = 100;
int main()
{pid_t id = fork();if (id == 0){// childwhile (1){printf("我是子進程:%d, ppid:%d, g_val:%d, &g_val:%p\n\n ",getpid(), getppid(), g_val, &g_val);sleep(1);}}else if (id > 0){// childwhile (1){printf("我是父進程:%d, ppid:%d, g_val:%d, &g_val:%p\n\n ",getpid(), getppid(), g_val, &g_val);sleep(1);}}return 0;
}

當父子進程都未修改全局數據時,共享該數據。
在這里插入圖片描述

當有一方嘗試寫入修改時:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
int un_g_val;
int g_val = 100;
int main()
{pid_t id = fork();if (id == 0){int flag = 0;// childwhile (1){printf("我是子進程:%d, ppid:%d, g_val:%d, &g_val:%p\n\n ",getpid(), getppid(), g_val, &g_val);sleep(1);flag++;if (flag == 3){g_val = 200;printf("我是子進程,全局數據我已經修改了,用戶注意參看!!!\n");}}}else if (id > 0){// childwhile (1){printf("我是父進程:%d, ppid:%d, g_val:%d, &g_val:%p\n\n ",getpid(), getppid(), g_val, &g_val);sleep(1);}}return 0;
}

會出現父子進程讀取同一個變量(地址相同),但讀取到的內容卻不一樣的情況。


在這里插入圖片描述

  • 這里父子進程讀取同一個變量(因為地址一樣),但是后續在沒有人修改的情況下,父子進程讀取到的內容卻不一樣!!!怎么會出現子進程和父進程對全局變量的地址是一樣的,但是輸出的內容確是不一樣的呢?

結論:我們在C、C++中使用的地址,絕對不是物理地址。 因為如果是物理地址,上述現象是不可能產生的!!!這種地址我們稱之為虛擬地址、線性地址、邏輯地址!!!

補充:為什么我的操作系統不讓我直接看到物理內存呢?

  • 因為不安全,內存就是一個硬件,不能阻攔你訪問!只能被動的進行讀取和寫入。不能直接訪問。

3、進程地址空間

之前所說的‘程序的地址空間’并不準確,準確的說法是進程地址空間

  • 每一個進程在啟動時,操作系統會為其創建一個地址空間,即進程地址空間每個進程都有屬于自己的進程地址空間。操作系統管理這些進程地址空間的方式是先描述,再組織,進程地址空間實際上是內核的一個數據結構(struct mm_struct )。

    在這里插入圖片描述

  • 進程具有獨立性,體現在相關的數據結構獨立,進程的代碼和數據獨立等方面。可以類比為一位圖書館管理員(相當于操作系統)同時管理三個獨立的書屋(相當于進程)。每個書屋都有自己的書籍(相當于進程的數據和代碼)和管理規則。管理員確保每個書屋獨立運營,每個書屋的書籍和其他資源都只供該書屋使用,不會與其他書屋混用。這樣可以避免書籍混亂,保證每個書屋的獨立性和秩序。

綜上:進程地址空間是OS通過軟件方式,為進程提供一個軟件視角,讓進程認為自己會獨占系統的所有資源(內存)


分頁 & 虛擬地址空間

在 Linux 內核中,每個進程都有task_struct結構體,該結構體中有個指針指向mm_struct(程序地址空間)。當磁盤上的程序被加載到物理內存時,需要在虛擬地址空間和物理內存之間建立映射關系,這種映射關系由頁表(映射表)完成(操作系統會為每個進程構建一個頁表結構)。


在這里插入圖片描述

問 1:什么叫做區域(代碼區……)

  • 區域類似于桌子上劃分的三八線,將空間一分為二,每一半又可進一步細分,比如這塊放書,這塊放筆盒,這塊放水杯等。mm_struct結構體也是按照類似方式進行區域劃分和限制的,如下代碼所示:
struct mm_struct
{long code_start;long code_end;long init_start;long init_end;long uninit_start;long uninit_end;//……
}

問 2:程序是如何變成進程的

  • 程序編譯后未加載時,程序內部有地址和區域,此時地址采用相對地址形式,區域在磁盤上已劃分好。程序加載就是按區域將其加載到內存的過程。

問 3:為什么先前修改一個進程時,地址是一樣的,但是父子進程訪問的內容卻是不一樣的

  • 父進程創建時,有自己的task_struct和地址空間mm_struct,地址空間通過頁表映射到物理內存。使用fork創建子進程時,子進程也有自己的task_struct、地址空間mm_struct和頁表 。如下:


    在這里插入圖片描述

  • 子進程剛創建時,和父進程的數據、代碼是共享的,即父子進程的代碼和數據通過頁表映射到物理內存的同一塊空間,所以此時打印g_val的值和內容是一樣的。

  • 當子進程需要修改數據g_val時,結果就變了,如下圖:


    在這里插入圖片描述

  • 無論父進程還是子進程,因為進程具有獨立性,如果子進程把變量g_val修改了,那么就會導致父進程識別此變量的時候出現問題,但是獨立性的要求是互不影響,所以此時操作系統會給你子進程重新開辟一塊空間,把先前g_val的值100 拷貝下來重新給此進程建立映射關系,所以子進程的頁表就不再指向父進程的數據100了,而是指向新的100,此時把100修改為200,無論怎么修改,變動的永遠都是右側,左側頁表間的關系不變, 所以最終讀到的結果為子進程是200,父進程是100.

  • 總結:當父子進程中有一方修改數據時,操作系統會為修改方重新開辟空間,拷貝原始數據到新空間,這種行為稱為寫時拷貝。通過頁表,利用寫時拷貝實現父子進程數據的分離,保證父子進程的獨立性

問 4:fork 有兩個返回值,pid_t id,同一個變量,怎么會有不同的值

  • 一般情況下,pid_t id是父進程棧空間中定義的變量,fork內部return會被執行兩次,return本質是通過寄存器將返回值寫入接收返回值的變量。 當執行id = fork()時,先返回的一方會發生寫時拷貝,所以同一個變量虛擬地址相同,但物理地址不同,從而有不同的內容值。

問 5:為什么要有虛擬地址空間

  • 保護內存:假設存在非法訪問野指針(*p = 110),若該野指針指向其他進程甚至操作系統,直接訪問物理內存會修改其他進程數據,存在安全風險。而有了虛擬地址空間,當遇到野指針時,頁表不會建立映射關系,無法訪問物理內存,相當于在內存訪問時增加了一層軟硬件審核機制,可攔截非法請求。
  • 解耦功能模塊:可以將 Linux 內存管理和進程管理通過地址空間進行功能模塊的解耦。
  • 統一視角與簡化實現:讓進程或程序以統一視角看待內存,便于以統一方式編譯和加載所有可執行程序,簡化進程本身的設計與實現。

4、Linux2.6 內核進程調度隊列 - (理解即可)


一個 CPU 擁有一個 runqueue

若存在多個 CPU,就需要考慮進程個數的負載均衡問題。


優先級

  • 普通優先級:100~139(與nice值取值范圍對應)
  • 實時優先級:0~99(不重點關注)

活動隊列

??時間片還沒有結束的所有進程都按照優先級放在該隊列。 nr_active:表示總共有多少個運行狀態的進程。
??queue[140]:一個元素就是一個進程隊列,相同優先級的進程按照 FIFO(先進先出)規則進行排隊調度,數組下標即優先級。

??從該結構中選擇一個最合適的進程的過程如下:

  • 1、從 0 下標開始遍歷queue[140]
  • 2、找到第一個非空隊列(該隊列優先級最高)
  • 3、拿到選中隊列的第一個進程開始運行,完成調度。
  • 4、遍歷queue[140]的時間復雜度是常數,但效率仍較低。

??為提高查找非空隊列的效率,使用bitmap[5],用 5*32 個比特位表示隊列是否為空,可大大提升查找效率。


過期隊列

  • 過期隊列和活動隊列結構一模一樣,放置的是時間片耗盡的進程。
  • 當活動隊列上的進程都被處理完畢后,會對過期隊列的進程重新計算時間片。

active 指針和 expired 指針

  • active指針永遠指向活動隊列。
  • expired指針永遠指向過期隊列。
  • 隨著進程運行,活動隊列上的進程會越來越少,過期隊列上的進程會越來越多。
  • 在合適的時候,交換active指針和expired指針的內容,就相當于獲得了一批新的活動進程。

總結

在系統中查找一個最合適調度的進程的時間復雜度是常數,不會隨著進程數量增多而增加時間成本,這種進程調度方式稱為進程調度 O (1)算法

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

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

相關文章

vite:npm 安裝 pdfjs-dist , PDF.js View 示例

pdfjs-dist 是 Mozilla 的 PDF.js 庫的預構建版本&#xff0c;能讓你在項目里展示 PDF 文件。下面為你介紹如何用 npm 安裝 pdfjs-dist 并應用 pdf.js 和 pdf.worker.js。 為了方便&#xff0c;我將使用 vite 搭建一個原生 js 項目。 1.創建項目 npm create vitelatest pdf-v…

精品,架構師總結,MySQL 5.7 查詢入門詳解

文章目錄 MySQL 5.7 查詢入門詳解一、數據庫與表基礎操作1.1 連接數據庫1.2 創建數據庫1.3 使用數據庫1.4 創建數據表1.5 表結構查看 二、SELECT基礎查詢2.1 全列查詢2.2 指定列查詢2.3 別名使用2.4 去重查詢2.5 表達式計算 三、WHERE條件查詢3.1 比較運算符3.2 邏輯運算符3.3 …

P48-56 應用游戲標簽

這一段課主要是把每種道具的游戲Tag進行了整理與應用 AuraAbilitySystemComponentBase.h // Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "AbilitySystemComponent.h"…

【AWS+Wordpress】將本地 WordPress 網站部署到AWS

前言 自學筆記&#xff0c;解決問題為主&#xff0c;親測有效&#xff0c;歡迎補充。 本地開發機&#xff1a;macOS&#xff08;Sequoia 15.0.1&#xff09; 服務器&#xff1a;AWS EC2&#xff08;Amazon Linux 2023&#xff09; 目標&#xff1a;從本地遷移 WordPress 到云…

從零開始:用PyTorch構建CIFAR-10圖像分類模型達到接近1的準確率

為了增強代碼可讀性&#xff0c;代碼均使用Chatgpt給每一行代碼都加入了注釋&#xff0c;方便大家在本文代碼的基礎上進行改進優化。 本文是搭建了一個稍微優化了一下的模型&#xff0c;訓練200個epoch&#xff0c;準確率達到了99.74%&#xff0c;簡單完成了一下CIFAR-10數據集…

C++復習類與對象基礎

類的成員函數為什么需要在類外定義 1.1 代碼組織與可讀性? ?類內定義?&#xff1a;適合 ?短小簡單的函數?&#xff08;如 getter/setter&#xff09;&#xff0c;能直觀體現類的接口設計。 ?類外定義?&#xff1a;當函數體較復雜時&#xff0c;將實現移到類外&#xf…

【計算機網絡】Cookie、Session、Token之間有什么區別?

大家在日常使用瀏覽器時可能會遇到&#xff1a;是否清理Cookie&#xff1f;這個問題。 那么什么是Cookie呢&#xff1f;與此相關的還有Session、Token這些。這兩個又是什么呢&#xff1f; 本文將對這三個進行講解區分&#xff0c;如果對小伙伴有幫助的話&#xff0c;也請點贊、…

Python 3.x 內置裝飾器 (4) - @dataclass

dataclass 是Python 3.7 引入的一個裝飾器&#xff0c;用來簡化創建數據類&#xff08;主要存儲數據的類&#xff09;的過程。它會自動為類生成一些常用的方法&#xff0c;比如&#xff1a; __init__: 對象的初始化 __repr__: 定義類的官方字符串表示。 __eq__: 定義兩個對象…

第一次被AI指點出文章的問題

二十年&#xff0c;幾乎從未收到過這種程度的反饋。大部分意見都是有效的批評。 1.前言 關于鏈家左暉的一篇評論稿 這篇文章有什么問題嗎&#xff1f;所有方面都可以談&#xff0c;包括敘述&#xff0c;用詞&#xff0c;重復&#xff0c;真實性&#xff0c;偏見&#xff0c;…

var、let、const三者之間的區別和使用

var、let、const 都是用于聲明變量的關鍵字&#xff0c;可以通過作用域、可變性、變量提升幾個方面進行區分&#xff1b; 1&#xff0c;作用域 1&#xff09;var的屬于函數作用域&#xff0c;只能在函數內使用。若在函數外部聲明變量&#xff0c;會變為全局變量&#xff1b; …

Consumer Group的作用是什么?Rebalance的觸發條件有哪些? (實現消費者負載均衡;消費者加入/離開、訂閱Topic變化等)

好的&#xff0c;我現在需要回答用戶關于Kafka Consumer Group和Rebalance的問題。首先&#xff0c;用戶問的是Consumer Group的作用以及Rebalance的觸發條件。根據之前的對話歷史&#xff0c;用戶已經了解了Kafka的基本組件、消息保留策略、順序性保證等&#xff0c;現在的問題…

出一期Source Insigned的使用教程

1. 創建新項目 打開 Source Insight&#xff0c;點擊菜單欄的 Project > New Project。在彈出的窗口中&#xff0c;輸入項目名稱&#xff08;建議與項目內容相關&#xff0c;便于識別&#xff09;。指定項目數據文件的存儲路徑&#xff08;即 Source Insight 配置文件保存的…

A. Row GCD(gcd的基本性質)

Problem - 1458A - Codeforces 思路&#xff1a; 首先得知道gcd的兩個基本性質&#xff1a; (1) gcd(a,b)gcd(a,|b-a|) (2) gcd(a,b,c)gcd(a,gcd(b,c)) 結合題目所給的a1bj&#xff0c;a2bj...... anbj 根據第一條性質得到&#xff1a; gcd(a1bj&#xff0c;a2bj)gcd(…

ES6入門---第三單元 模塊三:async、await

async function fn(){ //表示異步&#xff1a;這個函數里面有異步任務 let result await xxx //表示后面結果需要等待 } 讀取文件里數據實例&#xff1a; const fs require(fs);//簡單封裝 fs封裝成一個promise const readFile function (fileName){return…

如何在 C# 和 .NET 中打印 DataGrid

DataGrid 是 .NET 架構中一個功能極其豐富的組件&#xff0c;或許也是最復雜的組件之一。寫這篇文章是為了回答“我到底該如何打印 DataGrid 及其內容”這個問題。最初即興的建議是使用我的屏幕截圖文章來截取表單&#xff0c;但這當然無法解決打印 DataGrid 中虛擬顯示的無數行…

C語言 指針(5)

目錄 1.冒泡排序 2.二級指針 3.指針數組 4.指針數組模擬二級數組 1.冒泡排序 1.1 基本概念 冒泡排序&#xff08;Bubble Sort&#xff09; 是一種簡單的排序算法&#xff0c;它重復地遍歷要排序的數列&#xff0c;一次比較兩個元 素&#xff0c;如果它們的順序錯誤就把它…

15前端項目----用戶信息/導航守衛

登錄/注冊 持久存儲用戶信息問題 退出登錄導航守衛解決問題 持久存儲用戶信息 本地存儲&#xff1a;&#xff08;在actions中請求成功時&#xff09; 添加localStorage.setItem(token,result.data.token);獲取存儲&#xff1a;&#xff08;在user倉庫中&#xff0c;state中tok…

RSS 2025|斯坦福提出「統一視頻行動模型UVA」:實現機器人高精度動作推理

導讀 在機器人領域&#xff0c;讓機器人像人類一樣理解視覺信息并做出精準行動&#xff0c;一直是科研人員努力的方向。今天&#xff0c;我們要探討的統一視頻行動模型&#xff08;Unified Video Action Model&#xff0c;UVA&#xff09;&#xff0c;就像給機器人裝上了一個“…

基于論文的大模型應用:基于SmartETL的arXiv論文數據接入與預處理(四)

上一篇介紹了基于SmartETL框架實現arxiv采集處理的基本流程&#xff0c;通過少量的組件定制開發&#xff0c;配合yaml流程配置&#xff0c;實現了復雜的arxiv采集處理。 由于其業務流程復雜&#xff0c;在實際應用中還存在一些不足需要優化。 5. 基于Kafka的任務解耦設計 5.…

Fiori學習專題三十五:Device Adaptation

由于在類似于手機的小面板上顯示時&#xff0c;我們為了留出更多空間展示數據&#xff0c;可以將一些控件折疊。 1.修改HelloPanel.view.xml&#xff0c;加入expandable“{device>/system/phone}” expanded"{ !${device>/system/phone} <mvc:ViewcontrollerNam…